Repository: TheAlgorithms/Java
Pull request #4977 (2023-12-07 11:32:38)

Introduce 2P-Set (Two-Phase Set) for both addition and removal operations in distributed systems
<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

This PR consists of an implementation of a 2P-Set (Two-Phase Set), which is a state-based CRDT (Conflict-free Replicated Data Type) for managing sets with support for both addition and removal operations in a distributed and concurrent environment.

See: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type

- [X] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [X] This pull request is all my own work -- I have not plagiarized it.
- [X] All filenames are in PascalCase.
- [X] All functions and variable names follow Java naming conventions.
- [X] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [X] All new code is formatted with `clang-format -i --style=file path/to/your/file.java


Patch:
diff --git a/DIRECTORY.md b/DIRECTORY.md
index 4a948095..703642a0 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -86,6 +86,7 @@
               * [GCounter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java)
               * [GSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java)
               * [PNCounter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java)
+              * [TwoPSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java)
             * disjointsetunion
               * [DisjointSetUnion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnion.java)
               * [Node](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/disjointsetunion/Node.java)
@@ -528,7 +529,7 @@
             * [InfixToPostfix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/InfixToPostfix.java)
             * [LargestRectangle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/LargestRectangle.java)
             * [MaximumMinimumWindow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/MaximumMinimumWindow.java)
-            * [NextGraterElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/NextGraterElement.java)
+            * [NextGreaterElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/NextGreaterElement.java)
             * [NextSmallerElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/NextSmallerElement.java)
             * [PostfixToInfix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PostfixToInfix.java)
             * [StackPostfixNotation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java)
@@ -621,6 +622,7 @@
               * [GCounterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/GCounterTest.java)
               * [GSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/GSetTest.java)
               * [PNCounterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/PNCounterTest.java)
+              * [TwoPSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java)
             * disjointsetunion
               * [DisjointSetUnionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnionTest.java)
             * graphs
diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java
new file mode 100644
index 00000000..f5e155c2
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java
@@ -0,0 +1,84 @@
+package com.thealgorithms.datastructures.crdt;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * TwoPhaseSet (2P-Set) is a state-based CRDT (Conflict-free Replicated Data Type) designed for managing sets
+ * with support for both addition and removal operations in a distributed and concurrent environment.
+ * It combines two G-Sets (grow-only sets) - one set for additions and another set (tombstone set) for removals.
+ * Once an element is removed and placed in the tombstone set, it cannot be re-added, adhering to "remove-wins" semantics.
+ * This implementation supports querying the presence of elements, adding elements, removing elements,
+ * comparing with other 2P-Sets, and merging two 2P-Sets while preserving the remove-wins semantics.
+ * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
+ *
+ * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah)
+ */
+
+public class TwoPSet {
+    private Set<String> setA;
+    private Set<String> setR;
+
+    /**
+     * Constructs an empty Two-Phase Set.
+     */
+    public TwoPSet() {
+        this.setA = new HashSet<>();
+        this.setR = new HashSet<>();
+    }
+
+    /**
+     * Checks if an element is in the set and has not been removed.
+     *
+     * @param element The element to be checked.
+     * @return True if the element is in the set and has not been removed, otherwise false.
+     */
+    public boolean lookup(String element) {
+        return setA.contains(element) && !setR.contains(element);
+    }
+
+    /**
+     * Adds an element to the set.
+     *
+     * @param element The element to be added.
+     */
+    public void add(String element) {
+        setA.add(element);
+    }
+
+    /**
+     * Removes an element from the set. The element will be placed in the tombstone set.
+     *
+     * @param element The element to be removed.
+     */
+    public void remove(String element) {
+        if (lookup(element)) {
+            setR.add(element);
+        }
+    }
+
+    /**
+     * Compares the current 2P-Set with another 2P-Set.
+     *
+     * @param otherSet The other 2P-Set to compare with.
+     * @return True if both SetA and SetR are subset, otherwise false.
+     */
+    public boolean compare(TwoPSet otherSet) {
+        return otherSet.setA.containsAll(setA) && otherSet.setR.containsAll(setR);
+    }
+
+    /**
+     * Merges the current 2P-Set with another 2P-Set.
+     *
+     * @param otherSet The other 2P-Set to merge with.
+     * @return A new 2P-Set containing the merged elements.
+     */
+    public TwoPSet merge(TwoPSet otherSet) {
+        TwoPSet mergedSet = new TwoPSet();
+        mergedSet.setA.addAll(this.setA);
+        mergedSet.setA.addAll(otherSet.setA);
+        mergedSet.setR.addAll(this.setR);
+        mergedSet.setR.addAll(otherSet.setR);
+        return mergedSet;
+    }
+}


Test patch:
diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java
new file mode 100644
index 00000000..18ab5c16
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java
@@ -0,0 +1,68 @@
+package com.thealgorithms.datastructures.crdt;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class TwoPSetTest {
+
+    private TwoPSet set;
+
+    @BeforeEach
+    void setUp() {
+        set = new TwoPSet();
+    }
+
+    @Test
+    void testLookup() {
+        set.add("A");
+        assertTrue(set.lookup("A"));
+        assertFalse(set.lookup("B"));
+        set.remove("A");
+        assertFalse(set.lookup("A"));
+    }
+
+    @Test
+    void testAdd() {
+        set.add("A");
+        assertTrue(set.lookup("A"));
+    }
+
+    @Test
+    void testRemove() {
+        set.add("A");
+        set.remove("A");
+        assertFalse(set.lookup("A"));
+    }
+
+    @Test
+    void testCompare() {
+        TwoPSet set1 = new TwoPSet();
+        set1.add("A");
+        set1.add("B");
+        TwoPSet set2 = new TwoPSet();
+        set2.add("A");
+        assertFalse(set1.compare(set2));
+        set2.add("B");
+        assertTrue(set1.compare(set2));
+        set1.remove("A");
+        assertFalse(set1.compare(set2));
+        set2.remove("A");
+        assertTrue(set1.compare(set2));
+    }
+
+    @Test
+    void testMerge() {
+        TwoPSet set1 = new TwoPSet();
+        set1.add("A");
+        set1.add("B");
+        TwoPSet set2 = new TwoPSet();
+        set2.add("B");
+        set2.add("C");
+        TwoPSet mergedSet = set1.merge(set2);
+        assertTrue(mergedSet.lookup("A"));
+        assertTrue(mergedSet.lookup("B"));
+        assertTrue(mergedSet.lookup("C"));
+    }
+}