package jacobi.core.spatial.sort;

import static org.hamcrest.CoreMatchers.instanceOf;

import java.util.Arrays;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.Assert;
import org.junit.Test;


/**
 * 
 * @author Y.K. Chan
 *
 */
public class HilbertSort3DTest {
	
	@Test
	public void shouldBeAbleToGenerateLowerBasis() {
		IntFunction<String> toStr = i -> IntStream.range(0, 4)
				.map(k -> (i >> 3 * k) % 8)
				.mapToObj(j -> (j / 2 == 0 ? "L" : "U") + (j % 2 == 0 ? "L" : "U"))
				.collect(Collectors.joining(","));
		
		int base = this.curve();
		int[] curves = new int[4];
		for(int i = 0; i < 4; i++) {
			int basis = this.rotate(base, i);
			curves[basis % 8] = basis;			
		}
		
		Assert.assertEquals("LU,LL,UL,UU", toStr.apply(curves[1]));
		Assert.assertEquals("LL,UL,UU,LU", toStr.apply(curves[0]));
		Assert.assertEquals("UL,UU,LU,LL", toStr.apply(curves[2]));
		Assert.assertEquals("UU,LU,LL,UL", toStr.apply(curves[3]));
	}	
	
	@Test
	public void shouldBeAbleToReverseLowerBasis() {
		IntFunction<String> toStr = i -> IntStream.range(0, 4)
				.map(k -> (i >> 3 * k) % 8)
				.mapToObj(j -> (j / 2 == 0 ? "L" : "U") + (j % 2 == 0 ? "L" : "U"))
				.collect(Collectors.joining(","));
		
		int base = this.curve();
		int[] curves = new int[4];
		for(int i = 0; i < 4; i++) {
			int basis = this.rotate(base, i);
			curves[basis % 8] = this.reverse(basis);			
		}
		
		Assert.assertEquals("UU,UL,LL,LU", toStr.apply(curves[1]));
		Assert.assertEquals("LU,UU,UL,LL", toStr.apply(curves[0]));
		Assert.assertEquals("LL,LU,UU,UL", toStr.apply(curves[2]));
		Assert.assertEquals("UL,LL,LU,UU", toStr.apply(curves[3]));
	}
	
	@Test
	public void shouldBeAbleToGenerateAllBasisCurves() {		
		final int prime = this.curve();
		
		final int[] basis = new int[16];
		
		for(int i = 0; i < 4; i++){			
			int elevate = this.rotate(prime, i);
			int start = elevate % 8;
			// elevate stay
			basis[2 * start] = this.duplex(elevate, false);
			// elevate across
			basis[2 * start + 1] = this.duplex(elevate, true);
			
			int demote = this.rotate(prime + (4 + (4 << 3) + (4 << 6) + (4 << 9)), i);
			start = demote % 8;
			// demote stay
			basis[2 * start] = this.duplex(demote, false);
			// demote across
			basis[2 * start + 1] = this.duplex(demote, true);
		}
		
		System.out.println(Arrays.toString(basis));
	}
	
	protected int duplex(int floor, boolean diag) {
		int dir = floor % 8 < 4 ? 1 : -1;
		int next = diag ? this.rotate(floor, 3) : this.reverse(floor);
		return floor + (next + dir * (4 + 4 << 3 + 4 << 6 + 4 << 9) ) << 12;
	}
	
	protected int reverse(int code) {
		int rev = 0;
		for(int i = 0; i < 4; i++) {
			rev += ( (code >> (9 - 3 * i)) % 8 ) << 3 * i ;
		}
		return rev;
	}
	
	protected int rotate(int code, int delta) {
		for(int i = 0; i < delta; i++){
			int mod = code % 8;
			code = (code / 8) + (mod << 9);
		}
		return code;
	}
	

	protected int curve() {
		return 1 + (0 << 3) + (2 << 6) + (3 << 9);
	}
}

@@ -2208,44 +2208,15 @@
 e + 
-(4 + (4 %3C%3C 3) + (4 %3C%3C 6) + (4 %3C%3C 9))
+ELEVATE
 , i)
@@ -2402,51 +2402,166 @@
 %0D%0A%09%09
-System.out.println(Arrays.toString(basis));
+for(int i = 0; i %3C basis.length; i++) %7B%0D%0A%09%09%09String octal = Integer.toOctalString(basis%5Bi%5D);%0D%0A%09%09%09Assert.assertTrue(octal.endsWith(String.valueOf(i / 2)));%0D%0A%09%09%7D
 %0D%0A%09%7D
@@ -2735,16 +2735,17 @@
 floor + 
+(
 (next + 
@@ -2754,46 +2754,23 @@
 r * 
-(4 + 4 %3C%3C 3 + 4 %3C%3C 6 + 4 %3C%3C 9)
+ELEVATE
  ) %3C%3C 12
 ;%0D%0A%09
@@ -2765,16 +2765,17 @@
  ) %3C%3C 12
+)
 ;%0D%0A%09%7D%0D%0A%09
@@ -3182,15 +3182,93 @@
 %3C%3C 9);%0D%0A
-
 %09%7D%0D%0A
+%09%0D%0A%09protected static final int ELEVATE = 4 + (4 %3C%3C 3) + (4 %3C%3C 6) + (4 %3C%3C 9);%0D%0A
 %7D%0D%0A

Check in octal format