Add G-Counter (Grow-only Counter) (#4965)
This commit is contained in:
parent
c527dff92d
commit
b1efd4e34b
@ -0,0 +1,84 @@
|
|||||||
|
package com.thealgorithms.datastructures.crdt;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* G-Counter (Grow-only Counter) is a state-based CRDT (Conflict-free Replicated Data Type)
|
||||||
|
* designed for tracking counts in a distributed and concurrent environment.
|
||||||
|
* Each process maintains its own counter, allowing only increments. The total count
|
||||||
|
* is obtained by summing individual process counts.
|
||||||
|
* This implementation supports incrementing, querying the total count,
|
||||||
|
* comparing with other G-Counters, and merging with another G-Counter
|
||||||
|
* to compute the element-wise maximum.
|
||||||
|
* (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
|
||||||
|
*
|
||||||
|
* @author itakurah (https://github.com/itakurah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GCounter {
|
||||||
|
private final Map<Integer, Integer> P;
|
||||||
|
private final int myId;
|
||||||
|
private final int n;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a G-Counter for a cluster of n nodes.
|
||||||
|
*
|
||||||
|
* @param n The number of nodes in the cluster.
|
||||||
|
*/
|
||||||
|
public GCounter(int myId, int n) {
|
||||||
|
this.myId = myId;
|
||||||
|
this.n = n;
|
||||||
|
this.P = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
P.put(i, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the counter for the current node.
|
||||||
|
*/
|
||||||
|
public void increment() {
|
||||||
|
P.put(myId, P.get(myId) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total value of the counter by summing up values from all nodes.
|
||||||
|
*
|
||||||
|
* @return The total value of the counter.
|
||||||
|
*/
|
||||||
|
public int value() {
|
||||||
|
int sum = 0;
|
||||||
|
for (int v : P.values()) {
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the state of this G-Counter with another G-Counter.
|
||||||
|
*
|
||||||
|
* @param other The other G-Counter to compare with.
|
||||||
|
* @return True if the state of this G-Counter is less than or equal to the state of the other G-Counter.
|
||||||
|
*/
|
||||||
|
public boolean compare(GCounter other) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (this.P.get(i) > other.P.get(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the state of this G-Counter with another G-Counter.
|
||||||
|
*
|
||||||
|
* @param other The other G-Counter to merge with.
|
||||||
|
*/
|
||||||
|
public void merge(GCounter other) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
this.P.put(i, Math.max(this.P.get(i), other.P.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.thealgorithms.datastructures.crdt;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class GCounterTest {
|
||||||
|
@Test
|
||||||
|
void increment() {
|
||||||
|
GCounter counter = new GCounter(0, 3);
|
||||||
|
counter.increment();
|
||||||
|
counter.increment();
|
||||||
|
counter.increment();
|
||||||
|
assertEquals(3, counter.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void merge() {
|
||||||
|
GCounter counter1 = new GCounter(0, 3);
|
||||||
|
counter1.increment();
|
||||||
|
GCounter counter2 = new GCounter(1, 3);
|
||||||
|
counter2.increment();
|
||||||
|
counter2.increment();
|
||||||
|
GCounter counter3 = new GCounter(2, 3);
|
||||||
|
counter3.increment();
|
||||||
|
counter3.increment();
|
||||||
|
counter3.increment();
|
||||||
|
counter1.merge(counter2);
|
||||||
|
counter1.merge(counter3);
|
||||||
|
counter2.merge(counter1);
|
||||||
|
counter3.merge(counter2);
|
||||||
|
assertEquals(6, counter1.value());
|
||||||
|
assertEquals(6, counter2.value());
|
||||||
|
assertEquals(6, counter3.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void compare() {
|
||||||
|
GCounter counter1 = new GCounter(0, 5);
|
||||||
|
GCounter counter2 = new GCounter(3, 5);
|
||||||
|
counter1.increment();
|
||||||
|
counter1.increment();
|
||||||
|
counter2.merge(counter1);
|
||||||
|
counter2.increment();
|
||||||
|
counter2.increment();
|
||||||
|
assertTrue(counter1.compare(counter2));
|
||||||
|
counter1.increment();
|
||||||
|
counter2.increment();
|
||||||
|
counter2.merge(counter1);
|
||||||
|
assertTrue(counter1.compare(counter2));
|
||||||
|
counter1.increment();
|
||||||
|
assertFalse(counter1.compare(counter2));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user