From c7e3a91899b44466627efac6c4493f8436501184 Mon Sep 17 00:00:00 2001 From: Taranukha Leonid Date: Wed, 3 Jun 2026 23:02:23 +0300 Subject: [PATCH 1/4] add Parallel nodule and Property tests --- AVLSet_Solution.slnx | 1 + README.md | 38 +- src/AVLSet.Library/AVLSet.Library.fsproj | 3 +- src/AVLSet.Library/Library.fs | 334 +++++++----------- src/AVLSet.Library/LibraryParallel.fs | 119 +++++++ .../AVLSet.PropertyTests.fsproj | 25 ++ tests/AVLSet.PropertyTests/Tests.fs | 252 +++++++++++++ .../AVLSet.UnitTests/AVLSet.UnitTests.fsproj | 3 +- tests/AVLSet.UnitTests/Tests.fs | 202 +---------- 9 files changed, 548 insertions(+), 429 deletions(-) create mode 100644 src/AVLSet.Library/LibraryParallel.fs create mode 100644 tests/AVLSet.PropertyTests/AVLSet.PropertyTests.fsproj create mode 100644 tests/AVLSet.PropertyTests/Tests.fs diff --git a/AVLSet_Solution.slnx b/AVLSet_Solution.slnx index 8fdfd91..513dd38 100644 --- a/AVLSet_Solution.slnx +++ b/AVLSet_Solution.slnx @@ -6,6 +6,7 @@ + diff --git a/README.md b/README.md index 9413bc9..f2d2897 100644 --- a/README.md +++ b/README.md @@ -86,29 +86,41 @@ The `AVLSet` module provides a comprehensive interface: |:---|:---|:---| | **add** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Adds an element. | | **delete** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Removes an element. | -| **contains** | `'a -> AVLTree<'a> -> bool` | Checks membership. | +| **contains** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Copies an set. | +| **copy** | `'a -> AVLTree<'a> -> bool` | Checks membership. | | **union** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard union ($A \cup B$). | | **intersection** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard intersection ($A \cap B$). | | **difference** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard difference ($A \setminus B$). | | **symmDifference** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard symmetrical difference ($A \vartriangle B$). | -| **parallel(Union/Intersection/Difference/SymmDiff)**| `ParallelOptions -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Multi-threaded set-theoretic operations. | -| **(union/intersection/difference/symmDiff)Traversal**| `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Set-theoretic operations via tree traversal. | +| **Traversal.{union/intersection/difference/symmDiff}**| `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Set-theoretic operations via tree traversal. | + +The `ParallelAVLSet` module provides an interface for parallel set-theoretic operations: +| **union** | `option -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Parallel union ($A \cup B$). | +| **intersection** | `option -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Parallel intersection ($A \cap B$). | +| **difference** | `option -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Parallel difference ($A \setminus B$). | +| **symmDifference** | `option -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Parallel symmetrical difference ($A \vartriangle B$). | +| **{union/intersection/difference/symmDiff}Async** | `option -> AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Creates an asynchronous computation for set-theoretic operations. | --- ## Project Structure ```text /src -└── AVLSet.Library - ├── AVLSet.Library.fsproj -   └── Library.fs + └── AVLSet.Library + ├── AssemblyInfo.fs + ├── AVLSet.Library.fsproj + ├── Library.fs +   └── LibraryParallel.fs /tests -└── AVLSet.UnitTests - ├── AVLSet.UnitTests.fsproj -   └── Tests.fs + └── AVLSet.UnitTests + │ ├── AVLSet.UnitTests.fsproj +   │ └── Tests.fs + └── AVLSet.PropertyTests + ├── AVLSet.PropertyTests.fsproj +   └── Tests.fs /benchmarks -└── AVLSet.Benchmarks - ├── AVLSet.Benchmarks.fsproj -   ├── Benchmarks.fs - └── Program.fs + └── AVLSet.Benchmarks + ├── AVLSet.Benchmarks.fsproj +   ├── Benchmarks.fs + └── Program.fs ``` diff --git a/src/AVLSet.Library/AVLSet.Library.fsproj b/src/AVLSet.Library/AVLSet.Library.fsproj index 1ae3ffe..a0b9ab0 100644 --- a/src/AVLSet.Library/AVLSet.Library.fsproj +++ b/src/AVLSet.Library/AVLSet.Library.fsproj @@ -1,4 +1,4 @@ - + net10.0 @@ -7,6 +7,7 @@ + diff --git a/src/AVLSet.Library/Library.fs b/src/AVLSet.Library/Library.fs index d4e36fa..80acd89 100644 --- a/src/AVLSet.Library/Library.fs +++ b/src/AVLSet.Library/Library.fs @@ -1,103 +1,92 @@ namespace AVLSet.Library -open System.Threading.Tasks - -type AVLTree<'Value> = +type AVLSet<'Value> = | Empty - | Node of int * 'Value * AVLTree<'Value> * AVLTree<'Value> + | Node of int * 'Value * AVLSet<'Value> * AVLSet<'Value> -module Node = +module Tree = let height n = match n with | Empty -> -1 | Node(h, _, _, _) -> h - let value n = - match n with - | Empty -> failwith "Empty node has no value" - | Node(_, v, _, _) -> v - - let leftChild n = - match n with - | Empty -> failwith "Empty node has no left child" - | Node(_, _, ln, _) -> ln - - let rightChild n = - match n with - | Empty -> failwith "Empty node has no right child" - | Node(_, _, _, rn) -> rn - let maxMinNodesByHeights n1 n2 = - if height n1 >= height n2 then n1, n2 else n2, n1 + match n1, n2 with + | Empty, _ -> n2, n1 + | _, Empty -> n1, n2 + | Node(h1, _, _, _), Node(h2, _, _, _) -> if h1 >= h2 then n1, n2 else n2, n1 -module Tree = let LLrotate n = - let ln = Node.leftChild n - let lln = Node.leftChild ln - let rln = Node.rightChild ln - let rn = Node.rightChild n - let rlnNew = Node(max (Node.height rln) (Node.height rn) + 1, Node.value n, rln, rn) - Node(max (Node.height lln) (Node.height rlnNew) + 1, Node.value ln, lln, rlnNew) + match n with + | Node(_, vn, Node(_, vln, lln, rln), rn) -> + let rlnNew = Node(max (height rln) (height rn) + 1, vn, rln, rn) + Node(max (height lln) (height rlnNew) + 1, vln, lln, rlnNew) + | _ -> invalidArg "n" "Node cannot be rotated" let RRrotate n = - let ln = Node.leftChild n - let rn = Node.rightChild n - let rrn = Node.rightChild rn - let lrn = Node.leftChild rn - let lrnNew = Node(max (Node.height ln) (Node.height lrn) + 1, Node.value n, ln, lrn) - Node(max (Node.height lrnNew) (Node.height rrn) + 1, Node.value rn, lrnNew, rrn) + match n with + | Node(_, vn, ln, Node(_, vrn, lrn, rrn)) -> + let lrnNew = Node(max (height ln) (height lrn) + 1, vn, ln, lrn) + Node(max (height lrnNew) (height rrn) + 1, vrn, lrnNew, rrn) + | _ -> invalidArg "n" "Node cannot be rotated" let LRrotate n = - let lnNew = RRrotate(Node.leftChild n) - let rn = Node.rightChild n - LLrotate(Node(max (Node.height lnNew) (Node.height rn) + 1, Node.value n, lnNew, rn)) + match n with + | Node(hn, vn, ln, rn) -> + let lnNew = RRrotate ln + LLrotate(Node(max (height lnNew) (height rn) + 1, vn, lnNew, rn)) + | _ -> invalidArg "n" "Node cannot be rotated" let RLrotate n = - let rnNew = LLrotate(Node.rightChild n) - let ln = Node.leftChild n - RRrotate(Node(max (Node.height ln) (Node.height rnNew) + 1, Node.value n, ln, rnNew)) + match n with + | Node(hn, vn, ln, rn) -> + let rnNew = LLrotate rn + RRrotate(Node(max (height ln) (height rnNew) + 1, vn, ln, rnNew)) + | _ -> invalidArg "n" "Node cannot be rotated" let balance ln rn v = - let lnHeight = Node.height ln - let rnHeight = Node.height rn - - match lnHeight - rnHeight with - | 2 -> - let llnHeight = Node.height (Node.leftChild ln) - let rlnHeight = Node.height (Node.rightChild ln) - - if llnHeight >= rlnHeight then - LLrotate(Node(0, v, ln, rn)) - else - LRrotate(Node(0, v, ln, rn)) - | -2 -> - let lrnHeight = Node.height (Node.leftChild rn) - let rrnHeight = Node.height (Node.rightChild rn) - - if lrnHeight <= rrnHeight then - RRrotate(Node(0, v, ln, rn)) - else - RLrotate(Node(0, v, ln, rn)) - | _ -> Node(max lnHeight rnHeight + 1, v, ln, rn) + let lnHeight = height ln + let rnHeight = height rn + let diff = lnHeight - rnHeight + + if diff >= 2 then + match ln with + | Empty -> invalidArg "rn" "left child is Empty but diff is lesser than 2" + | Node(_, _, lln, rln) -> + if height lln >= height rln then + LLrotate(Node(0, v, ln, rn)) + else + LRrotate(Node(0, v, ln, rn)) + elif diff <= -2 then + match rn with + | Empty -> invalidArg "rn" "right child is Empty but diff is greater than -2" + | Node(_, _, lrn, rrn) -> + if height lrn <= height rrn then + RRrotate(Node(0, v, ln, rn)) + else + RLrotate(Node(0, v, ln, rn)) + else + Node(max lnHeight rnHeight + 1, v, ln, rn) let rec minNode n = match n with - | Empty -> failwith "minNode: cannot find minimum of an empty node" - | Node(_, v, Empty, rn) -> v, rn + | Empty -> None + | Node(_, v, Empty, rn) -> Some(v, rn) | Node(_, v, ln, rn) -> - let value, lnNew = minNode ln - value, balance lnNew rn v + match minNode ln with + | None -> None + | Some(value, lnNew) -> Some(value, balance lnNew rn v) let rec insert value n = match n with | Empty -> Node(0, value, Empty, Empty) | Node(h, v, ln, rn) -> - match value with - | value when value = v -> n - | value when value < v -> + if value = v then + n + elif value < v then let lnNew = insert value ln balance lnNew rn v - | _ -> + else let rnNew = insert value rn balance ln rnNew v @@ -105,18 +94,18 @@ module Tree = match n with | Empty -> Empty | Node(h, v, ln, rn) -> - match value with - | value when value = v -> + if value = v then match ln, rn with | Empty, _ -> rn | _, Empty -> ln | _, _ -> - let newValue, rnNew = minNode rn - balance ln rnNew newValue - | value when value < v -> + match minNode rn with + | None -> failwith "impossible error: rn is not Empty" + | Some(newValue, rnNew) -> balance ln rnNew newValue + elif value < v then let lnNew = remove value ln balance lnNew rn v - | _ -> + else let rnNew = remove value rn balance ln rnNew v @@ -125,12 +114,11 @@ module Tree = match n with | Empty -> false | Node(h, v, ln, rn) -> - match value with - | value when value = v -> true - | value when value < v -> contains value ln - | _ -> contains value rn + if value = v then true + elif value < v then contains value ln + else contains value rn - let rec traverse (func: 'A -> AVLTree<'B> -> AVLTree<'B>) nArg n = + let rec traverse (func: 'A -> AVLSet<'B> -> AVLSet<'B>) nArg n = match n with | Empty -> nArg | Node(_, v, ln, rn) -> @@ -144,20 +132,21 @@ module Tree = | Node(h, v, ln, rn) -> Node(h, v, copy ln, copy rn) let rec join left key right = - let leftHeight = Node.height left - let rightHeight = Node.height right + let leftHeight = height left + let rightHeight = height right + let diff = leftHeight - rightHeight - match leftHeight - rightHeight with - | diff when abs diff <= 1 -> Node(max leftHeight rightHeight + 1, key, left, right) - | diff when diff >= 2 -> + if abs diff <= 1 then + Node(max leftHeight rightHeight + 1, key, left, right) + elif diff >= 2 then match left with - | Empty -> failwith "Unreacheable message 1" + | Empty -> invalidArg "right" "left child is Empty but diff is lesser than 2" | Node(h, v, ln, rn) -> let rnNew = join rn key right balance ln rnNew v - | _ -> + else match right with - | Empty -> failwith "Unreacheable message 2" + | Empty -> invalidArg "left" "right child is Empty but diff is greater than -2" | Node(h, v, ln, rn) -> let lnNew = join left key ln balance lnNew rn v @@ -167,19 +156,20 @@ module Tree = | Empty, _ -> right | _, Empty -> left | _, _ -> - let key, newRight = minNode right - join left key newRight + match minNode right with + | None -> failwith "impossible error. right is not Empty" + | Some(key, newRight) -> join left key newRight let rec split key n = match n with | Empty -> Empty, Empty, false | Node(_, v, ln, rn) -> - match key with - | key when key = v -> ln, rn, true - | key when key < v -> + if key = v then + ln, rn, true + elif key < v then let lesser, greater, wasFound = split key ln lesser, join greater v rn, wasFound - | _ -> + else let lesser, greater, wasFound = split key rn join ln v lesser, greater, wasFound @@ -195,7 +185,7 @@ module AVLSet = let copy set = Tree.copy set let rec union set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 match maxSet, minSet with | Empty, _ -> minSet @@ -207,7 +197,7 @@ module AVLSet = Tree.join leftUnion v rightUnion let rec intersection set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 match maxSet, minSet with | Empty, _ -> Empty @@ -237,7 +227,7 @@ module AVLSet = Tree.join leftDiff v rightDiff let rec symmDifference set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 match maxSet, minSet with | Empty, _ -> minSet @@ -252,119 +242,37 @@ module AVLSet = else Tree.join leftSymm v rightSymm - let unionTraversal set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - let unSet = Tree.copy maxSet - Tree.traverse Tree.insert unSet minSet - - let intersectionTraversal set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - - Tree.traverse - (fun value set -> - if Tree.contains value maxSet then - Tree.insert value set - else - set) - Empty - minSet - - let differenceTraversal minuendSet subtrahendSet = - let diffSet = Tree.copy minuendSet - Tree.traverse Tree.remove diffSet subtrahendSet - - let symmDifferenceTraversal set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - let symmSet = Tree.copy maxSet - - Tree.traverse - (fun value set -> - if Tree.contains value maxSet then - Tree.remove value set - else - Tree.insert value set) - symmSet - minSet - - let rec parallelUnion (opts: ParallelOptions) set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - - match maxSet, minSet with - | Empty, _ -> minSet - | _, Empty -> maxSet - | Node(_, v, ln, rn), _ -> - let lesser, greater, _ = Tree.split v minSet - let mutable leftUnion = Empty - let mutable rightUnion = Empty - - Parallel.Invoke( - opts, - (fun () -> leftUnion <- parallelUnion opts ln lesser), - (fun () -> rightUnion <- parallelUnion opts rn greater) - ) - - Tree.join leftUnion v rightUnion - - let rec parallelIntersection (opts: ParallelOptions) set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - - match maxSet, minSet with - | Empty, _ -> Empty - | _, Empty -> Empty - | Node(_, v, ln, rn), _ -> - let lesser, greater, wasFound = Tree.split v minSet - let mutable leftInter = Empty - let mutable rightInter = Empty - - Parallel.Invoke( - opts, - (fun () -> leftInter <- parallelIntersection opts ln lesser), - (fun () -> rightInter <- parallelIntersection opts rn greater) - ) - - if wasFound then - Tree.join leftInter v rightInter - else - Tree.merge leftInter rightInter - - let rec parallelDifference (opts: ParallelOptions) minuendSet subtrahendSet = - match minuendSet, subtrahendSet with - | Empty, _ -> Empty - | _, Empty -> minuendSet - | Node(_, v, ln, rn), _ -> - let lesser, greater, wasFound = Tree.split v subtrahendSet - let mutable leftDiff = Empty - let mutable rightDiff = Empty - - Parallel.Invoke( - opts, - (fun () -> leftDiff <- parallelDifference opts ln lesser), - (fun () -> rightDiff <- parallelDifference opts rn greater) - ) - - if wasFound then - Tree.merge leftDiff rightDiff - else - Tree.join leftDiff v rightDiff - - let rec parallelSymmDifference (opts: ParallelOptions) set1 set2 = - let maxSet, minSet = Node.maxMinNodesByHeights set1 set2 - - match maxSet, minSet with - | Empty, _ -> minSet - | _, Empty -> maxSet - | Node(_, v, ln, rn), _ -> - let lesser, greater, wasFound = Tree.split v minSet - let mutable leftSymm = Empty - let mutable rightSymm = Empty - - Parallel.Invoke( - opts, - (fun () -> leftSymm <- parallelSymmDifference opts ln lesser), - (fun () -> rightSymm <- parallelSymmDifference opts rn greater) - ) - - if wasFound then - Tree.merge leftSymm rightSymm - else - Tree.join leftSymm v rightSymm + module Traversal = + let union set1 set2 = + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + let unSet = Tree.copy maxSet + Tree.traverse Tree.insert unSet minSet + + let intersection set1 set2 = + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + + Tree.traverse + (fun value set -> + if Tree.contains value maxSet then + Tree.insert value set + else + set) + Empty + minSet + + let difference minuendSet subtrahendSet = + let diffSet = Tree.copy minuendSet + Tree.traverse Tree.remove diffSet subtrahendSet + + let symmDifference set1 set2 = + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + let symmSet = Tree.copy maxSet + + Tree.traverse + (fun value set -> + if Tree.contains value maxSet then + Tree.remove value set + else + Tree.insert value set) + symmSet + minSet diff --git a/src/AVLSet.Library/LibraryParallel.fs b/src/AVLSet.Library/LibraryParallel.fs new file mode 100644 index 0000000..c05bae8 --- /dev/null +++ b/src/AVLSet.Library/LibraryParallel.fs @@ -0,0 +1,119 @@ +namespace AVLSet.Library.Parallel + +open AVLSet.Library + +/// +/// Parallel union of two AVL sets. +/// +/// +/// Optional thread limit: +/// - None: Auto-detect (uses all available CPU cores via System.Environment.ProcessorCount). +/// - Some(x): Hard limit to x threads (useful for benchmarking and resource control). +/// + +module ParallelAVLSet = + let rec unionAsync threads set1 set2 = + async { + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + + match maxSet, minSet with + | Empty, _ -> return minSet + | _, Empty -> return maxSet + | Node(_, v, ln, rn), _ -> + let lesser, greater, _ = Tree.split v minSet + + let limit = defaultArg threads System.Environment.ProcessorCount + + let left = unionAsync threads ln lesser + let right = unionAsync threads rn greater + + let! results = Async.Parallel([| left; right |], limit) + let leftUnion, rightUnion = results[0], results[1] + + return Tree.join leftUnion v rightUnion + } + + let rec intersectionAsync threads set1 set2 = + async { + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + + match maxSet, minSet with + | Empty, _ -> return Empty + | _, Empty -> return Empty + | Node(_, v, ln, rn), _ -> + let lesser, greater, wasFound = Tree.split v minSet + + let limit = defaultArg threads System.Environment.ProcessorCount + + let left = intersectionAsync threads ln lesser + let right = intersectionAsync threads rn greater + + let! results = Async.Parallel([| left; right |], limit) + let leftInter, rightInter = results[0], results[1] + + return + if wasFound then + Tree.join leftInter v rightInter + else + Tree.merge leftInter rightInter + } + + let rec differenceAsync threads minuendSet subtrahendSet = + async { + match minuendSet, subtrahendSet with + | Empty, _ -> return Empty + | _, Empty -> return minuendSet + | Node(_, v, ln, rn), _ -> + let lesser, greater, wasFound = Tree.split v subtrahendSet + + let limit = defaultArg threads System.Environment.ProcessorCount + + let left = differenceAsync threads ln lesser + let right = differenceAsync threads rn greater + + let! results = Async.Parallel([| left; right |], limit) + let leftDiff, rightDiff = results[0], results[1] + + return + if wasFound then + Tree.merge leftDiff rightDiff + else + Tree.join leftDiff v rightDiff + } + + let rec symmDifferenceAsync threads set1 set2 = + async { + let maxSet, minSet = Tree.maxMinNodesByHeights set1 set2 + + match maxSet, minSet with + | Empty, _ -> return minSet + | _, Empty -> return maxSet + | Node(_, v, ln, rn), _ -> + let lesser, greater, wasFound = Tree.split v minSet + + let limit = defaultArg threads System.Environment.ProcessorCount + + let left = symmDifferenceAsync threads ln lesser + let right = symmDifferenceAsync threads rn greater + + let! results = Async.Parallel([| left; right |], limit) + let leftSymm, rightSymm = results[0], results[1] + + return + if wasFound then + Tree.merge leftSymm rightSymm + else + Tree.join leftSymm v rightSymm + } + + let union threads t1 t2 = + unionAsync threads t1 t2 |> Async.RunSynchronously + + let intersection threads t1 t2 = + intersectionAsync threads t1 t2 |> Async.RunSynchronously + + let difference threads t1 t2 = + differenceAsync threads t1 t2 |> Async.RunSynchronously + + let symmDifference threads t1 t2 = + symmDifferenceAsync threads t1 t2 |> Async.RunSynchronously \ No newline at end of file diff --git a/tests/AVLSet.PropertyTests/AVLSet.PropertyTests.fsproj b/tests/AVLSet.PropertyTests/AVLSet.PropertyTests.fsproj new file mode 100644 index 0000000..c1b1597 --- /dev/null +++ b/tests/AVLSet.PropertyTests/AVLSet.PropertyTests.fsproj @@ -0,0 +1,25 @@ + + + + net10.0 + false + + + + + + + + + + + + + + + + + + + + diff --git a/tests/AVLSet.PropertyTests/Tests.fs b/tests/AVLSet.PropertyTests/Tests.fs new file mode 100644 index 0000000..3f0e818 --- /dev/null +++ b/tests/AVLSet.PropertyTests/Tests.fs @@ -0,0 +1,252 @@ +namespace AVLSet.PropertyTests + +open FsUnit.Xunit +open FsCheck.Xunit +open AVLSet.Library +open AVLSet.Library.Parallel + +module SetPropertyTests = + let rec isSetValid n mn mx = + match n with + | Empty -> true + | Node(h, v, ln, rn) -> + let isInBounds = + mn |> Option.forall (fun mn -> v > mn) && mx |> Option.forall (fun mx -> v < mx) + + let lnHeight = Node.height ln + let rnHeight = Node.height rn + + isInBounds + && h = (max lnHeight rnHeight + 1) + && abs (lnHeight - rnHeight) <= 1 + && isSetValid ln mn (Some v) + && isSetValid rn (Some v) mx + + let rec advancedContains (condition: 'A -> bool -> bool) setOfValues targetSet = + match setOfValues with + | Empty -> true + | Node(_, v, ln, rn) -> + let lesser, greater, wasFound = Tree.split v targetSet + + condition v wasFound + && advancedContains condition ln lesser + && advancedContains condition rn greater + + [] + let ``Adding elemements to set`` (elements: int list) = + let set = elements |> List.fold (fun t x -> AVLSet.add x t) AVLSet.empty + + let rec setContainsList list set = + match list with + | [ ] -> true + | head :: tail -> AVLSet.contains head set && setContainsList tail set + + isSetValid set None None |> should be True + setContainsList elements set |> should be True + + [] + let ``Set cloning`` (elements: int list) = + let set = elements |> List.fold (fun t x -> AVLSet.add x t) AVLSet.empty + + AVLSet.copy set |> should equal set + + [] + let ``Deleting elements from set`` (elements: int list) = + let set = elements |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let emptySet = elements |> List.fold (fun t e -> AVLSet.delete e t) set + let empty: AVLSet = AVLSet.empty + + emptySet |> should equal empty + + [] + let ``Standard set union`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let unionSet = AVLSet.union setA setB + + isSetValid unionSet None None |> should be True + + (advancedContains (fun v x -> x) setA unionSet + && advancedContains (fun v x -> x) setB unionSet) + |> should be True + + let unionSetSwapped = AVLSet.union setB setA + + (advancedContains (fun v x -> x) unionSet unionSetSwapped + && advancedContains (fun v x -> x) unionSetSwapped unionSet) + |> should be True + + + [] + let ``Standard set intersection`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let intersectionSet = AVLSet.intersection setA setB + + isSetValid intersectionSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet + |> should be True + + let intersectionSetSwapped = AVLSet.intersection setB setA + + (advancedContains (fun v x -> x) intersectionSet intersectionSetSwapped + && advancedContains (fun v x -> x) intersectionSetSwapped intersectionSet) + |> should be True + + [] + let ``Standard set difference`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let differenceSet = AVLSet.difference setA setB + + isSetValid differenceSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet + |> should be True + + [] + let ``Standard symmetric difference`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let symmDiffSet = AVLSet.symmDifference setA setB + + isSetValid symmDiffSet None None |> should be True + + (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA symmDiffSet + && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) + |> should be True + + let symmDiffSetSwapped = AVLSet.symmDifference setB setA + + (advancedContains (fun v x -> x) symmDiffSet symmDiffSetSwapped + && advancedContains (fun v x -> x) symmDiffSetSwapped symmDiffSet) + |> should be True + + [] + let ``Union via tree traversal`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let unionSet = AVLSet.Traversal.union setA setB + + isSetValid unionSet None None |> should be True + + (advancedContains (fun v x -> x) setA unionSet + && advancedContains (fun v x -> x) setB unionSet) + |> should be True + + let unionSetSwapped = AVLSet.union setB setA + + (advancedContains (fun v x -> x) unionSet unionSetSwapped + && advancedContains (fun v x -> x) unionSetSwapped unionSet) + |> should be True + + [] + let ``Intersection via tree traversal`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let intersectionSet = AVLSet.Traversal.intersection setA setB + + isSetValid intersectionSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet + |> should be True + + let intersectionSetSwapped = AVLSet.intersection setB setA + + (advancedContains (fun v x -> x) intersectionSet intersectionSetSwapped + && advancedContains (fun v x -> x) intersectionSetSwapped intersectionSet) + |> should be True + + [] + let ``Difference via tree traversal`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let differenceSet = AVLSet.Traversal.difference setA setB + + isSetValid differenceSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet + |> should be True + + [] + let ``Symmetric difference via tree traversal`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let symmDiffSet = AVLSet.Traversal.symmDifference setA setB + + isSetValid symmDiffSet None None |> should be True + + (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA symmDiffSet + && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) + |> should be True + + let symmDiffSetSwapped = AVLSet.symmDifference setB setA + + (advancedContains (fun v x -> x) symmDiffSet symmDiffSetSwapped + && advancedContains (fun v x -> x) symmDiffSetSwapped symmDiffSet) + |> should be True + + [] + let ``Parallel set union with threads`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let unionSet = ParallelAVLSet.union None setA setB + + isSetValid unionSet None None |> should be True + + (advancedContains (fun v x -> x) setA unionSet + && advancedContains (fun v x -> x) setB unionSet) + |> should be True + + let unionSetSwapped = AVLSet.union setB setA + + (advancedContains (fun v x -> x) unionSet unionSetSwapped + && advancedContains (fun v x -> x) unionSetSwapped unionSet) + |> should be True + + [] + let ``Parallel set intersection with threads`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let intersectionSet = ParallelAVLSet.intersection None setA setB + + isSetValid intersectionSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet + |> should be True + + let intersectionSetSwapped = AVLSet.intersection setB setA + + (advancedContains (fun v x -> x) intersectionSet intersectionSetSwapped + && advancedContains (fun v x -> x) intersectionSetSwapped intersectionSet) + |> should be True + + [] + let ``Parallel set difference with threads`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let differenceSet = ParallelAVLSet.difference None setA setB + + isSetValid differenceSet None None |> should be True + + advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet + |> should be True + + [] + let ``Parallel set symmetric difference with threads`` (elementsA: int list, elementsB: int list) = + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let symmDiffSet = ParallelAVLSet.symmDifference None setA setB + + isSetValid symmDiffSet None None |> should be True + + (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA symmDiffSet + && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) + |> should be True + + let symmDiffSetSwapped = AVLSet.symmDifference setB setA + + (advancedContains (fun v x -> x) symmDiffSet symmDiffSetSwapped + && advancedContains (fun v x -> x) symmDiffSetSwapped symmDiffSet) + |> should be True diff --git a/tests/AVLSet.UnitTests/AVLSet.UnitTests.fsproj b/tests/AVLSet.UnitTests/AVLSet.UnitTests.fsproj index 5fead54..5d5524b 100644 --- a/tests/AVLSet.UnitTests/AVLSet.UnitTests.fsproj +++ b/tests/AVLSet.UnitTests/AVLSet.UnitTests.fsproj @@ -11,8 +11,7 @@ - - + diff --git a/tests/AVLSet.UnitTests/Tests.fs b/tests/AVLSet.UnitTests/Tests.fs index 54982a7..6870734 100644 --- a/tests/AVLSet.UnitTests/Tests.fs +++ b/tests/AVLSet.UnitTests/Tests.fs @@ -1,11 +1,10 @@ namespace AVLSet.UnitTests -open System.Threading.Tasks open Xunit open FsUnit.Xunit open AVLSet.Library -module SetTests = +module SetUnitTests = let rec isSetValid n mn mx = match n with | Empty -> true @@ -111,7 +110,7 @@ module SetTests = let ``Single-node deletion`` () = let resultSet = Node(0, 15, Empty, Empty) |> AVLSet.delete 15 - let correctSet: AVLTree = Empty + let correctSet: AVLSet = Empty resultSet |> should equal correctSet @@ -217,200 +216,3 @@ module SetTests = ) resultSet |> should equal correctSet - - [] - let ``Search for missing element`` () = - let set = - Node( - 3, - 15, - Node(1, 10, Node(0, 6, Empty, Empty), Node(0, 12, Empty, Empty)), - Node( - 2, - 25, - Node(0, 20, Empty, Empty), - Node(1, 30, Node(0, 27, Empty, Empty), Node(0, 33, Empty, Empty)) - ) - ) - - AVLSet.contains 100 set |> should be False - - [] - let ``Search for negative value`` () = - let set = - Node( - 3, - 15, - Node(2, 5, Node(1, 0, Node(0, -3, Empty, Empty), Node(0, 2, Empty, Empty)), Node(0, 10, Empty, Empty)), - Node(1, 20, Node(0, 16, Empty, Empty), Node(0, 24, Empty, Empty)) - ) - - AVLSet.contains -3 set |> should be True - - [] - let ``Tree structure cloning`` () = - let set = - Node( - 3, - 15, - Node(2, 5, Node(1, 0, Node(0, -3, Empty, Empty), Node(0, 2, Empty, Empty)), Node(0, 10, Empty, Empty)), - Node(1, 20, Node(0, 16, Empty, Empty), Node(0, 24, Empty, Empty)) - ) - - AVLSet.copy set |> should equal set - - let rnd = System.Random(123561) - - let dataA = Array.init 10000 (fun _ -> rnd.Next()) - let dataB = Array.init 2000 (fun _ -> rnd.Next()) - - let setA = dataA |> Array.fold (fun s v -> AVLSet.add v s) AVLSet.empty - let setB = dataB |> Array.fold (fun s v -> AVLSet.add v s) AVLSet.empty - - [] - let ``100k operations stress test`` () = - let data = Array.init 100000 (fun _ -> rnd.Next()) - - let set = data |> Array.fold (fun s v -> AVLSet.add v s) AVLSet.empty - - isSetValid set None None |> should be True - - [] - let ``Standard set union`` () = - let unionSet = AVLSet.union setA setB - - isSetValid unionSet None None |> should be True - - (advancedContains (fun v x -> x) setA unionSet - && advancedContains (fun v x -> x) setB unionSet) - |> should be True - - AVLSet.union setB setA |> should equal unionSet - - [] - let ``Standard set intersection`` () = - let intersectionSet = AVLSet.intersection setA setB - - isSetValid intersectionSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet - |> should be True - - AVLSet.intersection setB setA |> should equal intersectionSet - - [] - let ``Standard set difference`` () = - let differenceSet = AVLSet.difference setA setB - - isSetValid differenceSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet - |> should be True - - [] - let ``Standard symmetric difference`` () = - let symmDiffSet = AVLSet.symmDifference setA setB - - isSetValid symmDiffSet None None |> should be True - - (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA symmDiffSet - && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) - |> should be True - - AVLSet.symmDifference setB setA |> should equal symmDiffSet - - [] - let ``Union via tree traversal`` () = - let unionSet = AVLSet.unionTraversal setA setB - - isSetValid unionSet None None |> should be True - - (advancedContains (fun v x -> x) setA unionSet - && advancedContains (fun v x -> x) setB unionSet) - |> should be True - - AVLSet.unionTraversal setB setA |> should equal unionSet - - [] - let ``Intersection via tree traversal`` () = - let intersectionSet = AVLSet.intersectionTraversal setA setB - - isSetValid intersectionSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet - |> should be True - - AVLSet.intersectionTraversal setB setA |> should equal intersectionSet - - [] - let ``Difference via tree traversal`` () = - let differenceSet = AVLSet.differenceTraversal setA setB - - isSetValid differenceSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet - |> should be True - - [] - let ``Symmetric difference via tree traversal`` () = - let symmDiffSet = AVLSet.symmDifferenceTraversal setA setB - - isSetValid symmDiffSet None None |> should be True - - (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x = true) setA symmDiffSet - && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) - |> should be True - - AVLSet.symmDifferenceTraversal setB setA |> should equal symmDiffSet - - [] - let ``Parallel set union with threads`` () = - let opts = ParallelOptions() - - let unionSet = AVLSet.parallelUnion opts setA setB - - isSetValid unionSet None None |> should be True - - (advancedContains (fun v x -> x) setA unionSet - && advancedContains (fun v x -> x) setB unionSet) - |> should be True - - AVLSet.parallelUnion opts setB setA |> should equal unionSet - - [] - let ``Parallel set intersection with threads`` () = - let opts = ParallelOptions() - - let intersectionSet = AVLSet.parallelIntersection opts setA setB - - isSetValid intersectionSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then x else not x) setA intersectionSet - |> should be True - - AVLSet.parallelIntersection opts setB setA |> should equal intersectionSet - - [] - let ``Parallel set difference with threads`` () = - let opts = ParallelOptions() - - let differenceSet = AVLSet.parallelDifference opts setA setB - - isSetValid differenceSet None None |> should be True - - advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA differenceSet - |> should be True - - [] - let ``Parallel set symmetrical difference with threads`` () = - let opts = ParallelOptions() - - let symmDiffSet = AVLSet.parallelSymmDifference opts setA setB - - isSetValid symmDiffSet None None |> should be True - - (advancedContains (fun v x -> if AVLSet.contains v setB then not x else x) setA symmDiffSet - && advancedContains (fun v x -> if AVLSet.contains v setA then not x else x) setB symmDiffSet) - |> should be True - - AVLSet.parallelSymmDifference opts setB setA |> should equal symmDiffSet From f6deae254c63613ba50dcf8a2e05081d5e6f8a4a Mon Sep 17 00:00:00 2001 From: Taranukha Leonid Date: Wed, 3 Jun 2026 23:10:59 +0300 Subject: [PATCH 2/4] improve benchmarks --- benchmarks/AVLSet.Benchmarks/Benchmarks.fs | 203 ++++++++++++++------- benchmarks/AVLSet.Benchmarks/Program.fs | 11 +- tests/AVLSet.PropertyTests/Tests.fs | 4 +- tests/AVLSet.UnitTests/Tests.fs | 4 +- 4 files changed, 147 insertions(+), 75 deletions(-) diff --git a/benchmarks/AVLSet.Benchmarks/Benchmarks.fs b/benchmarks/AVLSet.Benchmarks/Benchmarks.fs index 42af1e0..61d7c0f 100644 --- a/benchmarks/AVLSet.Benchmarks/Benchmarks.fs +++ b/benchmarks/AVLSet.Benchmarks/Benchmarks.fs @@ -1,132 +1,199 @@ namespace AVLSet.Benchmarks -open System.Threading.Tasks -open BenchmarkDotNet.Diagnosers open BenchmarkDotNet.Attributes open BenchmarkDotNet.Configs open AVLSet.Library +open AVLSet.Library.Parallel [] [] [] [] -[] -[] -type SetBenchmarks() = +type SingleOpsBenchmark() = let rnd = System.Random(1234561) - [] + [] [] val mutable public A: int - [] [] - val mutable public B: int + val mutable public rndInt: int - [] [] - val mutable public DataTypeA: string + val mutable public setA: AVLSet + + [] + member self.Setup() = + self.rndInt <- rnd.Next(self.A + 1, self.A + 1000) + + let dataA = Array.init self.A (fun _ -> rnd.Next()) + + self.setA <- + dataA + |> Array.fold + (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + + [] + [] + member self.AddingOneElement() = AVLSet.add self.rndInt self.setA - [] + [] + [] + member self.DeletingOneElement() = AVLSet.delete self.rndInt self.setA + + +[] +[] +[] +[] +type SequentialSetsBenchmark() = + let rnd = System.Random(1234561) + + [] [] - val mutable public Threads: int + val mutable public A: int + [] [] - val mutable public rndInt: int + val mutable public B: int [] - val mutable public setA: AVLTree + val mutable public setA: AVLSet [] - val mutable public setB: AVLTree + val mutable public setB: AVLSet [] member self.Setup() = - self.rndInt <- rnd.Next(self.A + 1, self.A + 1000) - - let dataA = - match self.DataTypeA with - | "Random" -> Array.init self.A (fun _ -> rnd.Next()) - | _ -> [| 1 .. self.A |] + let dataA = Array.init self.A (fun _ -> rnd.Next()) let dataB = Array.init self.B (fun _ -> rnd.Next()) - self.setA <- dataA |> Array.fold (fun s v -> AVLSet.add v s) AVLSet.empty - self.setB <- dataB |> Array.fold (fun s v -> AVLSet.add v s) AVLSet.empty + self.setA <- + dataA + |> Array.fold + (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + + self.setB <- + dataB + |> Array.fold + (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + + [] + [] + member self.SequentialUnion() = AVLSet.union self.setA self.setB [] - [] - member self.``Adding one element``() = AVLSet.add self.rndInt self.setA + [] + member self.UnionViaTreeTraversal() = + AVLSet.Traversal.union self.setA self.setB + + [] + [] + member self.SequentialIntersection() = AVLSet.intersection self.setA self.setB [] - [] - member self.``Deleting one element``() = AVLSet.delete self.rndInt self.setA + [] + member self.IntersectionViaTreeTraversal() = + AVLSet.Traversal.intersection self.setA self.setB [] - [] - member self.``Sequential union``() = AVLSet.union self.setA self.setB + [] + member self.SequentialDifference() = AVLSet.difference self.setA self.setB [] - [] - member self.``Union via tree traversal``() = - AVLSet.unionTraversal self.setA self.setB + [] + member self.DifferenceViaTreeTraversal() = + AVLSet.Traversal.difference self.setA self.setB + + [] + [] + member self.SequentialSymmetricalDifference() = + AVLSet.symmDifference self.setA self.setB [] - [] - member self.``Parallel union with threads``() = - let opts = ParallelOptions() - opts.MaxDegreeOfParallelism <- self.Threads + [] + member self.SymmetricalDifferenceViaTreeTraversal() = + AVLSet.Traversal.symmDifference self.setA self.setB + + +[] +[] +[] +[] +[] +[] +type ParallelSetsBenchmark() = + let rnd = System.Random(1234561) + + [] + [] + val mutable public A: int + + [] + [] + val mutable public B: int + + [] + [] + val mutable public threads: int + + [] + val mutable public setA: AVLSet + + [] + val mutable public setB: AVLSet + + [] + member self.Setup() = + let dataA = Array.init self.A (fun _ -> rnd.Next()) + + let dataB = Array.init self.B (fun _ -> rnd.Next()) + + self.setA <- + dataA + |> Array.fold + (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + + self.setB <- + dataB + |> Array.fold + (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty - AVLSet.parallelUnion opts self.setA self.setB [] - [] - member self.``Sequential intersection``() = AVLSet.intersection self.setA self.setB + [] + member self.SequentialUnion() = AVLSet.union self.setA self.setB [] + [] + member self.ParallelUnionWithThreads() = + ParallelAVLSet.union (Some self.threads) self.setA self.setB + + [] [] - member self.``Intersection via tree traversal``() = - AVLSet.intersectionTraversal self.setA self.setB + member self.SequentialIntersection() = AVLSet.intersection self.setA self.setB [] [] - member self.``Parallel intersection with threads``() = - let opts = ParallelOptions() - opts.MaxDegreeOfParallelism <- self.Threads - - AVLSet.parallelIntersection opts self.setA self.setB + member self.ParallelIntersectionWithThreads() = + ParallelAVLSet.intersection (Some self.threads) self.setA self.setB [] [] - member self.``Sequential difference``() = AVLSet.difference self.setA self.setB - - [] - [] - member self.``Difference via tree traversal``() = - AVLSet.differenceTraversal self.setA self.setB + member self.SequentialDifference() = AVLSet.difference self.setA self.setB [] [] - member self.``Parallel difference with threads``() = - let opts = ParallelOptions() - opts.MaxDegreeOfParallelism <- self.Threads - - AVLSet.parallelDifference opts self.setA self.setB + member self.ParallelDifferenceWithThreads() = + ParallelAVLSet.difference (Some self.threads) self.setA self.setB [] [] - member self.``Sequential symmetrical difference``() = + member self.SequentialSymmetricalDifference() = AVLSet.symmDifference self.setA self.setB [] [] - member self.``Symmetrical difference via tree traversal``() = - AVLSet.symmDifferenceTraversal self.setA self.setB - - [] - [] - member self.``Parallel symmetrical difference with threads``() = - let opts = ParallelOptions() - opts.MaxDegreeOfParallelism <- self.Threads - - AVLSet.parallelSymmDifference opts self.setA self.setB + member self.ParallelSymmetricalDifferenceWithThreads() = + ParallelAVLSet.symmDifference (Some self.threads) self.setA self.setB diff --git a/benchmarks/AVLSet.Benchmarks/Program.fs b/benchmarks/AVLSet.Benchmarks/Program.fs index 2c180ba..0407414 100644 --- a/benchmarks/AVLSet.Benchmarks/Program.fs +++ b/benchmarks/AVLSet.Benchmarks/Program.fs @@ -1,7 +1,12 @@ open BenchmarkDotNet.Running -open AVLSet.Benchmarks [] -let main args = - BenchmarkRunner.Run() |> ignore +let main argv = + let benchmarks = + BenchmarkSwitcher + [| typeof + typeof + typeof |] + + benchmarks.Run argv |> ignore 0 diff --git a/tests/AVLSet.PropertyTests/Tests.fs b/tests/AVLSet.PropertyTests/Tests.fs index 3f0e818..90c965f 100644 --- a/tests/AVLSet.PropertyTests/Tests.fs +++ b/tests/AVLSet.PropertyTests/Tests.fs @@ -13,8 +13,8 @@ module SetPropertyTests = let isInBounds = mn |> Option.forall (fun mn -> v > mn) && mx |> Option.forall (fun mx -> v < mx) - let lnHeight = Node.height ln - let rnHeight = Node.height rn + let lnHeight = Tree.height ln + let rnHeight = Tree.height rn isInBounds && h = (max lnHeight rnHeight + 1) diff --git a/tests/AVLSet.UnitTests/Tests.fs b/tests/AVLSet.UnitTests/Tests.fs index 6870734..faf3ae5 100644 --- a/tests/AVLSet.UnitTests/Tests.fs +++ b/tests/AVLSet.UnitTests/Tests.fs @@ -12,8 +12,8 @@ module SetUnitTests = let isInBounds = mn |> Option.forall (fun mn -> v > mn) && mx |> Option.forall (fun mx -> v < mx) - let lnHeight = Node.height ln - let rnHeight = Node.height rn + let lnHeight = Tree.height ln + let rnHeight = Tree.height rn isInBounds && h = (max lnHeight rnHeight + 1) From b0b85985003b84ab346ad10f7f62a274681e8875 Mon Sep 17 00:00:00 2001 From: Taranukha Leonid Date: Wed, 3 Jun 2026 23:12:45 +0300 Subject: [PATCH 3/4] fix README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2d2897..cd00226 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ The `AVLSet` module provides a comprehensive interface: |:---|:---|:---| | **add** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Adds an element. | | **delete** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Removes an element. | -| **contains** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Copies an set. | -| **copy** | `'a -> AVLTree<'a> -> bool` | Checks membership. | +| **contains** | `'a -> AVLTree<'a> -> AVLTree<'a>` | Checks membership. | +| **copy** | `'a -> AVLTree<'a> -> bool` | Copies an set. | | **union** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard union ($A \cup B$). | | **intersection** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard intersection ($A \cap B$). | | **difference** | `AVLTree<'a> -> AVLTree<'a> -> AVLTree<'a>` | Standard difference ($A \setminus B$). | From 3e6b2622472bdfaf917d70af437016099db0a4ac Mon Sep 17 00:00:00 2001 From: Taranukha Leonid Date: Thu, 4 Jun 2026 00:03:23 +0300 Subject: [PATCH 4/4] format files --- benchmarks/AVLSet.Benchmarks/Benchmarks.fs | 25 +++---------- src/AVLSet.Library/LibraryParallel.fs | 2 +- tests/AVLSet.PropertyTests/Tests.fs | 42 +++++++++++----------- 3 files changed, 27 insertions(+), 42 deletions(-) diff --git a/benchmarks/AVLSet.Benchmarks/Benchmarks.fs b/benchmarks/AVLSet.Benchmarks/Benchmarks.fs index 61d7c0f..e0f0f59 100644 --- a/benchmarks/AVLSet.Benchmarks/Benchmarks.fs +++ b/benchmarks/AVLSet.Benchmarks/Benchmarks.fs @@ -28,10 +28,7 @@ type SingleOpsBenchmark() = let dataA = Array.init self.A (fun _ -> rnd.Next()) - self.setA <- - dataA - |> Array.fold - (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + self.setA <- dataA |> Array.fold (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty [] [] @@ -69,15 +66,9 @@ type SequentialSetsBenchmark() = let dataB = Array.init self.B (fun _ -> rnd.Next()) - self.setA <- - dataA - |> Array.fold - (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + self.setA <- dataA |> Array.fold (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty - self.setB <- - dataB - |> Array.fold - (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + self.setB <- dataB |> Array.fold (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty [] [] @@ -150,15 +141,9 @@ type ParallelSetsBenchmark() = let dataB = Array.init self.B (fun _ -> rnd.Next()) - self.setA <- - dataA - |> Array.fold - (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + self.setA <- dataA |> Array.fold (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty - self.setB <- - dataB - |> Array.fold - (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty + self.setB <- dataB |> Array.fold (fun (set: AVLSet) v -> AVLSet.add v set) AVLSet.empty [] diff --git a/src/AVLSet.Library/LibraryParallel.fs b/src/AVLSet.Library/LibraryParallel.fs index c05bae8..6657bc6 100644 --- a/src/AVLSet.Library/LibraryParallel.fs +++ b/src/AVLSet.Library/LibraryParallel.fs @@ -116,4 +116,4 @@ module ParallelAVLSet = differenceAsync threads t1 t2 |> Async.RunSynchronously let symmDifference threads t1 t2 = - symmDifferenceAsync threads t1 t2 |> Async.RunSynchronously \ No newline at end of file + symmDifferenceAsync threads t1 t2 |> Async.RunSynchronously diff --git a/tests/AVLSet.PropertyTests/Tests.fs b/tests/AVLSet.PropertyTests/Tests.fs index 90c965f..6e57f60 100644 --- a/tests/AVLSet.PropertyTests/Tests.fs +++ b/tests/AVLSet.PropertyTests/Tests.fs @@ -34,13 +34,13 @@ module SetPropertyTests = [] let ``Adding elemements to set`` (elements: int list) = - let set = elements |> List.fold (fun t x -> AVLSet.add x t) AVLSet.empty + let set = elements |> List.fold (fun t x -> AVLSet.add x t) AVLSet.empty - let rec setContainsList list set = + let rec setContainsList list set = match list with - | [ ] -> true + | [] -> true | head :: tail -> AVLSet.contains head set && setContainsList tail set - + isSetValid set None None |> should be True setContainsList elements set |> should be True @@ -49,10 +49,10 @@ module SetPropertyTests = let set = elements |> List.fold (fun t x -> AVLSet.add x t) AVLSet.empty AVLSet.copy set |> should equal set - + [] let ``Deleting elements from set`` (elements: int list) = - let set = elements |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let set = elements |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let emptySet = elements |> List.fold (fun t e -> AVLSet.delete e t) set let empty: AVLSet = AVLSet.empty @@ -60,8 +60,8 @@ module SetPropertyTests = [] let ``Standard set union`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty - let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let unionSet = AVLSet.union setA setB isSetValid unionSet None None |> should be True @@ -79,8 +79,8 @@ module SetPropertyTests = [] let ``Standard set intersection`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty - let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let intersectionSet = AVLSet.intersection setA setB isSetValid intersectionSet None None |> should be True @@ -96,8 +96,8 @@ module SetPropertyTests = [] let ``Standard set difference`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty - let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let differenceSet = AVLSet.difference setA setB isSetValid differenceSet None None |> should be True @@ -107,7 +107,7 @@ module SetPropertyTests = [] let ``Standard symmetric difference`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let symmDiffSet = AVLSet.symmDifference setA setB @@ -125,7 +125,7 @@ module SetPropertyTests = [] let ``Union via tree traversal`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let unionSet = AVLSet.Traversal.union setA setB @@ -143,7 +143,7 @@ module SetPropertyTests = [] let ``Intersection via tree traversal`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let intersectionSet = AVLSet.Traversal.intersection setA setB @@ -160,7 +160,7 @@ module SetPropertyTests = [] let ``Difference via tree traversal`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let differenceSet = AVLSet.Traversal.difference setA setB @@ -171,7 +171,7 @@ module SetPropertyTests = [] let ``Symmetric difference via tree traversal`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let symmDiffSet = AVLSet.Traversal.symmDifference setA setB @@ -189,7 +189,7 @@ module SetPropertyTests = [] let ``Parallel set union with threads`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let unionSet = ParallelAVLSet.union None setA setB @@ -207,7 +207,7 @@ module SetPropertyTests = [] let ``Parallel set intersection with threads`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let intersectionSet = ParallelAVLSet.intersection None setA setB @@ -224,7 +224,7 @@ module SetPropertyTests = [] let ``Parallel set difference with threads`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let differenceSet = ParallelAVLSet.difference None setA setB @@ -235,7 +235,7 @@ module SetPropertyTests = [] let ``Parallel set symmetric difference with threads`` (elementsA: int list, elementsB: int list) = - let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty + let setA = elementsA |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let setB = elementsB |> List.fold (fun t e -> AVLSet.add e t) AVLSet.empty let symmDiffSet = ParallelAVLSet.symmDifference None setA setB