commit f079bf77c2f0d618996be47449d9f15c59ac027c
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Mar 16 01:16:24 2023 +0100

    PruningTables now support numBits=20,24.
    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 71d2a3c4..a936d8ec 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/PruningTable.java
@@ -13,7 +13,9 @@ package org.distorted.objectlib.tablebases;
 
 class PruningTable
 {
-  private static final int HEADER_SIZE = 5;
+  static final int[] SUPPORTED = {4,8,12,16,20,24};
+
+  private static final int HEADER_SIZE = 6;
   private byte[] mData;
   private int mNumBits;
   private int mBucketBytes;
@@ -49,8 +51,9 @@ class PruningTable
     mData[0] = (byte)mLevel;
     mData[1] = (byte)mBucketBytes;
     mData[2] = (byte)mNumBits;
-    mData[3] = (byte)(mNumBuckets/256);
-    mData[4] = (byte)(mNumBuckets%256);
+    mData[3] = (byte)(mNumBuckets>>>16);
+    mData[4] = (byte)(mNumBuckets>>> 8);
+    mData[5] = (byte)(mNumBuckets&0xff);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -102,9 +105,14 @@ class PruningTable
                break;
       case  8: ret = (mData[bytes]&0xff);
                break;
-      case 12: ret = (bits%8)!=0 ? ((mData[bytes]&0xf)*256 + (mData[bytes+1]&0xff)) : ( (mData[bytes]&0xff)*16 + ((mData[bytes+1]>>>4)&0xf));
+      case 12: ret = (bits%8)!=0 ? (((mData[bytes]&0xf)<<8) + (mData[bytes+1]&0xff)) : (((mData[bytes]&0xff)<<4) + ((mData[bytes+1]>>>4)&0xf));
+               break;
+      case 16: ret = ((mData[bytes]&0xff)<<8) + (mData[bytes+1]&0xff);
+               break;
+      case 20: if( (bits%8) !=0 ) ret = (((mData[bytes]&0x0f)<<16) + ((mData[bytes+1]&0xff)<<8) + ( mData[bytes+2]     &0xff));
+               else               ret = (((mData[bytes]&0xff)<<12) + ((mData[bytes+1]&0xff)<<4) + ((mData[bytes+2]>>>4)&0x0f));
                break;
-      case 16: ret = (mData[bytes]&0xff)*256 + (mData[bytes+1]&0xff);
+      case 24: ret = ((mData[bytes]&0xff)<<16) + ((mData[bytes+1]&0xff)<<8) + (mData[bytes+2]&0xff);
                break;
       }
 
@@ -133,11 +141,29 @@ class PruningTable
                else
                  {
                  mData[bytes]  = (byte)(value>>>4);
-                 mData[bytes+1]= (byte)((byte)(value<<4) + (mData[bytes+1]&( 0xf)));
+                 mData[bytes+1]= (byte)((byte)(value<<4) + (mData[bytes+1]&0xf));
                  }
                break;
       case 16: mData[bytes]   = (byte)(value>>>8);
                mData[bytes+1] = (byte)(value&(0xff));
+               break;
+      case 20: if( (bits%8) !=0 )
+                 {
+                 mData[bytes]  = (byte)((mData[bytes]&(~0xf)) + (value>>>16));
+                 mData[bytes+1]= (byte)(value>>>8);
+                 mData[bytes+2]= (byte)(value&(0xff));
+                 }
+               else
+                 {
+                 mData[bytes]  = (byte)(value>>>12);
+                 mData[bytes+1]= (byte)(value>>>4);
+                 mData[bytes+2]= (byte)((byte)(value<<4) + (mData[bytes+2]&0xf));
+                 }
+               break;
+      case 24: mData[bytes]   = (byte)(value>>>16);
+               mData[bytes+1] = (byte)(value>>>8);
+               mData[bytes+2] = (byte)(value&(0xff));
+               break;
       }
     }
 
@@ -240,7 +266,7 @@ class PruningTable
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// only numBits = 4,8,12,16 actually supported
+// only numBits = 4,8,12,16,20,24 actually supported
 
   PruningTable(Tablebase table, int level, int numBits)
     {
@@ -251,16 +277,15 @@ class PruningTable
 
   PruningTable(Tablebase table, int level)
     {
-    int[] supported = {4,8,12,16};
     int minimum = 0, chosen =  0;
     int size = retNumPositions(table,level);
     int tableSize = table.getSize();
 
     android.util.Log.e("D", "----- PRUNING TABLE LEVEL "+level+" ------");
 
-    for(int i=0; i<supported.length; i++)
+    for(int i=0; i<SUPPORTED.length; i++)
       {
-      int approx = computeApproximateSize( tableSize, size, supported[i]);
+      int approx = computeApproximateSize( tableSize, size, SUPPORTED[i]);
 
       if( i==0 || approx<minimum )
         {
@@ -268,10 +293,10 @@ class PruningTable
         chosen  = i;
         }
 
-      android.util.Log.e("D", "trying numBits: "+supported[i]+" approx size: "+approx);
+      android.util.Log.e("D", "trying numBits: "+SUPPORTED[i]+" approx size: "+approx);
       }
 
-    construct(table,level,supported[chosen]);
+    construct(table,level,SUPPORTED[chosen]);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -283,7 +308,7 @@ class PruningTable
     mLevel       = ((int)mData[0])&0xff;
     mBucketBytes = ((int)mData[1])&0xff;
     mNumBits     = ((int)mData[2])&0xff;
-    mNumBuckets  =(((int)mData[3])&0xff)*256 + ((int)mData[4])&0xff;
+    mNumBuckets  =((((int)mData[3])&0xff)<<16) + ((((int)mData[4])&0xff)<<8) + ((int)mData[5])&0xff;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
index 8a646654..796dd326 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
@@ -548,23 +548,26 @@ public abstract class TablebasesAbstract
 
     public void testPruning(int level)
       {
-      PruningTable table = new PruningTable(mTablebase,level);
-      int size = mTablebase.getSize();
+      for( int supp : PruningTable.SUPPORTED )
+        {
+        PruningTable table = new PruningTable(mTablebase, level, supp);
+        int size = mTablebase.getSize();
 
-      StringBuilder sb = new StringBuilder();
-      int num=0;
+        StringBuilder sb = new StringBuilder();
+        int num = 0;
 
-      for(int i=0; i<size; i++)
-        {
-        if( table.belongs(i) )
+        for(int i=0; i<size; i++)
           {
-          if( (num%10)==0 ) sb.append("\n");
-          num++;
-          sb.append(i);
-          sb.append(' ');
+          if (table.belongs(i))
+            {
+            if ((num % 10) == 0) sb.append("\n");
+            num++;
+            sb.append(i);
+            sb.append(' ');
+            }
           }
-        }
 
-      android.util.Log.e("D", "numbers: "+sb);
+        android.util.Log.e("D", "numbers: " + sb);
+        }
       }
 }
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesCube2.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesCube2.java
index 9263e517..768c896e 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesCube2.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesCube2.java
@@ -18,7 +18,7 @@ import org.distorted.objectlib.R;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class TablebasesCube2 extends TablebasesMITM
+public class TablebasesCube2 extends TablebasesPruning
 {
   private static final int[][] P =
       {
@@ -73,7 +73,7 @@ public class TablebasesCube2 extends TablebasesMITM
 
   public TablebasesCube2(Resources res)
     {
-    super(res, new int[] {R.raw.cube_2_pruning5,R.raw.cube_2_pruning11} );
+    super(res, new int[] {R.raw.cube_2_pruning5}, new int[] {R.raw.cube_2_pruning11} );
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -146,9 +146,23 @@ public class TablebasesCube2 extends TablebasesMITM
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int[] getPruningLevels()
+  int[] getMidPruningLevels()
     {
-    return new int[] {5,11};
+    return new int[] {4,5};
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getHighPruningLevels()
+    {
+    return null;//new int[] {11};
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getGodsNumber()
+    {
+    return 11;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesMITM.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesMITM.java
deleted file mode 100644
index 38bde70f..00000000
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesMITM.java
+++ /dev/null
@@ -1,119 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.objectlib.tablebases;
-
-import android.content.res.Resources;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public abstract class TablebasesMITM extends TablebasesAbstract
-{
-  private PruningTable[] mTables;
-  private boolean mInitialized;
-  private int[] mLevels;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  abstract int[] getPruningLevels();
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createPruningTable(Resources res, int id, int index)
-    {
-    InputStream stream = res.openRawResource(id);
-    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-
-    int nRead;
-    byte[] tmp = new byte[16384];
-
-    try
-      {
-      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
-        {
-        buffer.write(tmp, 0, nRead);
-        }
-      stream.close();
-      byte[] data = buffer.toByteArray();
-      buffer.close();
-      mTables[index] = new PruningTable(data);
-      mLevels[index] = mTables[index].getLevel();
-      }
-    catch(IOException ex)
-      {
-      mInitialized = false;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public TablebasesMITM()
-    {
-    super();
-    mInitialized = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public TablebasesMITM(Resources res, int[] resourceIDs)
-    {
-    super();
-
-    int numOfIDs = resourceIDs.length;
-    mTables = new PruningTable[numOfIDs];
-    mLevels = new int[numOfIDs];
-    mInitialized = true;
-
-    for(int i=0; i<numOfIDs; i++) createPruningTable(res,resourceIDs[i],i);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public byte[][] getPacked()
-    {
-    if( !mInitialized )
-      {
-      int[] levels = getPruningLevels();
-      int numLevels= levels.length;
-      int maxLevel = 0;
-
-      for( int l : levels )
-        if( l>maxLevel ) maxLevel = l;
-
-      createTablebase(maxLevel);
-      mTables = new PruningTable[numLevels];
-      mLevels = new int[numLevels];
-
-      for(int i=0; i<numLevels; i++)
-        {
-        mTables[i] = new PruningTable(mTablebase,levels[i]);
-        mLevels[i] = mTables[i].getLevel();
-        }
-
-      mInitialized = true;
-      }
-
-    int num = mTables.length;
-    byte[][] data = new byte[num][];
-    for(int i=0; i<num; i++) data[i] = mTables[i].getPacked();
-
-    return data;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int[][] solution(int index, int[] extra)
-    {
-    return null;
-    }
-}
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java
new file mode 100644
index 00000000..35e430cb
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPruning.java
@@ -0,0 +1,140 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objectlib.tablebases;
+
+import android.content.res.Resources;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public abstract class TablebasesPruning extends TablebasesAbstract
+{
+  private boolean mInitialized;
+  private PruningTable[] mHighTables;
+  private PruningTable[] mMidTables;
+  private int mGodsNumber, mLowestHigh, mHighestMid, mLowestMid;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  abstract int[] getMidPruningLevels();
+  abstract int[] getHighPruningLevels();
+  abstract int getGodsNumber();
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private PruningTable createPruningTable(Resources res, int id)
+    {
+    InputStream stream = res.openRawResource(id);
+    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    int nRead;
+    byte[] tmp = new byte[16384];
+
+    try
+      {
+      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
+        {
+        buffer.write(tmp, 0, nRead);
+        }
+      stream.close();
+      byte[] data = buffer.toByteArray();
+      buffer.close();
+      return new PruningTable(data);
+      }
+    catch(IOException ex)
+      {
+      mInitialized = false;
+      return null;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TablebasesPruning()
+    {
+    super();
+    mGodsNumber = getGodsNumber();
+    mInitialized = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TablebasesPruning(Resources res, int[] midIDs, int[] highIDs)
+    {
+    super();
+
+    int mid = midIDs !=null ? midIDs.length  : 0;
+    int high= highIDs!=null ? highIDs.length : 0;
+
+    mMidTables = mid >0 ? new PruningTable[mid] : null;
+    mHighTables= high>0 ? new PruningTable[high]: null;
+    mGodsNumber = getGodsNumber();
+    mInitialized = true;
+
+    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]);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public byte[][] getPacked()
+    {
+    if( !mInitialized )
+      {
+      int[] midLevels = getMidPruningLevels();
+      int numMidLevels= midLevels!=null ? midLevels.length : 0;
+      int[] highLevels = getHighPruningLevels();
+      int numHighLevels= highLevels!=null ? highLevels.length : 0;
+      int maxLevel = 0;
+
+      if( highLevels!=null )
+        {
+        for( int l : highLevels )
+          if( l>maxLevel ) maxLevel = l;
+        }
+      else
+        {
+        if( midLevels!=null )
+          for( int l : midLevels )
+            if( l>maxLevel ) maxLevel = l;
+        }
+
+      createTablebase(maxLevel);
+      mMidTables = numMidLevels >0 ? new PruningTable[numMidLevels ] : null;
+      mHighTables= numHighLevels>0 ? new PruningTable[numHighLevels] : null;
+
+      for(int i=0; i<numMidLevels; i++)
+        mMidTables[i] = new PruningTable(mTablebase,midLevels[i]);
+      for(int i=0; i<numHighLevels; i++)
+        mHighTables[i] = new PruningTable(mTablebase,highLevels[i]);
+
+      mInitialized = true;
+      }
+
+    int midNum  = mMidTables !=null ? mMidTables.length  : 0;
+    int highNum = mHighTables!=null ? mHighTables.length : 0;
+    byte[][] data = new byte[midNum+highNum][];
+
+    for(int i=0; i<midNum ; i++) data[i       ] = mMidTables[i].getPacked();
+    for(int i=0; i<highNum; i++) data[i+midNum] = mHighTables[i].getPacked();
+
+    return data;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] solution(int index, int[] extra)
+    {
+    return null;
+    }
+}
