/** * 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 jbenchmarker.rgabigdecimal; import collect.RangeList; import jbenchmarker.core.Document; import jbenchmarker.core.SequenceOperation; import java.util.HashMap; import java.util.NoSuchElementException; import crdt.Operation; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * * @author Roh, urso */ public class RGADocument<T> implements Document { private final HashMap<RGAS2Vector, RGANode<T>> hash; private final RangeList<RGANode<T>> localOrder; private final RGANode head; public RGADocument() { super(); head = new RGANode(); head.setPosition(BigDecimal.ZERO); hash = new HashMap<RGAS2Vector, RGANode<T>>(); localOrder = new RangeList<RGANode<T>>(); } @Override public String view() { StringBuilder s = new StringBuilder(); for (RGANode<T> n : localOrder) { s.append(n.getContent()); } return s.toString(); } @Override public void apply(Operation op) { RGAOperation rgaop = (RGAOperation) op; if (rgaop.getType() == SequenceOperation.OpType.delete) { boolean wasVisible = remoteDelete(rgaop); if (wasVisible) { localOrder.remove(findLocal(hash.get(rgaop.getS4VPos()).getPosition())); } } else { RGANode prev; if (rgaop.getS4VPos() == null) { prev = head; } else { prev = hash.get(rgaop.getS4VPos()); } RGANode n = remoteInsert(prev, rgaop); n.setPosition(middle(previousPosition(n), nextPosition(n))); localOrder.add(findLocal(n.getPosition()), n); } } RGANode remoteInsert(RGANode prev, RGAOperation op) { RGANode newnd = new RGANode(op.getS4VTms(), op.getContent()); RGANode next; RGAS2Vector s4v = op.getS4VTms(); if (prev == null) { throw new NoSuchElementException("RemoteInsert"); } next = prev.getNext(); while (next != null) { if (s4v.compareTo(next.getKey()) == RGAS2Vector.AFTER) { break; } prev = next; next = next.getNext(); } newnd.setNext(next); newnd.setLast(prev); prev.setNext(newnd); if (next != null) { next.setLast(newnd); } hash.put(op.getS4VTms(), newnd); return newnd; } boolean remoteDelete(RGAOperation op) { boolean wasVisible; RGANode node = hash.get(op.getS4VPos()); if (node == null) { throw new NoSuchElementException("Cannot find" + op.getS4VPos()); } wasVisible = node.isVisible(); node.makeTombstone(); return wasVisible; } public RGAS2Vector getVisibleS4V(int v) { RGANode node = getVisibleNode(v); if (node == null) { throw new NoSuchElementException("getVisibleS4V"); } return node.getKey(); } public RGANode getVisibleNode(int v) { if (v == 0) { return head; } else { return localOrder.get(v-1); } } @Override public int viewLength() { return localOrder.size(); } /** * Add a list of nodes in the local order table. * @param i position * @param ln nodes */ void addLocal(int i, List<RGANode<T>> ln) { localOrder.addAll(i, ln); } /** * Remove a range of nodes from the local order table. * @param p position * @param offset number of nodes */ void removeLocal(int p, int offset) { localOrder.removeRangeOffset(p, offset); } /** * Next position to be taken into account after a node * @param prev the node * @return the position of the next visible node. ONE if none */ private BigDecimal nextPosition(RGANode prev) { prev = prev.getNextVisible(); if (prev == null) { return BigDecimal.ONE; } else { return prev.getPosition(); } } /** * Previous position to be taken into account after a node * @param prev the node * @return the position of the previous visible node. ZERO if none */ private BigDecimal previousPosition(RGANode prev) { prev = prev.getLastVisible(); if (prev == null) { return BigDecimal.ZERO; } else { return prev.getPosition(); } } /** * Dichotomic search to find a position in the local order table. * @param pos the position * @return the lower index with a position greater or equal pos. */ private int findLocal(BigDecimal pos) { int startIndex = 0, endIndex = localOrder.size(), middleIndex; while (startIndex < endIndex) { middleIndex = startIndex + (endIndex - startIndex) / 2; int c = localOrder.get(middleIndex).getPosition().compareTo(pos); if (c == 0) { return middleIndex; } else if (c < 0) { startIndex = middleIndex + 1; } else { endIndex = middleIndex; } } return startIndex; } /** * Middle of two bigdecimal * @param a A * @param b B (B > A) * @return (A + (B - A) / 2) */ public static BigDecimal middle(BigDecimal a, BigDecimal b) { return a.add(b.subtract(a).divide(BigDecimal.valueOf(2))); } }