commit bdcb662f87d13b0bbaac8fbb9ba6c6f4c4e344b8
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Mar 17 16:26:08 2023 +0100

    Progress with TablebasesPruning

diff --git a/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java b/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java
index a936d8ec..10cf601a 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java
@@ -327,7 +327,7 @@ class PruningTable
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  boolean belongs(int number)
+  boolean contains(int number)
     {
     int bucket = (number>>mNumBits);
     int offset1 = getBucketOffset(bucket);
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TBCube2.java b/src/main/java/org/distorted/objectlib/tablebases/TBCube2.java
index c4022594..6208ceef 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TBCube2.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TBCube2.java
@@ -11,56 +11,24 @@ package org.distorted.objectlib.tablebases;
 
 import android.content.res.Resources;
 
-import org.distorted.library.main.QuatHelper;
 import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.R;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class TBCube2 extends TablebasesPruning
 {
-  private static final int[][] P =
-      {
-            { 2,-1,-1},
-            { 2,-1, 1},
-            { 2, 1,-1},
-            { 2, 1, 1},
-            {-2,-1,-1},
-            {-2,-1, 1},
-            {-2, 1,-1},
-            {-2, 1, 1},
-
-            {-1, 2,-1},
-            { 1, 2,-1},
-            {-1, 2, 1},
-            { 1, 2, 1},
-            {-1,-2,-1},
-            { 1,-2,-1},
-            {-1,-2, 1},
-            { 1,-2, 1},
-
-            {-1,-1, 2},
-            { 1,-1, 2},
-            {-1, 1, 2},
-            { 1, 1, 2},
-            {-1,-1,-2},
-            { 1,-1,-2},
-            {-1, 1,-2},
-            { 1, 1,-2},
-      };
-
-  private static final int[][] PT =
-      {
-          {4,0},{5,0},{6,0},{7,0},
-          {0,0},{1,0},{2,0},{3,0},
-          {2,2},{6,1},{3,1},{7,2},
-          {0,1},{4,2},{1,2},{5,1},
-          {1,1},{5,2},{3,2},{7,1},
-          {0,2},{4,1},{2,1},{6,2}
-      };
-
-  private int[][][] mQuatsMap;
+  private final int[][][] mQuatsMap =
+    {
+     { { 0,21,13},  { 1, 6,18},  { 3,17, 7},  { 2,12,22},  {14, 4, 9},  { 5,10,15},  { 8,20,23},  {11,16,19} },
+     { { 3,18, 4},  { 0,22,10},  { 2,13,20},  { 1, 7,16},  { 5,23,21},  {11, 9, 6},  {14,19,17},  { 8,15,12} },
+     { { 1, 9,17},  { 2,15,21},  { 0,23,12},  { 3,19, 6},  { 8,13,10},  {14,18,16},  {11, 7, 4},  { 5,22,20} },
+     { { 2,10,23},  { 3,16, 9},  { 1, 4,19},  { 0,20,15},  {11,17,18},  { 8,21,22},  { 5,12,13},  {14, 6, 7} },
+     { {14, 7, 6},  { 5,13,12},  { 8,22,21},  {11,18,17},  { 0,15,20},  { 1,19, 4},  { 3, 9,16},  { 2,23,10} },
+     { { 5,20,22},  {11, 4, 7},  {14,16,18},  { 8,10,13},  { 3, 6,19},  { 0,12,23},  { 2,21,15},  { 1,17, 9} },
+     { { 8,12,15},  {14,17,19},  {11, 6, 9},  { 5,21,23},  { 1,16, 7},  { 2,20,13},  { 0,10,22},  { 3, 4,18} },
+     { {11,19,16},  { 8,23,20},  { 5,15,10},  {14, 9, 4},  { 2,22,12},  { 3, 7,17},  { 1,18, 6},  { 0,13,21} },
+    };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -167,54 +135,9 @@ public class TBCube2 extends TablebasesPruning
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int[] getPermTwist(float[] point)
-    {
-    float ERR = 0.01f;
-
-    for(int i=0; i<24; i++)
-      {
-      float dx = point[0]-P[i][0];
-      float dy = point[1]-P[i][1];
-      float dz = point[2]-P[i][2];
-
-      if( dx*dx + dy*dy + dz*dz < ERR ) return PT[i];
-      }
-
-    return null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillOutMap(float[] buffer, int[][] map, float[] point, Static4D quat, int quatIndex)
+  boolean moveCanProceed(int lastA, int lastR, int currA, int currR)
     {
-    QuatHelper.rotateVectorByQuat(buffer,point[0],point[1],point[2],1,quat);
-    int[] pt = getPermTwist(buffer);
-    map[pt[0]][pt[1]] = quatIndex;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void computeMap()
-    {
-    final float[][] POINTS =
-        {
-            {-2,-1,-1},
-            {-2,-1, 1},
-            {-2, 1,-1},
-            {-2, 1, 1},
-            { 2,-1,-1},
-            { 2,-1, 1},
-            { 2, 1,-1},
-            { 2, 1, 1},
-        };
-
-    mQuatsMap = new int[8][8][3];
-    Static4D[] quats = getQuats();
-    float[] buffer = new float[4];
-
-    for(int c=0; c<8; c++)
-      for(int q=0; q<24; q++)
-        fillOutMap(buffer,mQuatsMap[c],POINTS[c],quats[q],q);
+    return lastA!=currA;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -303,8 +226,6 @@ public class TBCube2 extends TablebasesPruning
 
   public int[] getQuats(int index)
     {
-    if( mQuatsMap==null ) computeMap();
-
     int twistNum = index%729;
     int permuNum = index/729;
 
@@ -335,8 +256,6 @@ public class TBCube2 extends TablebasesPruning
 
   public int getIndex(int[] quats)
     {
-    if( mQuatsMap==null ) computeMap();
-
     int[] twist = new int[8];
     int[] perm8 = new int[8];
 
@@ -346,6 +265,19 @@ public class TBCube2 extends TablebasesPruning
     int twistNum = twist[0] + 3*(twist[2] + 3*(twist[3] + 3*(twist[4] + 3*(twist[5] + 3*twist[6]))));
     int permNum  = TablebaseHelpers.computePermutationNum(perm7);
 
+if( permNum<0 )
+  {
+  android.util.Log.e("D", " permNum="+permNum );
+
+  StringBuilder sb2 = new StringBuilder();
+  for(int i=0; i<8; i++) { sb2.append(' '); sb2.append(perm8[i]); }
+  android.util.Log.e("D", " perm8="+sb2 );
+
+  StringBuilder sb3 = new StringBuilder();
+  for(int i=0; i<8; i++) { sb3.append(' '); sb3.append(quats[i]); }
+  android.util.Log.e("D", " quats="+sb3 );
+  }
+
     return twistNum + 729*permNum;
     }
 }  
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
index 796dd326..24228070 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
@@ -29,22 +29,22 @@ public abstract class TablebasesAbstract
   private final Static3D[] mAxis;
   private final int mSize, mMinScramble;
   private final int[][] mAngles;
-  private final int mNumAxis;
   private final int[] mNumLayers;
   private final int mNumQuats;
   private final Static4D[] mQuats;
-  private final int[][] mRotRow;
-  private final int mNumCubits;
-  private final float[][] mPosition;
   private final float[][] mCuts;
   private final int[] mNumCuts;
-  private final boolean[][] mRotatable;
-  private final int mScalingFactor;
 
   private int[][] mQuatMult;
   private boolean mInitialized;
 
   Tablebase mTablebase;
+  final int mScalingFactor;
+  final int mNumAxis;
+  final float[][] mPosition;
+  final int[][] mRotRow;
+  final int mNumCubits;
+  final boolean[][] mRotatable;
 
   private static final float[] mTmp = new float[4];
 
@@ -127,7 +127,7 @@ public abstract class TablebasesAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int computeRow(float[] pos, int quat, int axisIndex)
+  int computeRow(float[] pos, int quat, int axisIndex)
     {
     int ret=0;
     int len = pos.length/3;
@@ -205,7 +205,7 @@ public abstract class TablebasesAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int getMultQuat(int index1, int index2)
+  int getMultQuat(int index1, int index2)
     {
     if( mQuatMult==null )
       {
@@ -323,6 +323,25 @@ public abstract class TablebasesAbstract
     return new byte[][] { data };
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void convertMoves(int[][] moves)
+    {
+    for(int[] move : moves )
+      {
+      int axis = move[0];
+      int layer= move[1];
+      int angle= move[2];
+
+      int maxAngle = mAngles[axis][layer];
+      angle = maxAngle-angle;
+      if( angle> 0.5f*maxAngle ) angle -= maxAngle;
+
+      move[1] = (1<<layer);
+      move[2] = angle;
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void addMove(ArrayList<int[]> moves, int axis, int layer, int angle)
@@ -347,7 +366,7 @@ public abstract class TablebasesAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void getNextAxisLayerAngleQuat(int[] data)
+  void getNextAxisLayerAngleQuat(int[] data)
     {
     int axis = data[0];
     int layer= data[1];
@@ -377,13 +396,6 @@ public abstract class TablebasesAbstract
     return moves;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Static4D[] getQuats()
-    {
-    return mQuats;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int[][] solution(int index, int[] extra)
@@ -558,7 +570,7 @@ public abstract class TablebasesAbstract
 
         for(int i=0; i<size; i++)
           {
-          if (table.belongs(i))
+          if (table.contains(i))
             {
             if ((num % 10) == 0) sb.append("\n");
             num++;
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java
index 35e430cb..7c69b293 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java
@@ -19,16 +19,19 @@ import java.io.InputStream;
 
 public abstract class TablebasesPruning extends TablebasesAbstract
 {
+  private final int mGodsNumber;
+
   private boolean mInitialized;
   private PruningTable[] mHighTables;
   private PruningTable[] mMidTables;
-  private int mGodsNumber, mLowestHigh, mHighestMid, mLowestMid;
+  private int mLowestHigh, mHighestMid, mLowestMid;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   abstract int[] getMidPruningLevels();
   abstract int[] getHighPruningLevels();
   abstract int getGodsNumber();
+  abstract boolean moveCanProceed(int lastA, int lastL, int currA, int currL);
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -83,6 +86,8 @@ public abstract class TablebasesPruning extends TablebasesAbstract
 
     for(int i=0; i<mid ; i++) mMidTables[i] = createPruningTable(res,midIDs[i] );
     for(int i=0; i<high; i++) mHighTables[i]= createPruningTable(res,highIDs[i]);
+
+    computeLowHigh();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -118,6 +123,8 @@ public abstract class TablebasesPruning extends TablebasesAbstract
       for(int i=0; i<numHighLevels; i++)
         mHighTables[i] = new PruningTable(mTablebase,highLevels[i]);
 
+      computeLowHigh();
+
       mInitialized = true;
       }
 
@@ -133,8 +140,366 @@ public abstract class TablebasesPruning extends TablebasesAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int[][] solution(int index, int[] extra)
+  private void computeLowHigh()
+    {
+    mLowestHigh = mGodsNumber+1;
+    int numHigh = mHighTables==null ? 0 : mHighTables.length;
+
+    for( int i=0; i<numHigh; i++ )
+      {
+      int level = mHighTables[i].getLevel();
+      if( i==0 || level<mLowestHigh ) mLowestHigh=level;
+      }
+
+    mLowestMid = 0;
+    mHighestMid= 0;
+    int numMid = mMidTables==null ? 0 : mMidTables.length;
+
+    for( int i=0; i<numMid; i++ )
+      {
+      int level = mMidTables[i].getLevel();
+      if( i==0 || level<mLowestMid  ) mLowestMid =level;
+      if( i==0 || level>mHighestMid ) mHighestMid=level;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int traverseDownTable(int index, PruningTable[] tables, int tableIndex, int lastA, int lastR, int[] move, int[] tmpQuats)
+    {
+    int[] quats = getQuats(index);
+    int numQuats = quats.length;
+
+    move[0]=0;
+    move[1]=0;
+    move[2]=1;
+    move[3]=1;
+
+    for(int ax=0; ax<mNumAxis; ax++)
+      for(int cubit=0; cubit<mNumCubits; cubit++)
+        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
+
+    for(int s=0; s<mScalingFactor; s++)
+      {
+      int ax    = move[0];
+      int layer = move[1];
+      int quat  = move[3];
+
+      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
+        {
+        int bitLayer = (1<<layer);
+        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
+
+        for(int cubit=0; cubit<mNumCubits; cubit++)
+          if( mRotRow[cubit][ax]==bitLayer )
+            {
+            int currQuat = tmpQuats[cubit];
+            int newQuat = getMultQuat(quat,currQuat);
+            tmpQuats[cubit] = newQuat;
+            }
+
+        int childIndex = getIndex(tmpQuats);
+
+        if( tableIndex>0 )
+          {
+          if( tables[tableIndex-1].contains(childIndex) ) return childIndex;
+          }
+        else if( !tables[0].contains(childIndex) )
+          {
+          if( tables.length<=1 || !tables[1].contains(childIndex) ) return childIndex;
+          }
+        }
+
+      getNextAxisLayerAngleQuat(move);
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int traversePruningBlock(int[][] moves, PruningTable[] tables, int tableIndex, int index, int lastA, int lastR)
     {
+    int movesIndex = 1;
+    int[] move = moves[movesIndex];
+    int[] tmpQuats = new int[4];
+
+    do
+      {
+      index = traverseDownTable(index,tables,tableIndex,lastA,lastR,move,tmpQuats);
+      lastA = move[0];
+      lastR = move[1];
+      tableIndex--;
+      movesIndex++;
+      move = moves[movesIndex];
+      }
+    while( tableIndex>=0 );
+
+    return index;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// ret: [0][] --> (new index,new depth,?,?) ; [1...N-1][] --> moves. (or null if index not in High)
+
+  private int[][] traverseBlock(int index, PruningTable[] tables, int lastA, int lastR)
+    {
+    int num = (tables==null) ? 0 : tables.length;
+    int tableIndex = -1;
+
+    for( int i=0; i<num; i++ )
+      if( tables[i].contains(index) )
+        {
+        tableIndex = i;
+        break;
+        }
+
+    if( tableIndex<0 ) return null;
+
+    int[][] ret = new int[tableIndex+2][4];
+    ret[0][1] = tables[0].getLevel()-1;
+    ret[0][0] = traversePruningBlock(ret,tables,tableIndex,index,lastA, lastR);
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int midTablesContain(int index)
+    {
+    int num = mMidTables.length;
+
+    for( int i=0; i<num; i++ )
+      if( mMidTables[i].contains(index) )
+        return i;
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean jumpMidZeroRecursive(int index, int jump, int depth, int lastA, int lastR, int[][] solution)
+    {
+    int[] quats  = getQuats(index);
+    int numQuats = quats.length;
+    int[] move   = solution[depth];
+    int[] tmp    = new int[mNumCubits];
+
+    move[0]=0;
+    move[1]=0;
+    move[2]=1;
+    move[3]=1;
+
+    for(int ax=0; ax<mNumAxis; ax++)
+      for(int cubit=0; cubit<mNumCubits; cubit++)
+        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
+
+    for(int s=0; s<mScalingFactor; s++)
+      {
+      int ax    = move[0];
+      int layer = move[1];
+      int quat  = move[3];
+
+      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,ax,layer) )
+        {
+        int bitLayer = (1<<layer);
+        System.arraycopy(quats, 0, tmp, 0, numQuats);
+
+        for(int cubit=0; cubit<mNumCubits; cubit++)
+          if( mRotRow[cubit][ax]==bitLayer )
+            {
+            int currQuat = tmp[cubit];
+            int newQuat = getMultQuat(quat,currQuat);
+            tmp[cubit] = newQuat;
+            }
+
+        int childIndex = getIndex(tmp);
+
+        if( childIndex==0 )
+          {
+          solution[0][0] = childIndex;
+          solution[0][1] = 0;
+          return true;
+          }
+
+        int containingTable = midTablesContain(childIndex);
+
+        if( containingTable>=0 )
+          {
+          solution[0][0] = childIndex;
+          solution[0][1] = mMidTables[containingTable].getLevel();
+          return true;
+          }
+
+        if( jump>1 && jumpMidZeroRecursive(childIndex, jump-1, depth+1, ax, layer, solution) )
+          {
+          return true;
+          }
+        }
+
+      getNextAxisLayerAngleQuat(move);
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// ret: [0][] --> (?,old depth,?,?) ; [1...N-1][] --> moves.
+
+  private int[][] jumpToMidOrZero(int index, int maxJump, int lastA, int lastR)
+    {
+    int[][] solution = new int[maxJump+1][4];
+
+    for(int i=1; i<maxJump; i++)
+      if( jumpMidZeroRecursive(index,i,1,lastA,lastR,solution) )
+        return solution;
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean jumpZeroRecursive(int index, int jump, int depth, int lastA, int lastR, int[][] solution)
+    {
+    int[] quats  = getQuats(index);
+    int numQuats = quats.length;
+    int[] move   = solution[depth];
+    int[] tmp    = new int[mNumCubits];
+
+    android.util.Log.e("D", "trying 0rec: index="+index);
+
+    move[0]=0;
+    move[1]=0;
+    move[2]=1;
+    move[3]=1;
+
+    for(int ax=0; ax<mNumAxis; ax++)
+      for(int cubit=0; cubit<mNumCubits; cubit++)
+        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
+
+    for(int s=0; s<mScalingFactor; s++)
+      {
+      int ax    = move[0];
+      int layer = move[1];
+      int quat  = move[3];
+
+      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
+        {
+        int bitLayer = (1<<layer);
+        System.arraycopy(quats, 0, tmp, 0, numQuats);
+
+        for(int cubit=0; cubit<mNumCubits; cubit++)
+          if( mRotRow[cubit][ax]==bitLayer )
+            {
+            int currQuat = tmp[cubit];
+            int newQuat = getMultQuat(quat,currQuat);
+            tmp[cubit] = newQuat;
+            }
+
+        int childIndex = getIndex(tmp);
+        if( childIndex==0 )
+          {
+          android.util.Log.e("D", "END: ax="+ax+" layer="+layer+" angle: "+move[2]);
+          return true;
+          }
+        if( jump>1 && jumpZeroRecursive(childIndex, jump-1, depth+1, ax, layer, solution) )
+          {
+          android.util.Log.e("D", "REC: ax="+ax+" layer="+layer+" angle: "+move[2]);
+          return true;
+          }
+        }
+
+      getNextAxisLayerAngleQuat(move);
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// ret: [0][] --> (?,old depth,?,?) ; [1...N-1][] --> moves.
+
+  private int[][] jumpToZero(int index, int maxJump, int lastA, int lastR)
+    {
+    int[][] solution = new int[maxJump+1][4];
+
+    for(int i=1; i<maxJump; i++)
+      if( jumpZeroRecursive(index,i,1,lastA,lastR,solution) )
+        return solution;
+
     return null;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[][] concatenateMoves(int[][] high, int[][] jump1, int[][] mid, int[][] jump2)
+    {
+    int len1 = high ==null ? 0 : high.length-1;
+    int len2 = jump1==null ? 0 : jump1[0][1];
+    int len3 = mid  ==null ? 0 : mid.length-1;
+    int len4 = jump2==null ? 0 : jump2[0][1];
+
+    int[][] moves = new int[len1+len2+len3+len4][];
+    int index = 0;
+
+    for(int i=0; i<len1; i++) moves[index++] =  high[i+1];
+    for(int i=0; i<len2; i++) moves[index++] = jump1[i+1];
+    for(int i=0; i<len3; i++) moves[index++] =   mid[i+1];
+    for(int i=0; i<len4; i++) moves[index++] = jump2[i+1];
+
+    convertMoves(moves);
+
+    return moves;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] solution(int index, int[] extra)
+    {
+    if( index==0 ) return null;
+    int lastA=-1, lastR=0;
+    int[][] highMoves = traverseBlock(index,mHighTables,lastA,lastR);
+
+    if( highMoves!=null )
+      {
+      index = highMoves[0][0];
+      int len = highMoves.length;
+      lastA = highMoves[len-1][0];
+      lastR = highMoves[len-1][1];
+      }
+
+    int maxJump = Math.max(mLowestHigh-1-mHighestMid,mLowestMid/2);
+    int[][] jump1Moves = jumpToMidOrZero(index,maxJump,lastA,lastR);
+
+    if( jump1Moves!=null )
+      {
+      if( jump1Moves[0][0]==0 )
+        {
+        return concatenateMoves(null,null,null,jump1Moves);
+        }
+      if( jump1Moves[0][1]==mLowestMid )
+        {
+        int[][] jump2Moves = jumpToZero(index,mLowestMid-1,-1,0);
+        if( jump2Moves==null ) throw new RuntimeException("1 error jumping to 0");
+        return concatenateMoves(null,null,null,jump2Moves);
+        }
+
+      index = jump1Moves[0][0];
+      int len = jump1Moves.length;
+      lastA = jump1Moves[len-1][0];
+      lastR = jump1Moves[len-1][1];
+      }
+
+    int[][] midMoves = traverseBlock(index,mMidTables,lastA,lastR);
+    if( midMoves!=null )
+      {
+      index = midMoves[0][0];
+      int len = midMoves.length;
+      lastA = midMoves[len-1][0];
+      lastR = midMoves[len-1][1];
+      }
+    else throw new RuntimeException("error traversing mid Tables");
+
+    int[][] jump2Moves = jumpToZero(index,mLowestMid-1,lastA,lastR);
+    if( jump2Moves==null ) throw new RuntimeException("2 error jumping to 0");
+    return concatenateMoves(highMoves,jump1Moves,midMoves,jump2Moves);
+    }
 }
