/**
* Replication Benchmarker
* https://github.com/score-team/replication-benchmarker/
* Copyright (C) 2013 LORIA / Inria / SCORE Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package collect;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Implementation of a vector clock.
* A map replica identifier -> logical clock.
* @author urso
* @author oster
*/
public class VectorClock extends HashMap<Integer, Integer> {
public enum Causality {HB, CO, HA};
public VectorClock() {
super();
}
// TODO: test me, plz
public VectorClock(VectorClock siteVC) {
super(siteVC);
}
/*
* Is this VC is ready to integrate O ?
* true iff VCr = Or - 1 && for all i!=r, VCi >= Oi
*/
public boolean readyFor(int r, VectorClock O) {
if (this.getSafe(r) != O.get(r) - 1) {
return false;
}
Iterator<Map.Entry<Integer, Integer>> it = O.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> e = it.next();
if ((e.getKey() != r) && (this.getSafe(e.getKey()) < e.getValue())) {
return false;
}
}
return true;
}
/**
* Get the sum of all entries
* added by Roh.
*/
public int getSum(){
int sum = 0;
Iterator<Map.Entry<Integer, Integer>> it = this.entrySet().iterator();
while (it.hasNext()) sum+= it.next().getValue();
return sum;
}
/*
* Increment an entry.
*/
public void inc(int r) {
put(r, getSafe(r) + 1);
}
/*
* Increment an entry by n.
*/
public void incN(int r, int n) {
put(r, getSafe(r) + n);
}
/*
* Returns the entry for replica r. 0 if none.
*/
public int getSafe(int r) {
Integer v = get(r);
return (v != null) ? v : 0;
}
/*
* Is this VC > T ?
* true iff for all i, VCi >= Ti and exists j VCj > Tj
*/
public boolean greaterThan(VectorClock T) {
boolean gt = false;
Iterator<Map.Entry<Integer, Integer>> it = T.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> i = it.next();
if (this.getSafe(i.getKey()) < i.getValue()) {
return false;
} else if (this.getSafe(i.getKey()) > i.getValue()) {
gt = true;
}
}
if (gt) {
return true;
}
it = this.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> i = it.next();
if (T.getSafe(i.getKey()) < i.getValue()) {
return true;
}
}
return false;
}
/**
* This function works correctly only when causality is preserved.
* added by Roh
*/
public static Causality comp(int s1, VectorClock T1, int s2, VectorClock T2){
int e11=T1.getSafe(s1);
int e12=T2.getSafe(s1);
int e21=T1.getSafe(s2);
int e22=T2.getSafe(s2);
if(e11 > e12 && e21 >= e22) return Causality.HA; // T1 happened after T2; T2->T1
else if(e11 <= e12 && e21 < e22) return Causality.HB; // T1 Happened before T2; T1->T2
return Causality.CO;
}
@Override
public String toString(){
StringBuilder ret = new StringBuilder("{");
Iterator<Map.Entry<Integer, Integer>> it = this.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Integer, Integer> i = it.next();
ret.append("(").append(i.getKey()).append(",").append(i.getValue()).append("),");
}
return ret.append("}").toString();
}
/*
* Is this VC // T ?
* true iff nor VC > T nor T > VC
*/
public boolean concurrent(VectorClock T) {
return !(this.greaterThan(T) || T.greaterThan(this));
}
/**
* Sets each entry of the VC to max(VCi, Oi)
*/
public void upTo(VectorClock O) {
for (Entry<Integer, Integer> k : O.entrySet()) {
if (k.getValue() > this.getSafe(k.getKey())) {
this.put(k.getKey(), k.getValue());
}
}
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final VectorClock other = (VectorClock) obj;
Set<Integer> h = new HashSet<Integer>(this.keySet());
h.addAll(other.keySet());
for (Integer k : h) {
if (this.getSafe(k) != other.getSafe(k)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
for (Entry<Integer, Integer> k : this.entrySet()) {
hash += 7 * k.getKey() * k.getValue();
}
return hash;
}
/*
* computes minimal vector from current and vector clocks provided in parameters.
* for each vc in {this} U otherVectorClocks, for each i in min, min[i] <= vc[i]
*/
public VectorClock min(int localSiteId, Map<Integer, VectorClock> otherVectorClocks) {
VectorClock min = new VectorClock(this);
// min.put(localSiteId, Math.max(min.getSafe(localSiteId), 0));
for (Entry<Integer, VectorClock> clockEntry : otherVectorClocks.entrySet()) {
for (Entry<Integer, Integer> entry : min.entrySet()) {
int e = entry.getKey();
min.put(e, Math.min(min.getSafe(e), clockEntry.getValue().getSafe(e)));
}
}
return min;
}
}