package org.sglj.util.struct;
import java.util.Random;
import junit.framework.Assert;
import org.junit.Test;
import org.sglj.util.struct.FenwickTree;
import org.sglj.util.struct.ImmutableFenwickTree;
public class FenwickTreeTest {
static Random RANDOM = new Random();
static class FTSumTester extends FenwickTree<Integer, Integer> {
boolean parentInitialized = true;
public FTSumTester(int size, boolean rightToLeft) {
super(size, rightToLeft);
}
public FTSumTester(Integer[] elements, boolean rightToLeft) {
super(elements, rightToLeft);
}
@Override
public Integer mergeCodomains(Integer a, Integer b) {
return a + b;
}
@Override
public Integer inheritSubCodomain(Integer domain, Integer subdomain) {
return domain - subdomain;
}
@Override
protected Integer createData(Integer element) {
return element != null ? element : 0;
}
@Override
public Integer retrieveQuery(int index) {
int actual = super.retrieveQuery(index);
if (parentInitialized) {
int expected = 0;
if (!isRightToLeft()) {
for (int i = 0; i < index; ++i)
expected += get(i);
} else {
for (int i = index; i < indexCount(); ++i)
expected += get(i);
}
Assert.assertEquals(expected, actual);
}
return actual;
}
}
@Test
public void testSum01() {
FTSumTester ft = new FTSumTester(
// 0 1 2 3 4 5 6 7 8 9
new Integer[]{2, 4, 3, 1, 6, 7, 8, 9, 1, 7}, false);
System.out.println(ft);
for (int i = 0; i < ft.size(); ++i) {
System.out.print("sum [0, " + i + "): ");
System.out.println(ft.retrieveQuery(i));
}
ft.set(4, 2);
System.out.println(ft);
for (int i = 0; i < ft.size(); ++i) {
System.out.print("sum [0, " + i + "): ");
System.out.println(ft.retrieveQuery(i));
}
ft.set(1, 4);
System.out.println(ft);
for (int i = 0; i < ft.size(); ++i) {
System.out.print("sum [0, " + i + "): ");
System.out.println(ft.retrieveQuery(i));
}
}
@Test
public void testRandomSum01() {
for (int i = 1; i < 20; ++i) {
randomMinTest(i, 1000, 100, false);
}
}
@Test
public void testRandomSum02() {
randomMinTest(100, 100, 0x3f3f3f3f, true);
randomMinTest(100, 100, 0x3f3f3f3f, false);
}
static void randomSumTest(final int n, int q, final int maxVal,
boolean rightToLeft) {
Integer[] arr = new Integer[n];
for (int i = 0; i < n; ++i) {
arr[i] = RANDOM.nextInt(maxVal);
}
FTSumTester ft = new FTSumTester(arr, rightToLeft);
System.out.println(ft);
while (q-- > 0) {
if (RANDOM.nextBoolean()) {
int to = RANDOM.nextInt(n + 1);
ft.retrieveQuery(to);
} else {
ft.set(RANDOM.nextInt(n), RANDOM.nextInt());
}
}
}
static class FTMinTester extends ImmutableFenwickTree<Integer, Integer> {
boolean parentInitialized = true;
public FTMinTester(Integer[] elements, boolean rightToLeft) {
super(elements, rightToLeft);
}
@Override
public Integer mergeCodomains(Integer a, Integer b) {
return Math.min(a, b);
}
@Override
protected Integer createData(Integer element) {
return element != null ? element : Integer.MAX_VALUE;
}
@Override
public Integer retrieveQuery(int index) {
int actual = super.retrieveQuery(index);
if (parentInitialized) {
int expected = Integer.MAX_VALUE;
if (!isRightToLeft()) {
for (int i = 0; i < index; ++i)
expected = Math.min(expected, get(i));
} else {
for (int i = index; i < indexCount(); ++i)
expected = Math.min(expected, get(i));
}
Assert.assertEquals(expected, actual);
}
return actual;
}
}
@Test
public void testMin01() {
FTMinTester ft = new FTMinTester(
// 0 1 2 3 4 5 6 7 8 9
new Integer[]{2, 4, 3, 1, 6, 7, 8, 9, 1, 7}, false);
System.out.println(ft);
for (int i = 0; i < ft.size(); ++i) {
System.out.print("min [0, " + i + "): ");
System.out.println(ft.retrieveQuery(i));
}
System.out.println(ft.retrieveQuery(2));
try {
ft.set(0, 5);
Assert.fail();
} catch (UnsupportedOperationException e) {
}
}
@Test
public void testMin02() {
FTMinTester ft = new FTMinTester(
// 0 1 2 3 4 5 6 7
new Integer[]{9, 7, 8, 5, 3, 3, 5, 1}, false);
System.out.println(ft);
for (int i = 0; i < ft.size(); ++i) {
System.out.print("min [0, " + i + "): ");
System.out.println(ft.retrieveQuery(i));
}
}
@Test
public void testRandomMin01() {
for (int i = 1; i < 20; ++i) {
randomMinTest(i, 1000, 100, false);
}
}
@Test
public void testRandomMin02() {
randomMinTest(100, 100, 0x3f3f3f3f, true);
randomMinTest(100, 100, 0x3f3f3f3f, false);
}
static void randomMinTest(final int n, int q, final int maxVal,
boolean rightToLeft) {
Integer[] arr = new Integer[n];
for (int i = 0; i < n; ++i) {
arr[i] = RANDOM.nextInt(maxVal);
}
FTMinTester ft = new FTMinTester(arr, rightToLeft);
System.out.println(ft);
while (q-- > 0) {
int to = RANDOM.nextInt(n + 1);
ft.retrieveQuery(to);
}
}
}