commit 8b57a8f974322b2709fd8155ceefc22c6fd8617e
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 20 00:38:55 2023 +0100

    Jing tablebase. New, less pillowed Jing mesh.

diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyJing.java b/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
index 04d5eacf..49e6a3c4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
@@ -40,9 +40,9 @@ public class TwistyJing extends ShapeTetrahedron
            new Static3D(-SQ6/3, SQ3/3,     0),
          };
 
-  static final float F = 0.48f;  // length of the edge of the corner cubit. Keep<0.5
-                                 // Assuming the length of the edge of the whole
-                                 // tetrahedron is 2.0 (ie standard, equal to numLayers)
+  public static final float F = 0.48f;  // length of the edge of the corner cubit. Keep<0.5
+                                        // Assuming the length of the edge of the whole
+                                        // tetrahedron is 2.0 (ie standard, equal to numLayers)
 
   private int[][] mEdges;
   private int[][] mBasicAngle;
@@ -62,7 +62,7 @@ public class TwistyJing extends ShapeTetrahedron
   @Override
   public float getPillowCoeff()
     {
-    return 1.5f;
+    return 1.25f;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java b/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
index 07054741..eaf3f80a 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
@@ -27,6 +27,7 @@ public enum ImplementedTablebasesList
   DIAM2    (ObjectSignatures.DIAM_2, TBSkewbDiamond.class),
   CUBE2    (ObjectSignatures.CUBE_2, TBCube2.class),
   MIRR2    (ObjectSignatures.MIRR_2, TBCube2.class),
+  JING2    (ObjectSignatures.JING_2, TBJing.class),
   ;
 
   public static final int NUM_OBJECTS = values().length;
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TBJing.java b/src/main/java/org/distorted/objectlib/tablebases/TBJing.java
new file mode 100644
index 00000000..de2ad833
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/tablebases/TBJing.java
@@ -0,0 +1,280 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 static org.distorted.objectlib.main.TwistyObject.SQ2;
+import static org.distorted.objectlib.main.TwistyObject.SQ3;
+import static org.distorted.objectlib.main.TwistyObject.SQ6;
+import static org.distorted.objectlib.objects.TwistyJing.F;
+
+import android.content.res.Resources;
+
+import org.distorted.library.type.Static3D;
+import org.distorted.objectlib.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TBJing extends TablebasesAbstract
+{
+  static final int[][][] QUATS_EDGE = new int[][][]
+      {
+          { { 0,10},{ 5, 3},{ 8, 4},{ 6, 2},{ 7, 1},{ 9,11} },
+          { { 6, 4},{ 0,11},{ 3, 1},{ 5, 7},{ 9,10},{ 2, 8} },
+          { { 7, 3},{ 4, 2},{ 0, 9},{11,10},{ 8, 6},{ 5, 1} },
+          { { 5, 1},{ 6, 8},{11,10},{ 0, 9},{ 2, 4},{ 7, 3} },
+          { { 8, 2},{ 9,10},{ 7, 5},{ 1, 3},{ 0,11},{ 4, 6} },
+          { { 9,11},{ 1, 7},{ 6, 2},{ 8, 4},{ 3, 5},{ 0,10} }
+      };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TBJing()
+    {
+    super();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TBJing(Resources res)
+    {
+    super(res, R.raw.pyra_3_tablebase);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getBasicAngles()
+    {
+    int[] tmp = {3,3};
+    return new int[][] { tmp,tmp,tmp,tmp };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getRotationAxis()
+    {
+    return new Static3D[]
+         {
+           new Static3D(     0,-SQ3/3,-SQ6/3),
+           new Static3D(     0,-SQ3/3, SQ6/3),
+           new Static3D( SQ6/3, SQ3/3,     0),
+           new Static3D(-SQ6/3, SQ3/3,     0),
+         };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[][] getPosition()
+    {
+    return new float[][]
+         {
+           { 0.000f, -SQ2/2, 1.000f },
+           { 0.000f, -SQ2/2,-1.000f },
+           {-1.000f,  SQ2/2, 0.000f },
+           { 1.000f,  SQ2/2, 0.000f },
+
+           { 0.000f, -SQ2/2, 0.000f },
+           {-0.500f, 0.000f, 0.500f },
+           { 0.500f, 0.000f, 0.500f },
+           {-0.500f, 0.000f,-0.500f },
+           { 0.500f, 0.000f,-0.500f },
+           { 0.000f,  SQ2/2, 0.000f },
+
+           { 0.000f,  SQ2/6, 1.0f/3 },
+           { 0.000f,  SQ2/6,-1.0f/3 },
+           {-1.0f/3, -SQ2/6, 0.000f },
+           { 1.0f/3, -SQ2/6, 0.000f },
+         };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[][] getCuts()
+    {
+    float[] cut = { (F-0.5f)*(SQ6/3) };
+    return new float[][] { cut,cut,cut,cut };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean[][] getRotatable()
+    {
+    boolean[] tmp = new boolean[] {false,true};
+    return new boolean[][] { tmp,tmp,tmp,tmp };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// specifically for the tablebase
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getSize()
+    {
+    return 3732480;  // see https://www.jaapsch.net/puzzles/pyraminx.htm
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getMinScramble()
+    {
+    return 9;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void getEdgePermTwist(int[] perm, int[] twist, int[] quats, int index)
+    {
+    for(int i=0; i<6; i++)
+      {
+      int quat = quats[i+index];
+
+      for(int j=0; j<6; j++)
+        {
+        int[] edge = QUATS_EDGE[i][j];
+        if( quat==edge[0] ) { perm[i]=j; twist[i]=0; break; }
+        if( quat==edge[1] ) { perm[i]=j; twist[i]=1; break; }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getVertexTwist(int[] quats)
+    {
+    int twist = 0;
+
+    if( quats[3]==5 ) twist += 1;
+    if( quats[3]==6 ) twist += 2;
+    twist *= 3;
+    if( quats[2]==7 ) twist += 1;
+    if( quats[2]==8 ) twist += 2;
+    twist *= 3;
+    if( quats[1]==1 ) twist += 1;
+    if( quats[1]==2 ) twist += 2;
+    twist *= 3;
+    if( quats[0]==3 ) twist += 1;
+    if( quats[0]==4 ) twist += 2;
+
+    return twist;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getVertexQuats(int twist, int[] output)
+    {
+    switch(twist%3)
+      {
+      case 0: output[0]=0; break;
+      case 1: output[0]=3; break;
+      case 2: output[0]=4; break;
+      }
+    twist /= 3;
+
+    switch(twist%3)
+      {
+      case 0: output[1]=0; break;
+      case 1: output[1]=1; break;
+      case 2: output[1]=2; break;
+      }
+    twist /= 3;
+
+    switch(twist%3)
+      {
+      case 0: output[2]=0; break;
+      case 1: output[2]=7; break;
+      case 2: output[2]=8; break;
+      }
+    twist /= 3;
+
+    switch(twist%3)
+      {
+      case 0: output[3]=0; break;
+      case 1: output[3]=5; break;
+      case 2: output[3]=6; break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getEdgeQuats(int twist, int[] perm, int[] output)
+    {
+    int totalTwist = 0;
+
+    for(int i=0; i<5; i++)
+      {
+      int p = perm[i];
+      int t = (twist%2);
+      twist/=2;
+      output[i+4] = QUATS_EDGE[i][p][t];
+
+      totalTwist += t;
+      }
+
+    int p = perm[5];
+    int t = (totalTwist%2)==0 ? 0:1;
+    output[9] = QUATS_EDGE[5][p][t];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getQuats(int index)
+    {
+    int green_face = (index%4);
+    index /= 4;
+    int vertexTwist = (index%81);
+    index /= 81;
+    int edgeTwist = (index%32);
+    int perm_num = index/32;
+
+    int[] quats = new int[14];
+
+    getVertexQuats(vertexTwist,quats);
+    int[] permutation = new int[6];
+    TablebaseHelpers.getEvenPermutationFromNum(permutation,perm_num);
+    getEdgeQuats(edgeTwist,permutation,quats);
+
+    // we don't need to care about the values of the last 3 quats, but do include them.
+    switch(green_face)
+      {
+      case 0: quats[10] = 0; break;
+      case 1: quats[10] = 5; break;
+      case 2: quats[10] = 4; break;
+      case 3: quats[10] = 3; break;
+      }
+
+    return quats;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIndex(int[] quats)
+    {
+    int vertexTwist = getVertexTwist(quats);
+    int[] permutation = new int[6];
+    int[] twist = new int[6];
+    getEdgePermTwist(permutation,twist,quats,4);
+
+    int edgeTwist = twist[0]+ 2*(twist[1]+ 2*(twist[2]+ 2*(twist[3]+ 2*twist[4])));
+    int perm_num = TablebaseHelpers.computeEvenPermutationNum(permutation);
+
+    int green_face = -1;
+    switch(quats[10])
+      {
+      case  0: case  1: case  2: green_face=0; break;
+      case  5: case  8: case 10: green_face=1; break;
+      case  4: case  7: case 11: green_face=2; break;
+      case  3: case  6: case  9: green_face=3; break;
+      }
+/*
+for(int i=0; i<6; i++) android.util.Log.e("D", "edge perm = "+permutation[i]);
+android.util.Log.e("D", "ret: green_face="+green_face+" vertTwist="+vertexTwist+" edgeTwist="+edgeTwist+" perm: "+perm_num);
+*/
+    return green_face + 4*(vertexTwist + 81*(edgeTwist + 32*perm_num));
+    }
+}  
+
