Add TreeCharBag (WIP)

This commit is contained in:
MrLetsplay 2024-06-20 21:21:41 +02:00
parent 941a0973e7
commit a64a685e75
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
3 changed files with 273 additions and 19 deletions

View File

@ -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<Char> 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<Char> toList() {
return chars.stream().toList();
}
@Override
public byte[] getContents() {
byte[] bytes = new byte[chars.size()];
int i = 0;
Iterator<Char> 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;
}
}

View File

@ -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)}).<br>
* Can only hold unique elements
* @param <E> The element type
*/
public class AVLOrderTree<E> extends AbstractCollection<E> {
private Node root;
private Comparator<E> comparator;
public AVLOrderTree(Comparator<E> comparator) {
this.comparator = comparator;
}
@SuppressWarnings("unchecked")
public AVLOrderTree() {
this.comparator = (Comparator<E>) 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<E> iterator() {
return new TreeIterator();
}
@Override
public int size() {
Iterator<E> it = iterator();
int i = 0;
while(it.hasNext()) {
it.next();
i++;
}
return i;
}
private class TreeIterator implements Iterator<E> {
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();
}
}
}

View File

@ -1,38 +1,36 @@
package me.mrletsplay.shareclientcore; package me.mrletsplay.shareclientcore;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.jupiter.params.ParameterizedTest; 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.ArrayCharBag;
import me.mrletsplay.shareclientcore.document.BinaryTreeCharBag;
import me.mrletsplay.shareclientcore.document.Char; import me.mrletsplay.shareclientcore.document.Char;
import me.mrletsplay.shareclientcore.document.CharBag; import me.mrletsplay.shareclientcore.document.CharBag;
import me.mrletsplay.shareclientcore.document.Identifier; import me.mrletsplay.shareclientcore.document.Identifier;
public class CharBagTest { public class CharBagTest {
private CharBag createBag(Class<? extends CharBag> bagClass) { private static List<CharBag> getBags() {
return assertDoesNotThrow(() -> bagClass.getDeclaredConstructor().newInstance()); return List.of(new ArrayCharBag(), new BinaryTreeCharBag());
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(classes = { ArrayCharBag.class }) @MethodSource("getBags")
public void testCharBagAdd(Class<? extends CharBag> bagClass) { public void testCharBagAdd(CharBag bag) {
CharBag bag = createBag(bagClass);
Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0);
assertEquals(0, bag.add(c)); assertEquals(0, bag.add(c));
assertEquals(List.of(c), bag.toList()); assertEquals(List.of(c), bag.toList());
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(classes = { ArrayCharBag.class }) @MethodSource("getBags")
public void testCharBagAddBetween(Class<? extends CharBag> bagClass) { public void testCharBagAddBetween(CharBag bag) {
CharBag bag = createBag(bagClass);
Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); 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 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); Char cBetween = new Char(new Identifier[] { new Identifier(0, 1) }, 2, (byte) 0);
@ -48,9 +46,8 @@ public class CharBagTest {
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(classes = { ArrayCharBag.class }) @MethodSource("getBags")
public void testCharBagRemove(Class<? extends CharBag> bagClass) { public void testCharBagRemove(CharBag bag) {
CharBag bag = createBag(bagClass);
Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0);
bag.add(c); bag.add(c);
assertEquals(0, bag.remove(c)); assertEquals(0, bag.remove(c));
@ -58,9 +55,8 @@ public class CharBagTest {
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(classes = { ArrayCharBag.class }) @MethodSource("getBags")
public void testCharBagGet(Class<? extends CharBag> bagClass) { public void testCharBagGet(CharBag bag) {
CharBag bag = createBag(bagClass);
Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); 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 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); Char c3 = new Char(new Identifier[] { new Identifier(1, 0) }, 1, (byte) 0);
@ -74,9 +70,8 @@ public class CharBagTest {
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(classes = { ArrayCharBag.class }) @MethodSource("getBags")
public void testCharBagClear(Class<? extends CharBag> bagClass) { public void testCharBagClear(CharBag bag) {
CharBag bag = createBag(bagClass);
Char c = new Char(new Identifier[] { new Identifier(0, 0) }, 0, (byte) 0); 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 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); Char c3 = new Char(new Identifier[] { new Identifier(1, 0) }, 1, (byte) 0);