diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/BinaryTreeCharBag.java b/src/main/java/me/mrletsplay/shareclientcore/document/BinaryTreeCharBag.java new file mode 100644 index 0000000..b7f2bdf --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/document/BinaryTreeCharBag.java @@ -0,0 +1,71 @@ +package me.mrletsplay.shareclientcore.document; + +import java.util.Iterator; +import java.util.List; + +import me.mrletsplay.shareclientcore.debug.DebugValues; +import me.mrletsplay.shareclientcore.util.AVLOrderTree; + +public class BinaryTreeCharBag implements CharBag { + + private AVLOrderTree chars; + + public BinaryTreeCharBag() { + this.chars = new AVLOrderTree<>(Util::compareChars); + } + + @Override + public int add(Char character) { + return chars.addIndex(character); + } + + @Override + public int remove(Char character) { + return chars.removeIndex(character); + } + + @Override + public Char get(int index) { + return chars.get(index); + } + + @Override + public void clear() { + chars.clear(); + } + + @Override + public int size() { + return chars.size(); + } + + @Override + public List toList() { + return chars.stream().toList(); + } + + @Override + public byte[] getContents() { + byte[] bytes = new byte[chars.size()]; + int i = 0; + Iterator it = chars.iterator(); + while(it.hasNext()) { + Char c = it.next(); + bytes[i++] = c.value(); + } + return bytes; + } + + @Override + public String getContentsAsString() { + // TODO Auto-generated method stub + return null; + } + + @Override + public DebugValues getDebugValues() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/util/AVLOrderTree.java b/src/main/java/me/mrletsplay/shareclientcore/util/AVLOrderTree.java new file mode 100644 index 0000000..cfdf9cd --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/util/AVLOrderTree.java @@ -0,0 +1,188 @@ +package me.mrletsplay.shareclientcore.util; + +import java.util.AbstractCollection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A balanced binary tree that supports order statistics ({@link #get(int)} and {@link #indexOf(Object)}).
+ * Can only hold unique elements + * @param The element type + */ +public class AVLOrderTree extends AbstractCollection { + + private Node root; + private Comparator comparator; + + public AVLOrderTree(Comparator comparator) { + this.comparator = comparator; + } + + @SuppressWarnings("unchecked") + public AVLOrderTree() { + this.comparator = (Comparator) Comparator.naturalOrder(); + } + + @Override + public boolean add(E e) { + return addIndex(e) != -1; + } + + public int addIndex(E e) { + if(root == null) { + root = new Node(e); + return 0; + } + + return index(root.add(e)); + } + + @Override + public boolean remove(Object o) { + return removeIndex(o) != -1; + } + + @SuppressWarnings("unchecked") + public int removeIndex(Object o) { + if(root == null) return -1; + + Node removed = root.remove((E) o); + if(removed == root) root = null; + return index(removed); + } + + @Override + public void clear() { + root = null; + } + + public int indexOf(E e) { + return 0; // TODO + } + + private int index(Node n) { + return 0; // TODO + } + + public E get(int index) { + return null; + } + + @Override + public Iterator iterator() { + return new TreeIterator(); + } + + @Override + public int size() { + Iterator it = iterator(); + int i = 0; + while(it.hasNext()) { + it.next(); + i++; + } + return i; + } + + private class TreeIterator implements Iterator { + + private Node current; + private Node next; + + public TreeIterator() { + this.current = null; + this.next = root == null ? null : root.leftmostChild(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public E next() { + // FIXME incorrect + if(next == null) throw new NoSuchElementException(); + + current = next; + + Node afterNext; + if(next.right == null) { + afterNext = (next.parent != null && next.parent.left == next) ? next.parent : null; + }else { + afterNext = next.right.leftmostChild(); + } + + next = afterNext; + return current.value; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); // TODO + } + + } + + private class Node { + + private E value; + private Node parent; + private Node left; + private Node right; + private int balance; + private int size; + + public Node(E value) { + this.value = value; + } + + public Node add(E value) { + // TODO: rebalance + int comp = comparator.compare(this.value, value); + if(comp == 0) return null; + + if(comp < 0) { + if(left == null) { + return left = new Node(value); + } + + return left.add(value); + }else { + if(right == null) { + return right = new Node(value); + } + + return right.add(value); + } + } + + public Node remove(E value) { + // TODO: rebalance + int comp = comparator.compare(value, value); + if(comp == 0) return this; + + if(comp < 0) { + if(left == null) return null; + + Node removed = left.remove(value); + if(removed == left) left = null; + return removed; + }else { + if(right == null) return null; + + Node removed = right.remove(value); + if(removed == right) right = null; + return removed; + } + } + + public Node leftmostChild() { + if(left == null) return this; + return left.leftmostChild(); + } + + } + +} diff --git a/src/test/java/me/mrletsplay/shareclientcore/CharBagTest.java b/src/test/java/me/mrletsplay/shareclientcore/CharBagTest.java index 2f54e46..75f4662 100644 --- a/src/test/java/me/mrletsplay/shareclientcore/CharBagTest.java +++ b/src/test/java/me/mrletsplay/shareclientcore/CharBagTest.java @@ -1,38 +1,36 @@ package me.mrletsplay.shareclientcore; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Collections; import java.util.List; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.MethodSource; import me.mrletsplay.shareclientcore.document.ArrayCharBag; +import me.mrletsplay.shareclientcore.document.BinaryTreeCharBag; import me.mrletsplay.shareclientcore.document.Char; import me.mrletsplay.shareclientcore.document.CharBag; import me.mrletsplay.shareclientcore.document.Identifier; public class CharBagTest { - private CharBag createBag(Class bagClass) { - return assertDoesNotThrow(() -> bagClass.getDeclaredConstructor().newInstance()); + private static List getBags() { + return List.of(new ArrayCharBag(), new BinaryTreeCharBag()); } @ParameterizedTest - @ValueSource(classes = { ArrayCharBag.class }) - public void testCharBagAdd(Class bagClass) { - CharBag bag = createBag(bagClass); + @MethodSource("getBags") + public void testCharBagAdd(CharBag bag) { Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); assertEquals(0, bag.add(c)); assertEquals(List.of(c), bag.toList()); } @ParameterizedTest - @ValueSource(classes = { ArrayCharBag.class }) - public void testCharBagAddBetween(Class bagClass) { - CharBag bag = createBag(bagClass); + @MethodSource("getBags") + public void testCharBagAddBetween(CharBag bag) { Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); Char c2 = new Char(new Identifier[] { new Identifier(1, 0) }, 1, (byte) 0); Char cBetween = new Char(new Identifier[] { new Identifier(0, 1) }, 2, (byte) 0); @@ -48,9 +46,8 @@ public class CharBagTest { } @ParameterizedTest - @ValueSource(classes = { ArrayCharBag.class }) - public void testCharBagRemove(Class bagClass) { - CharBag bag = createBag(bagClass); + @MethodSource("getBags") + public void testCharBagRemove(CharBag bag) { Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); bag.add(c); assertEquals(0, bag.remove(c)); @@ -58,9 +55,8 @@ public class CharBagTest { } @ParameterizedTest - @ValueSource(classes = { ArrayCharBag.class }) - public void testCharBagGet(Class bagClass) { - CharBag bag = createBag(bagClass); + @MethodSource("getBags") + public void testCharBagGet(CharBag bag) { Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); Char c2 = new Char(new Identifier[] { new Identifier(0, 1) }, 2, (byte) 0); Char c3 = new Char(new Identifier[] { new Identifier(1, 0) }, 1, (byte) 0); @@ -74,9 +70,8 @@ public class CharBagTest { } @ParameterizedTest - @ValueSource(classes = { ArrayCharBag.class }) - public void testCharBagClear(Class bagClass) { - CharBag bag = createBag(bagClass); + @MethodSource("getBags") + public void testCharBagClear(CharBag bag) { Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); Char c2 = new Char(new Identifier[] { new Identifier(0, 1) }, 2, (byte) 0); Char c3 = new Char(new Identifier[] { new Identifier(1, 0) }, 1, (byte) 0);