commit 1d5819935651d3e30bcfede1650ae403e5d20e2e
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun May 8 23:15:19 2022 +0200

    Introduce ObjectSignature that can incorporate 192-bit signatures (for 5x5x5 bandaged cubes).
    ObjectScrambler does not fully work yet.

diff --git a/src/main/java/org/distorted/objectlib/helpers/ObjectSignature.java b/src/main/java/org/distorted/objectlib/helpers/ObjectSignature.java
new file mode 100644
index 00000000..bfa487a9
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/helpers/ObjectSignature.java
@@ -0,0 +1,476 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objectlib.helpers;
+
+import org.distorted.objectlib.main.ObjectType;
+
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ObjectSignature
+{
+  private long mSignature1, mSignature2, mSignature3;
+  private int[] mLayer;
+  private float[][][] mAllCycles;
+  private float[][] mCubitTouch;
+  private int mNumCubitTouches, mNumCycles;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// built-in objects
+
+  public ObjectSignature(ObjectType type)
+    {
+    mSignature1 = 0;
+    mSignature2 = 0;
+    mSignature3 = type.ordinal();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature(ObjectSignature sig)
+    {
+    mSignature1 = sig.mSignature1;
+    mSignature2 = sig.mSignature2;
+    mSignature3 = sig.mSignature3;
+
+    mLayer      = sig.mLayer;
+    mAllCycles  = sig.mAllCycles;
+    mCubitTouch = sig.mCubitTouch;
+
+    mNumCycles       = sig.mNumCycles;
+    mNumCubitTouches = sig.mNumCubitTouches;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// built-in bandaged 3x3s; objects created from JSON (version1)
+
+  public ObjectSignature(long signature)
+    {
+    mSignature1 = 0;
+    mSignature2 = 0;
+    mSignature3 = signature;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// objects created from JSON (version2)
+
+  public ObjectSignature(long signature1, long signature2, long signature3)
+    {
+    mSignature1 = signature1;
+    mSignature2 = signature2;
+    mSignature3 = signature3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Locally created bandaged cuboids 1<=N,M,K<=5
+// This is the 'Andreas signature' of a bandaged cube.
+// https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
+
+  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
+    {
+    mLayer = new int[] {lenx,leny,lenz};
+
+    prepareCubitTouch();
+    prepareAllCycles();
+
+    for(float[] pos : position)
+      {
+      int numCenters = pos.length/3;
+
+      for(int i=0; i<numCenters; i++)
+        for(int j=i+1; j<numCenters; j++)
+          {
+          float x1 = pos[3*i  ];
+          float y1 = pos[3*i+1];
+          float z1 = pos[3*i+2];
+          float x2 = pos[3*i  ];
+          float y2 = pos[3*i+1];
+          float z2 = pos[3*i+2];
+
+          if(areNeighbours(x1-x2,y1-y2,z1-z2))
+            {
+            float xc = (x1+y1)/2;
+            float yc = (y1+y2)/2;
+            float zc = (z1+z2)/2;
+
+            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
+            setBit(bitIndex,1);
+            }
+          }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setSignature(ObjectSignature sig)
+    {
+    mSignature1 = sig.mSignature1;
+    mSignature2 = sig.mSignature2;
+    mSignature3 = sig.mSignature3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setSignature(int sig)
+    {
+    mSignature1 = 0;
+    mSignature2 = 0;
+    mSignature3 = sig;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean isEqual(ObjectSignature sig)
+    {
+    return mSignature1==sig.mSignature1 && mSignature2==sig.mSignature2 && mSignature3==sig.mSignature3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getLong1()
+    {
+    return mSignature1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getLong2()
+    {
+    return mSignature2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getLong3()
+    {
+    return mSignature3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getString()
+    {
+    return mSignature1+"_"+mSignature2+"_"+mSignature3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean isUnblockedFromLeft(int axis, int layer)
+    {
+    if(layer>0)
+      for(int index=0; index<mNumCubitTouches; index++)
+        if( getBit(index)!=0 )
+          {
+          float[] touch = getCubitTouchOfIndex(index);
+          if( belongsToTheLeft(touch,axis,layer) ) return false;
+          }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature turn(int axis, int layer, int turn)
+    {
+    ObjectSignature ret = new ObjectSignature(this);
+
+    for(int i=0; i<mNumCycles; i++)
+      {
+      float[][] cyc = mAllCycles[i];
+      float[] p0 = cyc[0];
+      float[] p1 = cyc[1];
+
+      if( (belongsToTheLeft(p0,axis,layer) && belongsToTheLeft(p1,axis,layer)) ||
+          (belongsToTheCent(p0,axis,layer) && belongsToTheCent(p1,axis,layer))  )
+        {
+        ret.cycle(turn,cyc);
+        }
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void cycle(int turn, float[][] cyc)
+    {
+    int numPoints = cyc.length;
+
+    if( numPoints==2 )
+      {
+      float[] p0 = cyc[0];
+      float[] p1 = cyc[1];
+
+      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
+      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
+
+      long b0 = getBit(index0);
+      long b1 = getBit(index1);
+
+      setBit(index1,b0);
+      setBit(index0,b1);
+      }
+    else if( numPoints==4 )
+      {
+      float[] p0 = cyc[0];
+      float[] p1 = cyc[1];
+      float[] p2 = cyc[2];
+      float[] p3 = cyc[3];
+
+      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
+      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
+      int index2 = getIndexOfCubitTouch(p2[0],p2[1],p2[2]);
+      int index3 = getIndexOfCubitTouch(p3[0],p3[1],p3[2]);
+
+      long b0 = getBit(index0);
+      long b1 = getBit(index1);
+      long b2 = getBit(index2);
+      long b3 = getBit(index3);
+
+      switch(turn)
+        {
+        case 1: setBit(index0,b3);
+                setBit(index1,b0);
+                setBit(index2,b1);
+                setBit(index3,b2);
+                break;
+        case 2: setBit(index0,b2);
+                setBit(index1,b3);
+                setBit(index2,b0);
+                setBit(index3,b1);
+                break;
+        case 3: setBit(index0,b1);
+                setBit(index1,b2);
+                setBit(index2,b3);
+                setBit(index3,b0);
+                break;
+        }
+      }
+    else
+      {
+      android.util.Log.e("D", "error in cycle, numPoints="+numPoints);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void prepareCubitTouch()
+    {
+    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
+    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
+
+    float[][] centers = new float[numCenters][];
+    int index = 0;
+
+    for(int i = 0; i<mLayer[0]; i++)
+      for(int j = 0; j<mLayer[1]; j++)
+        for(int k = 0; k<mLayer[2]; k++)
+          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
+            {
+            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
+            }
+
+    ArrayList<float[]> mTouch = new ArrayList<>();
+
+    for(int i=0; i<numCenters; i++)
+      for(int j=i+1; j<numCenters; j++)
+        {
+        float[] c0 = centers[i];
+        float[] c1 = centers[j];
+
+        float x1 = c0[0];
+        float y1 = c0[1];
+        float z1 = c0[2];
+        float x2 = c1[0];
+        float y2 = c1[1];
+        float z2 = c1[2];
+
+        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
+          {
+          float xc = (x1+y1)/2;
+          float yc = (y1+y2)/2;
+          float zc = (z1+z2)/2;
+
+          float[] touch = new float[] {xc,yc,zc};
+          mTouch.add(touch);
+          }
+        }
+
+    mNumCubitTouches = mTouch.size();
+    mCubitTouch = new float[mNumCubitTouches][];
+    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void prepareAllCycles()
+    {
+    ArrayList<float[][]> cycles = new ArrayList<>();
+
+    boolean[] longTurn = new boolean[] { mLayer[1]==mLayer[2], mLayer[0]==mLayer[2], mLayer[0]==mLayer[1] };
+
+    for(int i=0; i<mNumCubitTouches; i++)
+      for(int axis=0; axis<3; axis++)
+        {
+        if( longTurn[axis] )
+          {
+          int i0 = rotateIndex(axis,i);
+          if( i0<i ) continue;
+          int i1 = rotateIndex(axis,i0);
+          if( i1<i ) continue;
+          int i2 = rotateIndex(axis,i1);
+          if( i2<i ) continue;
+
+          float[] f0 = getCubitTouchOfIndex(i);
+          float[] f1 = getCubitTouchOfIndex(i0);
+          float[] f2 = getCubitTouchOfIndex(i1);
+          float[] f3 = getCubitTouchOfIndex(i2);
+
+          float[][] cycle = new float[][] { f0,f1,f2,f3 };
+          cycles.add(cycle);
+          }
+        else
+          {
+          int i0 = rotateIndex2(axis,i);
+          if( i0<i ) continue;
+
+          float[] f0 = getCubitTouchOfIndex(i);
+          float[] f1 = getCubitTouchOfIndex(i0);
+
+          float[][] cycle = new float[][] { f0,f1 };
+          cycles.add(cycle);
+          }
+        }
+
+    mNumCycles = cycles.size();
+    mAllCycles = new float[mNumCycles][][];
+    for(int i=0; i<mNumCycles; i++) mAllCycles[i] = cycles.remove(0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int rotateIndex(int axis, int index)
+    {
+    float[] touch = getCubitTouchOfIndex(index);
+
+    switch(axis)
+      {
+      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
+      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
+      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int rotateIndex2(int axis, int index)
+    {
+    float[] touch = getCubitTouchOfIndex(index);
+
+    switch(axis)
+      {
+      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
+      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
+      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean belongsToTheLeft(float[] point, int axis, int layer)
+    {
+    return 2*point[axis]+mLayer[axis] == 2*layer;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean belongsToTheCent(float[] point, int axis, int layer)
+    {
+    return 2*point[axis]+mLayer[axis] == 2*layer+1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getIndexOfCubitTouch(float x, float y, float z)
+    {
+    for(int i=0; i<mNumCubitTouches; i++)
+      {
+      float[] touch = mCubitTouch[i];
+      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] getCubitTouchOfIndex(int index)
+    {
+    return mCubitTouch[index];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean areNeighbours(float dx, float dy, float dz)
+    {
+    return dx*dx+dy*dy+dz*dz<1.1f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private long getBit(int index)
+    {
+    switch(index/64)
+      {
+      case 0: return (mSignature3>>(index%64))&0x1;
+      case 1: return (mSignature2>>(index%64))&0x1;
+      case 2: return (mSignature1>>(index%64))&0x1;
+      default: android.util.Log.e("D", "error in getBit, index="+index);
+      }
+
+    return 0L;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setBit(int index, long bit)
+    {
+    long diff = (1L<<(index%64));
+
+    switch(index/64)
+      {
+      case 0: if( bit!=0 ) mSignature3 |= diff;
+              else         mSignature3 &=~diff;
+              break;
+      case 1: if( bit!=0 ) mSignature2 |= diff;
+              else         mSignature2 &=~diff;
+              break;
+      case 2: if( bit!=0 ) mSignature1 |= diff;
+              else         mSignature1 &=~diff;
+              break;
+      default: android.util.Log.e("D", "error in setBit, index="+index);
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objectlib/json/JsonReader.java b/src/main/java/org/distorted/objectlib/json/JsonReader.java
index 59dc7025..54be8379 100644
--- a/src/main/java/org/distorted/objectlib/json/JsonReader.java
+++ b/src/main/java/org/distorted/objectlib/json/JsonReader.java
@@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets;
 import android.content.Context;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -78,7 +79,7 @@ public class JsonReader
   private boolean mResetMaps;
   private String mTutorialObject;
   private String[][] mTutorials;
-  private long mSignature;
+  private ObjectSignature mSignature;
 
   private static JsonReader mThis;
 
@@ -98,7 +99,7 @@ public class JsonReader
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void parseMetadata(JSONObject object) throws JSONException
+  private void parseMetadata1(JSONObject object) throws JSONException
     {
     mLongName        = object.getString("longname");
     mShortName       = object.getString("shortname");
@@ -112,13 +113,43 @@ public class JsonReader
 
     try
       {
-      mSignature = object.getLong("signature");
+      long signature = object.getLong("signature");
+      mSignature = new ObjectSignature(signature);
       }
     catch(JSONException ex)
       {
       // objects older than Feb 2022 do not have the 'signature' field. They all use the ObjectType.ordinal
       // as their signature.
-      mSignature = ObjectType.getOrdinal(mShortName);
+      long signature = ObjectType.getOrdinal(mShortName);
+      mSignature = new ObjectSignature(signature);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void parseMetadata2(JSONObject object) throws JSONException
+    {
+    mLongName        = object.getString("longname");
+    mShortName       = object.getString("shortname");
+    mInventor        = object.getString("inventor");
+    mYearOfInvention = object.getInt("year");
+    mComplexity      = object.getInt("complexity");
+    mSize            = (float)object.getDouble("size");
+    mNumScrambles    = object.getInt("scrambles");
+    mResetMaps       = object.getBoolean("resetmaps");
+    mNumFaces        = object.getInt("num_faces");
+
+    try
+      {
+      long signature1 = object.getLong("signature1");
+      long signature2 = object.getLong("signature2");
+      long signature3 = object.getLong("signature3");
+      mSignature = new ObjectSignature(signature1,signature2,signature3);
+      }
+    catch(JSONException ex)
+      {
+      long signature = object.getLong("signature");
+      mSignature = new ObjectSignature(signature);
       }
     }
 
@@ -554,7 +585,7 @@ public class JsonReader
   private void parseVersion1(JSONObject object) throws JSONException
     {
     JSONObject metadata    = object.getJSONObject("metadata");
-    parseMetadata(metadata);
+    parseMetadata1(metadata);
     JSONObject mesh        = object.getJSONObject("mesh");
     parseMesh(mesh);
     JSONArray axis         = object.getJSONArray("axis");
@@ -576,7 +607,7 @@ public class JsonReader
   private void parseVersion2(JSONObject object) throws JSONException
     {
     JSONObject metadata    = object.getJSONObject("metadata");
-    parseMetadata(metadata);
+    parseMetadata2(metadata);
     JSONObject mesh        = object.getJSONObject("mesh");
     parseMesh(mesh);
     JSONArray axis         = object.getJSONArray("axis");
@@ -615,10 +646,18 @@ public class JsonReader
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void parseVersion1or2Metadata(JSONObject object) throws JSONException
+  private void parseVersion1Metadata(JSONObject object) throws JSONException
+    {
+    JSONObject metadata = object.getJSONObject("metadata");
+    parseMetadata1(metadata);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void parseVersion2Metadata(JSONObject object) throws JSONException
     {
     JSONObject metadata = object.getJSONObject("metadata");
-    parseMetadata(metadata);
+    parseMetadata2(metadata);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -678,9 +717,13 @@ public class JsonReader
       JSONObject object = new JSONObject(contents.toString());
       int major = object.getInt("major");
 
-      if( major==1 || major==2 )
+      if( major==1 )
+        {
+        parseVersion1Metadata(object);
+        }
+      else if( major==2 )
         {
-        parseVersion1or2Metadata(object);
+        parseVersion2Metadata(object);
         }
       else
         {
@@ -951,7 +994,7 @@ public class JsonReader
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     return mSignature;
     }
diff --git a/src/main/java/org/distorted/objectlib/json/JsonWriter.java b/src/main/java/org/distorted/objectlib/json/JsonWriter.java
index ff0081b4..6780caf6 100644
--- a/src/main/java/org/distorted/objectlib/json/JsonWriter.java
+++ b/src/main/java/org/distorted/objectlib/json/JsonWriter.java
@@ -26,6 +26,7 @@ import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -362,7 +363,12 @@ public class JsonWriter
     metadata.put("shortname"  , object.getShortName() );
     metadata.put("resetmaps"  , object.shouldResetTextureMaps() );
     metadata.put("num_faces"  , object.getNumFaces() );
-    metadata.put("signature"  , object.getSignature() );
+
+    ObjectSignature signature = object.getSignature();
+
+    metadata.put("signature1" , signature.getLong1() );
+    metadata.put("signature2" , signature.getLong2() );
+    metadata.put("signature3" , signature.getLong3() );
 
     return metadata;
     }
diff --git a/src/main/java/org/distorted/objectlib/main/TwistyJson.java b/src/main/java/org/distorted/objectlib/main/TwistyJson.java
index 8ff8b57c..629e919e 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyJson.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyJson.java
@@ -25,6 +25,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.helpers.ObjectSticker;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.json.JsonReader;
@@ -284,7 +285,7 @@ public class TwistyJson extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     return mReader.getSignature();
     }
diff --git a/src/main/java/org/distorted/objectlib/main/TwistyObject.java b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
index 0b6fb9b7..5981fb1d 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyObject.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
@@ -54,6 +54,7 @@ import org.distorted.objectlib.helpers.FactorySticker;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectLibInterface;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.helpers.ObjectSticker;
 import org.distorted.objectlib.helpers.QuatGroupGenerator;
 import org.distorted.objectlib.scrambling.ScrambleState;
@@ -1749,7 +1750,7 @@ public abstract class TwistyObject
   public abstract float getScreenRatio();
   public abstract int getColor(int face);
   public abstract String getShortName();
-  public abstract long getSignature();
+  public abstract ObjectSignature getSignature();
 
   // not only for JSON
   public abstract Static3D[] getRotationAxis();
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyAxis.java b/src/main/java/org/distorted/objectlib/objects/TwistyAxis.java
index 4c7ffd69..06b6fd29 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyAxis.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyAxis.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeHexahedron;
@@ -490,9 +491,9 @@ public class TwistyAxis extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.AXIS_3.ordinal();
+    return new ObjectSignature(ObjectType.AXIS_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandaged2Bar.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandaged2Bar.java
index aa73f575..a5b38fb0 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandaged2Bar.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandaged2Bar.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -79,6 +80,13 @@ public class TwistyBandaged2Bar extends TwistyBandagedAbstract
     return ObjectType.BAN2_3.name();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    return new ObjectSignature(ObjectType.BAN2_3);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public String getObjectName()
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandaged3Plate.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandaged3Plate.java
index c379cf34..d4a527f4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandaged3Plate.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandaged3Plate.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -73,6 +74,13 @@ public class TwistyBandaged3Plate extends TwistyBandagedAbstract
     return ObjectType.BAN3_3.name();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    return new ObjectSignature(ObjectType.BAN3_3);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public String getObjectName()
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
index 90f232f7..c6f08b9a 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
@@ -30,7 +30,8 @@ import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.FactoryBandagedCubit;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
-import org.distorted.objectlib.scrambling.ScrambleStateBandagedCuboid;
+import org.distorted.objectlib.helpers.ObjectSignature;
+import org.distorted.objectlib.scrambling.ScrambleStateBandaged3x3;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.helpers.ObjectShape;
 import org.distorted.objectlib.scrambling.ScrambleState;
@@ -191,14 +192,25 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
     return mSolvedQuatsAbstract;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    long signature = 0;
+    float[][] positions = getPositions();
+    for(float[] pos : positions ) signature = markConnections(signature,pos);
+
+    return new ObjectSignature(signature);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public ScrambleState[] getScrambleStates()
     {
     if( mStates==null && !isInIconMode() )
       {
-      long signature = getSignature();
-      mStates = ScrambleStateBandagedCuboid.computeGraph(signature);
+      ObjectSignature signature = getSignature();
+      mStates = ScrambleStateBandaged3x3.computeGraph(signature.getLong3());
       }
 
     return mStates;
@@ -382,16 +394,6 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
     return signature;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public long getSignature()
-    {
-    long signature = 0;
-    float[][] positions = getPositions();
-    for(float[] pos : positions ) signature = markConnections(signature,pos);
-    return signature;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public ObjectShape getObjectShape(int variant)
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedBiCube.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedBiCube.java
index 19631562..aacf5b5b 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedBiCube.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedBiCube.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -69,6 +70,13 @@ public class TwistyBandagedBiCube extends TwistyBandagedAbstract
     return ObjectType.BAN4_3.name();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    return new ObjectSignature(ObjectType.BAN4_3);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public String getObjectName()
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedFused.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedFused.java
index 1e4d5e9b..0fa63d9b 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedFused.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedFused.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -83,6 +84,13 @@ public class TwistyBandagedFused extends TwistyBandagedAbstract
     return ObjectType.BAN1_3.name();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    return new ObjectSignature(ObjectType.BAN1_3);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public String getObjectName()
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
index bbb37be2..9ae77af4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
@@ -21,6 +21,7 @@ package org.distorted.objectlib.objects;
 
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 
 import java.io.InputStream;
@@ -30,7 +31,7 @@ import java.io.InputStream;
 public class TwistyBandagedGeneric extends TwistyBandagedAbstract
 {
   private static float[][] POS;
-  private static long mSignature;
+  private static ObjectSignature mSignature;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -62,7 +63,7 @@ public class TwistyBandagedGeneric extends TwistyBandagedAbstract
   public static void setPositions(float[][] positions)
     {
     POS = positions;
-    mSignature = -1;
+    mSignature = null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -110,20 +111,22 @@ public class TwistyBandagedGeneric extends TwistyBandagedAbstract
 
   public String getShortName()
     {
-    if( mSignature<0 ) mSignature = getSignature();
-    return ""+mSignature;
+    if( mSignature==null ) mSignature = getSignature();
+    int[] numLayers = getNumLayers();
+    int number = 100*numLayers[0]+10*numLayers[1]+numLayers[0];
+
+    return number+"_"+mSignature.getString();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    if( mSignature<0 )
+    if( mSignature==null )
       {
-      mSignature = 0;
-      for(float[] pos : POS ) mSignature = markConnections(mSignature,pos);
+      int[] numLayers = getNumLayers();
+      mSignature = new ObjectSignature(numLayers[0],numLayers[1],numLayers[2],POS);
       }
-
     return mSignature;
     }
 
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyContainer.java b/src/main/java/org/distorted/objectlib/objects/TwistyContainer.java
index 19380bb2..3f08de5c 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyContainer.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyContainer.java
@@ -25,6 +25,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeHexahedron;
@@ -344,9 +345,9 @@ public class TwistyContainer extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.CONT_2.ordinal();
+    return new ObjectSignature(ObjectType.CONT_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyCrystal.java b/src/main/java/org/distorted/objectlib/objects/TwistyCrystal.java
index 7711bc8b..58ed3423 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyCrystal.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyCrystal.java
@@ -25,6 +25,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -245,9 +246,9 @@ public class TwistyCrystal extends TwistyMinx
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.CRYS_3.ordinal();
+    return new ObjectSignature(ObjectType.CRYS_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java b/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
index 5c463917..71f6b1f6 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlCuboids;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -679,7 +680,7 @@ public class TwistyCuboid extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     int[] numLayers = getNumLayers();
 
@@ -690,22 +691,22 @@ public class TwistyCuboid extends ShapeHexahedron
       {
       case 2: switch(y)
                 {
-                case 2: return ObjectType.CUBE_2.ordinal();
-                case 3: return ObjectType.CU_232.ordinal();
+                case 2: return new ObjectSignature(ObjectType.CUBE_2);
+                case 3: return new ObjectSignature(ObjectType.CU_232);
                 }
       case 3: switch(y)
                 {
-                case 2: return ObjectType.CU_323.ordinal();
-                case 3: return ObjectType.CUBE_3.ordinal();
-                case 4: return ObjectType.CU_343.ordinal();
+                case 2: return new ObjectSignature(ObjectType.CU_323);
+                case 3: return new ObjectSignature(ObjectType.CUBE_3);
+                case 4: return new ObjectSignature(ObjectType.CU_343);
                 }
-      case 4: return ObjectType.CUBE_4.ordinal();
-      case 5: return ObjectType.CUBE_5.ordinal();
-      case 6: return ObjectType.CUBE_6.ordinal();
-      case 7: return ObjectType.CUBE_7.ordinal();
+      case 4: return new ObjectSignature(ObjectType.CUBE_4);
+      case 5: return new ObjectSignature(ObjectType.CUBE_5);
+      case 6: return new ObjectSignature(ObjectType.CUBE_6);
+      case 7: return new ObjectSignature(ObjectType.CUBE_7);
       }
 
-    return ObjectType.CUBE_3.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyDiamond.java b/src/main/java/org/distorted/objectlib/objects/TwistyDiamond.java
index e4f63ddd..20c183e0 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyDiamond.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyDiamond.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlOctahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -504,16 +505,16 @@ public class TwistyDiamond extends ShapeOctahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 2: return ObjectType.DIAM_2.ordinal();
-      case 3: return ObjectType.DIAM_3.ordinal();
-      case 4: return ObjectType.DIAM_4.ordinal();
+      case 2: return new ObjectSignature(ObjectType.DIAM_2);
+      case 3: return new ObjectSignature(ObjectType.DIAM_3);
+      case 4: return new ObjectSignature(ObjectType.DIAM_4);
       }
 
-    return ObjectType.DIAM_2.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java b/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
index f92b178a..4584f412 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.scrambling.ScrambleState;
 
@@ -105,9 +106,9 @@ public class TwistyDino4 extends TwistyDino
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.DIN4_3.ordinal();
+    return new ObjectSignature(ObjectType.DIN4_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyDino6.java b/src/main/java/org/distorted/objectlib/objects/TwistyDino6.java
index c836725d..5cc25768 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyDino6.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyDino6.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.scrambling.ScrambleState;
 
@@ -81,9 +82,9 @@ public class TwistyDino6 extends TwistyDino
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.DINO_3.ordinal();
+    return new ObjectSignature(ObjectType.DINO_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyFisher.java b/src/main/java/org/distorted/objectlib/objects/TwistyFisher.java
index b65ab8ce..9f951492 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyFisher.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyFisher.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeHexahedron;
@@ -412,9 +413,9 @@ public class TwistyFisher extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.FISH_3.ordinal();
+    return new ObjectSignature(ObjectType.FISH_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyHelicopter.java b/src/main/java/org/distorted/objectlib/objects/TwistyHelicopter.java
index 9c5be3bf..a689fc8e 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyHelicopter.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyHelicopter.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -332,9 +333,9 @@ public class TwistyHelicopter extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.HELI_3.ordinal();
+    return new ObjectSignature(ObjectType.HELI_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyIvy.java b/src/main/java/org/distorted/objectlib/objects/TwistyIvy.java
index 54eae00d..8ca4fc81 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyIvy.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyIvy.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -448,9 +449,9 @@ public class TwistyIvy extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.IVY_2.ordinal();
+    return new ObjectSignature(ObjectType.IVY_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyJing.java b/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
index 2111b0d0..cf46f4f4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyJing.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlTetrahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -374,9 +375,9 @@ public class TwistyJing extends ShapeTetrahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.JING_2.ordinal();
+    return new ObjectSignature(ObjectType.JING_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyKilominx.java b/src/main/java/org/distorted/objectlib/objects/TwistyKilominx.java
index 5bebe4bd..87b34be4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyKilominx.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyKilominx.java
@@ -29,6 +29,7 @@ import org.distorted.library.type.Static4D;
 import org.distorted.library.main.QuatHelper;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
 
@@ -571,15 +572,15 @@ public class TwistyKilominx extends TwistyMinx
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 3: return ObjectType.KILO_3.ordinal();
-      case 5: return ObjectType.KILO_5.ordinal();
+      case 3: return new ObjectSignature(ObjectType.KILO_3);
+      case 5: return new ObjectSignature(ObjectType.KILO_5);
       }
 
-    return ObjectType.KILO_3.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -591,7 +592,7 @@ public class TwistyKilominx extends TwistyMinx
       case 3: return "Kilominx";
       case 5: return "Master Kilominx";
       }
-    return "Kilominx";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyMegaminx.java b/src/main/java/org/distorted/objectlib/objects/TwistyMegaminx.java
index da370996..a6446581 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyMegaminx.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyMegaminx.java
@@ -29,6 +29,7 @@ import org.distorted.library.type.Static4D;
 import org.distorted.library.main.QuatHelper;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
 
@@ -492,15 +493,15 @@ public class TwistyMegaminx extends TwistyMinx
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 3: return ObjectType.MEGA_3.ordinal();
-      case 5: return ObjectType.MEGA_5.ordinal();
+      case 3: return new ObjectSignature(ObjectType.MEGA_3);
+      case 5: return new ObjectSignature(ObjectType.MEGA_5);
       }
 
-    return ObjectType.MEGA_3.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyMirror.java b/src/main/java/org/distorted/objectlib/objects/TwistyMirror.java
index c0e755c9..d8d87bd3 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyMirror.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyMirror.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
 import org.distorted.objectlib.scrambling.ScrambleState;
@@ -397,16 +398,16 @@ public class TwistyMirror extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 2: return ObjectType.MIRR_2.ordinal();
-      case 3: return ObjectType.MIRR_3.ordinal();
-      case 4: return ObjectType.MIRR_4.ordinal();
+      case 2: return new ObjectSignature(ObjectType.MIRR_2);
+      case 3: return new ObjectSignature(ObjectType.MIRR_3);
+      case 4: return new ObjectSignature(ObjectType.MIRR_4);
       }
 
-    return ObjectType.MIRR_2.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -419,7 +420,7 @@ public class TwistyMirror extends ShapeHexahedron
       case 3: return "Mirror Cube";
       case 4: return "Master Mirror Blocks";
       }
-    return "Pocket Mirror";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -432,7 +433,7 @@ public class TwistyMirror extends ShapeHexahedron
       case 3: return "Hidetoshi Takeji";
       case 4: return "Traiphum Prungtaengkit";
       }
-    return "Hidetoshi Takeji";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyMorphix.java b/src/main/java/org/distorted/objectlib/objects/TwistyMorphix.java
index 20c6c9b7..c571033e 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyMorphix.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyMorphix.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeTetrahedron;
@@ -301,9 +302,9 @@ public class TwistyMorphix extends ShapeTetrahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.MORP_2.ordinal();
+    return new ObjectSignature(ObjectType.MORP_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyPentultimate.java b/src/main/java/org/distorted/objectlib/objects/TwistyPentultimate.java
index c0b61e91..17120f95 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyPentultimate.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyPentultimate.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.scrambling.ScrambleState;
 
@@ -363,9 +364,9 @@ public class TwistyPentultimate extends TwistyMinx
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.PENT_2.ordinal();
+    return new ObjectSignature(ObjectType.PENT_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objectlib/objects/TwistyPyraminx.java
index a31f66fc..0b472fb1 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyPyraminx.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlTetrahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -386,16 +387,16 @@ public class TwistyPyraminx extends ShapeTetrahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 3: return ObjectType.PYRA_3.ordinal();
-      case 4: return ObjectType.PYRA_4.ordinal();
-      case 5: return ObjectType.PYRA_5.ordinal();
+      case 3: return new ObjectSignature(ObjectType.PYRA_3);
+      case 4: return new ObjectSignature(ObjectType.PYRA_4);
+      case 5: return new ObjectSignature(ObjectType.PYRA_5);
       }
 
-    return ObjectType.PYRA_3.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -408,7 +409,7 @@ public class TwistyPyraminx extends ShapeTetrahedron
       case 4: return "Master Pyraminx";
       case 5: return "Professor's Pyraminx";
       }
-    return "Pyraminx";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -421,7 +422,7 @@ public class TwistyPyraminx extends ShapeTetrahedron
       case 4: return "Katsuhiko Okamoto";
       case 5: return "Timur Evbatyrov";
       }
-    return "Uwe Meffert";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyRedi.java b/src/main/java/org/distorted/objectlib/objects/TwistyRedi.java
index 41a4cfcf..aa0e7599 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyRedi.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyRedi.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -377,9 +378,9 @@ public class TwistyRedi extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.REDI_3.ordinal();
+    return new ObjectSignature(ObjectType.REDI_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyRex.java b/src/main/java/org/distorted/objectlib/objects/TwistyRex.java
index cb311873..be24ef7d 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyRex.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyRex.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -376,9 +377,9 @@ public class TwistyRex extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.REX_3.ordinal();
+    return new ObjectSignature(ObjectType.REX_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistySkewb.java b/src/main/java/org/distorted/objectlib/objects/TwistySkewb.java
index 8918c417..fac99727 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistySkewb.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistySkewb.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -509,15 +510,15 @@ public class TwistySkewb extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     switch(getNumLayers()[0])
       {
-      case 2: return ObjectType.SKEW_2.ordinal();
-      case 3: return ObjectType.SKEW_3.ordinal();
+      case 2: return new ObjectSignature(ObjectType.SKEW_2);
+      case 3: return new ObjectSignature(ObjectType.SKEW_3);
       }
 
-    return ObjectType.SKEW_2.ordinal();
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -529,7 +530,7 @@ public class TwistySkewb extends ShapeHexahedron
       case 2: return "Skewb";
       case 3: return "Master Skewb";
       }
-    return "Skewb";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -541,7 +542,7 @@ public class TwistySkewb extends ShapeHexahedron
       case 2: return "Tony Durham";
       case 3: return "Katsuhiko Okamoto";
       }
-    return "Tony Durham";
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistySquare1.java b/src/main/java/org/distorted/objectlib/objects/TwistySquare1.java
index 6118782c..57450922 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistySquare1.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistySquare1.java
@@ -25,6 +25,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
 import org.distorted.objectlib.scrambling.ScrambleState;
@@ -274,9 +275,9 @@ public class TwistySquare1 extends TwistySquare
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.SQU1_3.ordinal();
+    return new ObjectSignature(ObjectType.SQU1_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistySquare2.java b/src/main/java/org/distorted/objectlib/objects/TwistySquare2.java
index 7d2cbd2c..d0a53e37 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistySquare2.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistySquare2.java
@@ -25,6 +25,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
 import org.distorted.objectlib.scrambling.ScrambleState;
@@ -306,9 +307,9 @@ public class TwistySquare2 extends TwistySquare
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.SQU2_3.ordinal();
+    return new ObjectSignature(ObjectType.SQU2_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyStarminx.java b/src/main/java/org/distorted/objectlib/objects/TwistyStarminx.java
index cedbfdd2..21d7b46a 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyStarminx.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyStarminx.java
@@ -24,6 +24,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.main.ObjectType;
 
 import java.io.InputStream;
@@ -374,9 +375,9 @@ public class TwistyStarminx extends TwistyMinx
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.STAR_3.ordinal();
+    return new ObjectSignature(ObjectType.STAR_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyTrajber.java b/src/main/java/org/distorted/objectlib/objects/TwistyTrajber.java
index a526a607..d1047aae 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyTrajber.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyTrajber.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeOctahedron;
@@ -611,10 +612,17 @@ public class TwistyTrajber extends ShapeOctahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
     int[] numLayers = getNumLayers();
-    return numLayers[0]==3 ? ObjectType.TRAJ_3.ordinal() : ObjectType.TRAJ_4.ordinal();
+
+    switch(numLayers[0])
+      {
+      case 3: return new ObjectSignature(ObjectType.TRAJ_3);
+      case 4: return new ObjectSignature(ObjectType.TRAJ_4);
+      }
+
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -622,7 +630,14 @@ public class TwistyTrajber extends ShapeOctahedron
   public String getObjectName()
     {
     int[] numLayers = getNumLayers();
-    return numLayers[0]==3 ? "Trajber's Octahedron" : "Trajber 4x4";
+
+    switch(numLayers[0])
+      {
+      case 3: return "Trajber's Octahedron";
+      case 4: return "Trajber 4x4";
+      }
+
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -630,7 +645,14 @@ public class TwistyTrajber extends ShapeOctahedron
   public String getInventor()
     {
     int[] numLayers = getNumLayers();
-    return numLayers[0]==3 ? "Josef Trajber" : "Jürgen Brandt";
+
+    switch(numLayers[0])
+      {
+      case 3: return "Josef Trajber";
+      case 4: return "Jürgen Brandt";
+      }
+
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyUltimate.java b/src/main/java/org/distorted/objectlib/objects/TwistyUltimate.java
index e2e18363..4d5e7713 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyUltimate.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyUltimate.java
@@ -28,6 +28,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.touchcontrol.TouchControlDodecahedron;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.ObjectShape;
@@ -378,9 +379,9 @@ public class TwistyUltimate extends ShapeDodecahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.ULTI_2.ordinal();
+    return new ObjectSignature(ObjectType.ULTI_2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyVoid.java b/src/main/java/org/distorted/objectlib/objects/TwistyVoid.java
index b61c9bc5..09fa0c8c 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyVoid.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyVoid.java
@@ -23,6 +23,7 @@ import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeHexahedron;
@@ -414,9 +415,9 @@ public class TwistyVoid extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.VOID_3.ordinal();
+    return new ObjectSignature(ObjectType.VOID_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyWindmill.java b/src/main/java/org/distorted/objectlib/objects/TwistyWindmill.java
index b1fe35c3..3596f9b4 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyWindmill.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyWindmill.java
@@ -26,6 +26,7 @@ import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
 import org.distorted.objectlib.scrambling.ScrambleState;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.ShapeHexahedron;
@@ -424,9 +425,9 @@ public class TwistyWindmill extends ShapeHexahedron
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getSignature()
+  public ObjectSignature getSignature()
     {
-    return ObjectType.WIND_3.ordinal();
+    return new ObjectSignature(ObjectType.WIND_3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java b/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
index fc42691b..920f618c 100644
--- a/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
+++ b/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
@@ -22,6 +22,8 @@ package org.distorted.objectlib.scrambling;
 import java.util.ArrayList;
 import java.util.Random;
 
+import org.distorted.objectlib.helpers.ObjectSignature;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class ObjectScrambler
@@ -52,8 +54,9 @@ public class ObjectScrambler
   private int[][] mQuatMult;
 
   // type=2 , i.e. locally created bandaged cuboids
-  private static long mSignature;
+  private static ObjectSignature mSignature;
   private ArrayList<ScrambleStateBandagedCuboid> mBandagedStates;
+  private int[] mSize, mTurns;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -70,6 +73,19 @@ public class ObjectScrambler
       mCornerQuat = new int[8];
       mLastRot = LAST_SL;
       }
+    if( mType==2 )
+      {
+      mSize = new int[3];
+      mTurns= new int[3];
+
+      mSize[0] = (mNumLayers[0]>1 ? mNumLayers[0] : 0);
+      mSize[1] = (mNumLayers[1]>1 ? mNumLayers[1] : 0);
+      mSize[2] = (mNumLayers[2]>1 ? mNumLayers[2] : 0);
+
+      mTurns[0] = mNumLayers[1]==mNumLayers[2] ? 3:1;
+      mTurns[1] = mNumLayers[0]==mNumLayers[2] ? 3:1;
+      mTurns[2] = mNumLayers[0]==mNumLayers[1] ? 3:1;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -405,6 +421,39 @@ public class ObjectScrambler
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TYPE 2
+
+  private void fillOutScramble(int[] scramble, int moveIndex)
+    {
+    for(int axis=0; axis<3; axis++)
+      {
+      int size = mTurns[axis]*mSize[axis];
+
+      if( moveIndex<size )
+        {
+        scramble[0] = axis;
+        scramble[1] = moveIndex/mTurns[axis];
+
+        if( mTurns[axis]==3 )
+          {
+          switch(moveIndex%3)
+            {
+            case 0: scramble[2] = -1; break;
+            case 1: scramble[2] =  2; break;
+            case 2: scramble[2] =  1; break;
+            }
+          }
+        else scramble[2] =  1;
+        return;
+        }
+
+      moveIndex -= size;
+      }
+
+    android.util.Log.e("D", "ERROR in fillOutScramble moveIndex="+moveIndex);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // TYPE 2
 
@@ -431,19 +480,10 @@ public class ObjectScrambler
       int randMove = rnd.nextInt(numMoves);
       int moveIndex = currState.getNthMove(randMove,indexExcluded);
       mSignature = currState.getMove(moveIndex);
-
-      scramble[curr][0] = moveIndex/9;
-      scramble[curr][1] = (moveIndex%9)/3;
-
-      switch(moveIndex%3)
-        {
-        case 0: scramble[curr][2] = -1; break;
-        case 1: scramble[curr][2] =  2; break;
-        case 2: scramble[curr][2] =  1; break;
-        }
+      fillOutScramble(scramble[curr],moveIndex);
       }
 
-    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mSignature);
+    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], mSignature);
     mBandagedStates.add(nextState);
     }
 
@@ -462,7 +502,7 @@ public class ObjectScrambler
         {
         mBandagedStates.remove(curr);
         ScrambleStateBandagedCuboid prevState = mBandagedStates.get(curr-1);
-        long signature = currState.getID();
+        ObjectSignature signature = currState.getSignature();
         prevState.removeMoves(signature);
         boolean result = buildMove(scramble,rnd,curr-1);
         if( !result ) return false;
@@ -479,18 +519,9 @@ public class ObjectScrambler
     int randMove = rnd.nextInt(numMoves);
     int moveIndex = currState.getNthMove(randMove,indexExcluded);
     mSignature = currState.getMove(moveIndex);
+    fillOutScramble(scramble[curr],moveIndex);
 
-    scramble[curr][0] = moveIndex/9;
-    scramble[curr][1] = (moveIndex%9)/3;
-
-    switch(moveIndex%3)
-      {
-      case 0: scramble[curr][2] = -1; break;
-      case 1: scramble[curr][2] =  2; break;
-      case 2: scramble[curr][2] =  1; break;
-      }
-
-    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mSignature);
+    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], mSignature);
     mBandagedStates.add(nextState);
 
     return true;
@@ -504,7 +535,7 @@ public class ObjectScrambler
     if( mBandagedStates==null ) mBandagedStates = new ArrayList<>();
     else                        mBandagedStates.clear();
 
-    ScrambleStateBandagedCuboid state = new ScrambleStateBandagedCuboid(mSignature);
+    ScrambleStateBandagedCuboid state = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], mSignature);
     mBandagedStates.add(state);
     boolean success = true;
 
@@ -521,7 +552,7 @@ public class ObjectScrambler
     if( !success )
       {
       mBandagedStates.clear();
-      state = new ScrambleStateBandagedCuboid(mSignature);
+      state = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], mSignature);
       mBandagedStates.add(state);
 
       for(int curr=0; curr<total; curr++)
@@ -529,12 +560,17 @@ public class ObjectScrambler
         buildMoveForced(scramble,rnd,curr);
         }
       }
+
+for(int i=0; i<total; i++)
+  {
+  android.util.Log.e("D", "scramble "+i+" axis: "+scramble[i][0]+" layer="+scramble[i][1]+" angle="+scramble[i][2]);
+  }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // TYPE 2
 
-  public static void setSignature(long signature)
+  public static void setSignature(ObjectSignature signature)
     {
     mSignature = signature;
     }
diff --git a/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandaged3x3.java b/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandaged3x3.java
new file mode 100644
index 00000000..4c9786c2
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandaged3x3.java
@@ -0,0 +1,724 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objectlib.scrambling;
+
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Producer of the ScrambleStateGraph for any bandaged 3x3x3.
+
+public class ScrambleStateBandaged3x3
+{
+  public static final int AXIS_X = 0;
+  public static final int AXIS_Y = 1;
+  public static final int AXIS_Z = 2;
+
+  private static final long INVALID_MOVE = -1;
+  private static final int NUM_MOVES = 27;
+
+  private static final int LAYER_L = 0;
+  private static final int LAYER_M = 1;
+  private static final int LAYER_R = 2;
+
+  private long mID;
+  private int mDistance;
+  private final long[] mMoves;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ScrambleStateBandaged3x3(long id)
+    {
+    mDistance = -1;
+    mID = id;
+    mMoves = createMoves(mID);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getID()
+    {
+    return mID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getMove(int index)
+    {
+    return (index>=0 && index<NUM_MOVES) ? mMoves[index] : INVALID_MOVE;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int numAxis()
+    {
+    int num = 0;
+
+    if( mMoves[ 0]!=INVALID_MOVE || mMoves[ 1]!=INVALID_MOVE || mMoves[ 2]!=INVALID_MOVE ||
+        mMoves[ 3]!=INVALID_MOVE || mMoves[ 4]!=INVALID_MOVE || mMoves[ 5]!=INVALID_MOVE ||
+        mMoves[ 6]!=INVALID_MOVE || mMoves[ 7]!=INVALID_MOVE || mMoves[ 8]!=INVALID_MOVE   ) num++;
+
+    if( mMoves[ 9]!=INVALID_MOVE || mMoves[10]!=INVALID_MOVE || mMoves[11]!=INVALID_MOVE ||
+        mMoves[12]!=INVALID_MOVE || mMoves[13]!=INVALID_MOVE || mMoves[14]!=INVALID_MOVE ||
+        mMoves[15]!=INVALID_MOVE || mMoves[16]!=INVALID_MOVE || mMoves[17]!=INVALID_MOVE   ) num++;
+
+    if( mMoves[18]!=INVALID_MOVE || mMoves[19]!=INVALID_MOVE || mMoves[20]!=INVALID_MOVE ||
+        mMoves[21]!=INVALID_MOVE || mMoves[22]!=INVALID_MOVE || mMoves[23]!=INVALID_MOVE ||
+        mMoves[24]!=INVALID_MOVE || mMoves[25]!=INVALID_MOVE || mMoves[26]!=INVALID_MOVE   ) num++;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numXMoves()
+    {
+    int num=0;
+
+    if( mMoves[ 0]!=INVALID_MOVE ) num++;
+    if( mMoves[ 1]!=INVALID_MOVE ) num++;
+    if( mMoves[ 2]!=INVALID_MOVE ) num++;
+    if( mMoves[ 3]!=INVALID_MOVE ) num++;
+    if( mMoves[ 4]!=INVALID_MOVE ) num++;
+    if( mMoves[ 5]!=INVALID_MOVE ) num++;
+    if( mMoves[ 6]!=INVALID_MOVE ) num++;
+    if( mMoves[ 7]!=INVALID_MOVE ) num++;
+    if( mMoves[ 8]!=INVALID_MOVE ) num++;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numYMoves()
+    {
+    int num=0;
+
+    if( mMoves[ 9]!=INVALID_MOVE ) num++;
+    if( mMoves[10]!=INVALID_MOVE ) num++;
+    if( mMoves[11]!=INVALID_MOVE ) num++;
+    if( mMoves[12]!=INVALID_MOVE ) num++;
+    if( mMoves[13]!=INVALID_MOVE ) num++;
+    if( mMoves[14]!=INVALID_MOVE ) num++;
+    if( mMoves[15]!=INVALID_MOVE ) num++;
+    if( mMoves[16]!=INVALID_MOVE ) num++;
+    if( mMoves[17]!=INVALID_MOVE ) num++;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numZMoves()
+    {
+    int num=0;
+
+    if( mMoves[18]!=INVALID_MOVE ) num++;
+    if( mMoves[19]!=INVALID_MOVE ) num++;
+    if( mMoves[20]!=INVALID_MOVE ) num++;
+    if( mMoves[21]!=INVALID_MOVE ) num++;
+    if( mMoves[22]!=INVALID_MOVE ) num++;
+    if( mMoves[23]!=INVALID_MOVE ) num++;
+    if( mMoves[24]!=INVALID_MOVE ) num++;
+    if( mMoves[25]!=INVALID_MOVE ) num++;
+    if( mMoves[26]!=INVALID_MOVE ) num++;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int numMoves()
+    {
+    return numXMoves()+numYMoves()+numZMoves();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int numMoves(int excludedAxis)
+    {
+    switch(excludedAxis)
+      {
+      case AXIS_X: return numYMoves()+numZMoves();
+      case AXIS_Y: return numXMoves()+numZMoves();
+      case AXIS_Z: return numXMoves()+numYMoves();
+      }
+
+    int ret= numXMoves()+numYMoves()+numZMoves();
+
+    //android.util.Log.e("D", "numMoves returning "+ret+" "+formatMoves() );
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getNthMove(int n, int excludedAxis)
+    {
+    int num = 0;
+
+    for(int m=0; m<NUM_MOVES; m++)
+      {
+      if( (m/9)!=excludedAxis && mMoves[m]!=INVALID_MOVE)
+        {
+        if( num==n ) return m;
+        num++;
+        }
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void removeMoves(long signature)
+    {
+    for(int m=0; m<NUM_MOVES; m++)
+      if( signature==mMoves[m] ) mMoves[m]=INVALID_MOVE;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String formatMoves()
+    {
+    String x = getTable( 0);
+    String y = getTable( 9);
+    String z = getTable(18);
+
+    return mID+"\n"+x+"\n"+y+"\n"+z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String getTable(int index)
+    {
+    long m0 = getMove(index  );
+    long m1 = getMove(index+1);
+    long m2 = getMove(index+2);
+    long m3 = getMove(index+3);
+    long m4 = getMove(index+4);
+    long m5 = getMove(index+5);
+    long m6 = getMove(index+6);
+    long m7 = getMove(index+7);
+    long m8 = getMove(index+8);
+
+    String ret = "";
+
+    if( m0!=INVALID_MOVE ) ret += formatRet(ret,0,-1,m0);
+    if( m1!=INVALID_MOVE ) ret += formatRet(ret,0, 2,m1);
+    if( m2!=INVALID_MOVE ) ret += formatRet(ret,0, 1,m2);
+    if( m3!=INVALID_MOVE ) ret += formatRet(ret,1,-1,m3);
+    if( m4!=INVALID_MOVE ) ret += formatRet(ret,1, 2,m4);
+    if( m5!=INVALID_MOVE ) ret += formatRet(ret,1, 1,m5);
+    if( m6!=INVALID_MOVE ) ret += formatRet(ret,2,-1,m6);
+    if( m7!=INVALID_MOVE ) ret += formatRet(ret,2, 2,m7);
+    if( m8!=INVALID_MOVE ) ret += formatRet(ret,2, 1,m8);
+
+    return formatL("{" + ret + "}");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] getMoves(int index)
+    {
+    int numValid = 0;
+    for(int i=index; i<index+9; i++) if( mMoves[i]!=INVALID_MOVE ) numValid++;
+
+    int[] ret = new int[3*numValid];
+
+    long m0 = getMove(index  );
+    long m1 = getMove(index+1);
+    long m2 = getMove(index+2);
+    long m3 = getMove(index+3);
+    long m4 = getMove(index+4);
+    long m5 = getMove(index+5);
+    long m6 = getMove(index+6);
+    long m7 = getMove(index+7);
+    long m8 = getMove(index+8);
+
+    int pointer=0;
+
+    if( m0!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]=-1; ret[pointer+2]= (int)m0; pointer+=3; }
+    if( m1!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 2; ret[pointer+2]= (int)m1; pointer+=3; }
+    if( m2!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 1; ret[pointer+2]= (int)m2; pointer+=3; }
+    if( m3!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]=-1; ret[pointer+2]= (int)m3; pointer+=3; }
+    if( m4!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 2; ret[pointer+2]= (int)m4; pointer+=3; }
+    if( m5!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 1; ret[pointer+2]= (int)m5; pointer+=3; }
+    if( m6!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]=-1; ret[pointer+2]= (int)m6; pointer+=3; }
+    if( m7!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 2; ret[pointer+2]= (int)m7; pointer+=3; }
+    if( m8!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 1; ret[pointer+2]= (int)m8; pointer+=3; }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private ScrambleState produceScrambleState()
+    {
+    int[] xMoves = getMoves(0);
+    int[] yMoves = getMoves(9);
+    int[] zMoves = getMoves(18);
+
+    int[][] moves = { xMoves,yMoves,zMoves };
+
+    return new ScrambleState( moves );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// STATIC STUFF
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static ScrambleState[] computeGraph(long id)
+    {
+    ScrambleStateBandaged3x3 bsg = new ScrambleStateBandaged3x3(id);
+    Map<Long, ScrambleStateBandaged3x3> graph = new LinkedHashMap<>();
+    graph.put(id,bsg);
+
+    insertChildren(graph,id);
+    // if there's only one state, do not prune moves which point to itself
+    if(graph.size()>1) pruneGraph(graph,id);
+    computeDistance(graph,id,0);
+    removeDisconnectedParts(graph);
+    remapGraph(graph);
+
+    int num = graph.size();
+    ScrambleState[] ret = new ScrambleState[num];
+
+    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : graph.entrySet())
+      {
+      ScrambleStateBandaged3x3 value = entry.getValue();
+      int mid = (int)value.mID;
+      ret[mid] = value.produceScrambleState();
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void insertChildren(Map<Long, ScrambleStateBandaged3x3> map, long id)
+    {
+    ScrambleStateBandaged3x3 bsg = findState(map,id);
+
+    if( bsg==null )
+      {
+      android.util.Log.e("D", "error: "+id+" doesn't exist");
+      return;
+      }
+
+    for(int i=0; i<NUM_MOVES; i++)
+      {
+      long move = bsg.getMove(i);
+
+      if( move!=INVALID_MOVE )
+        {
+        ScrambleStateBandaged3x3 tmp = findState(map,move);
+
+        if( tmp==null )
+          {
+          tmp = new ScrambleStateBandaged3x3(move);
+          map.put(move,tmp);
+          insertChildren(map,move);
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void pruneGraph(Map<Long, ScrambleStateBandaged3x3> map, long startingID)
+    {
+    boolean pruned = false;
+    boolean startingIsSingle = false;
+
+    Iterator<Map.Entry<Long, ScrambleStateBandaged3x3>> it = map.entrySet().iterator();
+
+    while (it.hasNext())
+      {
+      Map.Entry<Long, ScrambleStateBandaged3x3> entry = it.next();
+      ScrambleStateBandaged3x3 value = entry.getValue();
+
+      if( value.numAxis()<2 )
+        {
+        long prunedID = value.getID();
+
+        if( prunedID!=startingID ) // do not remove the starting point, even if it does have only 1 axis
+          {
+          it.remove();
+          pruned = true;
+          }
+        else
+          {
+          startingIsSingle = true;
+          }
+        }
+      }
+
+    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet() )
+      {
+      ScrambleStateBandaged3x3 value = entry.getValue();
+
+      for(int m=0; m<NUM_MOVES; m++)
+        {
+        long move = value.mMoves[m];
+        ScrambleStateBandaged3x3 tmp = findState(map,move);
+
+        if( tmp==null || (startingIsSingle && move==startingID) )
+          {
+          value.mMoves[m]=INVALID_MOVE;
+          }
+        }
+      }
+
+    if( pruned ) pruneGraph(map,startingID);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void remapGraph(Map<Long, ScrambleStateBandaged3x3> map)
+    {
+    int id=0;
+
+    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
+      {
+      ScrambleStateBandaged3x3 value = entry.getValue();
+      value.mID = id++;
+      }
+
+    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
+      {
+      ScrambleStateBandaged3x3 value = entry.getValue();
+
+      for(int m=0; m<NUM_MOVES; m++)
+        {
+        long move = value.mMoves[m];
+
+        if( move!=INVALID_MOVE )
+          {
+          ScrambleStateBandaged3x3 tmp = map.get(move);
+          if( tmp!=null ) value.mMoves[m] = tmp.mID;
+          else            android.util.Log.e("D", "ERROR in remapGraph");
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void computeDistance(Map<Long, ScrambleStateBandaged3x3> map, long id, int distance)
+    {
+    ScrambleStateBandaged3x3 state = findState(map,id);
+
+    if( state==null )
+      {
+      android.util.Log.e("D", "error: "+id+" doesn't exist");
+      return;
+      }
+
+    if( state.mDistance<0 )
+      {
+      state.mDistance = distance;
+
+      for(int i=0; i<NUM_MOVES; i++)
+        {
+        long move = state.getMove(i);
+
+        if( move!=INVALID_MOVE )
+          {
+          computeDistance(map,move,distance+1);
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void removeDisconnectedParts(Map<Long, ScrambleStateBandaged3x3> map)
+    {
+    Iterator<Map.Entry<Long, ScrambleStateBandaged3x3>> it = map.entrySet().iterator();
+
+    while (it.hasNext())
+      {
+      Map.Entry<Long, ScrambleStateBandaged3x3> entry = it.next();
+      ScrambleStateBandaged3x3 value = entry.getValue();
+      if( value.mDistance<0 ) it.remove();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static ScrambleStateBandaged3x3 findState(Map<Long, ScrambleStateBandaged3x3> map, long id)
+    {
+    return map.get(id);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String formatRet(String str, int row, int angle, long id)
+    {
+    String ret = str.length()!=0 ? ",":"";
+
+    ret += row;
+    ret += angle<0 ? "," : ", ";
+    ret += angle;
+
+         if( id< 10 ) ret += (",  "+id);
+    else if( id<100 ) ret += (", " +id);
+    else              ret += (","  +id);
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static final int LENGTH = 28;
+
+  private static String formatL(String input)
+    {
+    int len = input.length();
+    String ret = input;
+    for(int i=0 ;i<LENGTH-len; i++) ret += " ";
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static long[] createMoves(long id)
+    {
+    long[] ret = new long[NUM_MOVES];
+    int index = 0;
+
+    for(int axis=0; axis<3; axis++)
+      for(int layer=0; layer<3; layer++)
+        {
+        long x1 = turn(id,axis,layer);
+
+        if( x1!=INVALID_MOVE )
+          {
+          long x2 = turn(x1,axis,layer);
+          long x3 = turn(x2,axis,layer);
+
+          ret[index  ] = x1;
+          ret[index+1] = x2;
+          ret[index+2] = x3;
+          }
+        else
+          {
+          ret[index  ] = INVALID_MOVE;
+          ret[index+1] = INVALID_MOVE;
+          ret[index+2] = INVALID_MOVE;
+          }
+
+        index+=3;
+        }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Definition of the id: it's an 'Andreas signature' of a bandaged cube.
+// https://twistypuzzles.com/forum/viewtopic.php?t=24452&sid=f3b4ac0c611c4713e4d7840f1aabbb0b&start=350
+
+  private static long turn(long id, int axis, int layer)
+    {
+    switch(axis)
+      {
+      case AXIS_X: switch(layer)
+                     {
+                     case LAYER_L: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
+                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
+                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1  ) return INVALID_MOVE;
+
+                                   long x1 = cycle(id,9,14,46,41);
+                                   long x2 = cycle(x1,4,35,51,20);
+                                   return    cycle(x2,17,25,38,30);
+
+                     case LAYER_M: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
+                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
+                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1 ||
+                                       getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
+                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
+                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
+
+                                   long x4 = cycle(id,8,13,45,40);
+                                   long x5 = cycle(x4,3,34,50,19);
+                                   return    cycle(x5,16,24,37,29);
+
+                     case LAYER_R: if( getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
+                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
+                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
+
+                                   long x7 = cycle(id,7,12,44,39);
+                                   long x8 = cycle(x7,2,33,49,18);
+                                   return    cycle(x8,15,23,36,28);
+                     }
+
+      case AXIS_Y: switch(layer)
+                     {
+                     case LAYER_L: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
+                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
+                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1  ) return INVALID_MOVE;
+
+                                   long y1 = cycle(id,1,9,10,2);
+                                   long y2 = cycle(y1,0,4,11,7);
+                                   return    cycle(y2,3,6,8,5);
+
+                     case LAYER_M: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
+                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
+                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1 ||
+                                       getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
+                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
+                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
+
+                                   long y4 = cycle(id,21,25,32,28);
+                                   long y5 = cycle(y4,22,30,31,23);
+                                   return    cycle(y5,24,27,29,26);
+
+                     case LAYER_R: if( getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
+                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
+                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
+
+                                   long y7 = cycle(id,42,46,53,49);
+                                   long y8 = cycle(y7,43,51,52,44);
+                                   return    cycle(y8,45,48,50,47);
+                     }
+
+      case AXIS_Z: switch(layer)
+                     {
+                     case LAYER_L: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
+                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
+                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1  ) return INVALID_MOVE;
+
+                                   long z1 = cycle(id,10,20,53,39);
+                                   long z2 = cycle(z1,11,41,52,18);
+                                   return    cycle(z2,19,32,40,31);
+
+                     case LAYER_M: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
+                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
+                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1 ||
+                                       getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
+                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
+                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
+
+                                   long z4 = cycle(id,5,17,48,36);
+                                   long z5 = cycle(z4,6,38,47,15);
+                                   return    cycle(z5,16,27,37,26);
+
+                     case LAYER_R: if( getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
+                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
+                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
+
+                                   long z7 = cycle(id,0,14,43,33);
+                                   long z8 = cycle(z7,1,35,42,12);
+                                   return    cycle(z8,13,22,34,21);
+                     }
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// bit b1 in place of b2 etc.
+
+  private static long cycle(long id, int b1, int b2, int b3, int b4)
+    {
+    long bit1 = getBit(id,b1);
+    long bit2 = getBit(id,b2);
+    long bit3 = getBit(id,b3);
+    long bit4 = getBit(id,b4);
+
+    long i1 = setBit(id,b2,bit1);
+    long i2 = setBit(i1,b3,bit2);
+    long i3 = setBit(i2,b4,bit3);
+    return    setBit(i3,b1,bit4);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static long getBit(long id, int bit)
+    {
+    return (id>>bit)&0x1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static long setBit(long id, int bit, long value)
+    {
+    long old = getBit(id,bit);
+
+    if( old!=value )
+      {
+      long diff = (1L<<bit);
+      id += (value==0 ? -diff : diff);
+      }
+
+    return id;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void printMoves()
+    {
+    String moves = "";
+
+    for(int i=0; i<NUM_MOVES; i++)
+      {
+      moves += (" " + mMoves[i]);
+      }
+
+    android.util.Log.e("D", moves);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String printBits(long id)
+    {
+    String ret = "[";
+    boolean first = true;
+
+    for(int i=0; i<64; i++)
+      {
+      if( ( (id>>i)&0x1)==1 )
+        {
+        String num = (i<10 ? " "+i : ""+i);
+
+        if( first ) { ret += num; first=false; }
+        else          ret += (","+num);
+        }
+      }
+
+    return ret + "]";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void printGraph(Map<Long, ScrambleStateBandaged3x3> map)
+    {
+    int num = map.size();
+    android.util.Log.e("D", "\n"+num+" states\n");
+
+    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
+      {
+      ScrambleStateBandaged3x3 value = entry.getValue();
+      android.util.Log.e("D", value.formatMoves());
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandagedCuboid.java b/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandagedCuboid.java
index a2c2cc8c..2b343db3 100644
--- a/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandagedCuboid.java
+++ b/src/main/java/org/distorted/objectlib/scrambling/ScrambleStateBandagedCuboid.java
@@ -19,59 +19,84 @@
 
 package org.distorted.objectlib.scrambling;
 
-import java.util.LinkedHashMap;
-import java.util.Iterator;
-import java.util.Map;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Producer of the ScrambleStateGraph for any bandaged 3x3x3.
+// Info about a scramble state of any bandaged cuboid.
+
+import org.distorted.objectlib.helpers.ObjectSignature;
 
 public class ScrambleStateBandagedCuboid
 {
+  public static int MAX_SUPPORTED_SIZE = 5;
+
   public static final int AXIS_NONE = -1;
   public static final int AXIS_X = 0;
   public static final int AXIS_Y = 1;
   public static final int AXIS_Z = 2;
 
-  private static final long INVALID_MOVE = -1;
-  private static final int NUM_MOVES = 27;
-
-  private static final int LAYER_L = 0;
-  private static final int LAYER_M = 1;
-  private static final int LAYER_R = 2;
-
-  private long mID;
-  private int mDistance;
-  private final long[] mMoves;
+  private final ObjectSignature[] mMoves;
+  private final ObjectSignature mSignature;
+  private final int[] mLayer, mTurns;
+  private final int mNumMoves;
+  private final int mStartX, mStartY, mStartZ;
+  private final boolean[][] mIsUnblocked;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public ScrambleStateBandagedCuboid(long id)
+  public ScrambleStateBandagedCuboid(int x, int y, int z, ObjectSignature signature)
     {
-    mDistance = -1;
-    mID = id;
-    mMoves = createMoves(mID);
+    mLayer = new int[3];
+    mTurns = new int[3];
+
+    mIsUnblocked = new boolean[3][MAX_SUPPORTED_SIZE];
+
+    mLayer[0] = x;
+    mLayer[1] = y;
+    mLayer[2] = z;
+
+    mTurns[0] = mLayer[1]==mLayer[2] ? 3:1;
+    mTurns[1] = mLayer[0]==mLayer[2] ? 3:1;
+    mTurns[2] = mLayer[0]==mLayer[1] ? 3:1;
+
+    int xMoves = mLayer[0]>1 ? mTurns[0]*mLayer[0] : 0;
+    int yMoves = mLayer[1]>1 ? mTurns[1]*mLayer[1] : 0;
+    int zMoves = mLayer[2]>1 ? mTurns[2]*mLayer[2] : 0;
+
+    mNumMoves = xMoves + yMoves + zMoves;
+
+    mStartX = 0;
+    mStartY = xMoves;
+    mStartZ = xMoves + yMoves;
+
+    mSignature = signature;
+
+    mMoves = createMoves();
+
+
+android.util.Log.d("D", "sig: "+mSignature.getString() );
+printMoves();
+
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getID()
+  public ObjectSignature getSignature()
     {
-    return mID;
+    return mSignature;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setID(long id)
+  public ObjectSignature getMove(int index)
     {
-    mID = id;
+    return (index>=0 && index<mNumMoves) ? mMoves[index] : null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long getMove(int index)
+  public void removeMoves(ObjectSignature signature)
     {
-    return (index>=0 && index<NUM_MOVES) ? mMoves[index] : INVALID_MOVE;
+    for(int m=0; m<mNumMoves; m++)
+      if( signature.isEqual(mMoves[m]) ) mMoves[m]=null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -80,17 +105,9 @@ public class ScrambleStateBandagedCuboid
     {
     int num = 0;
 
-    if( mMoves[ 0]!=INVALID_MOVE || mMoves[ 1]!=INVALID_MOVE || mMoves[ 2]!=INVALID_MOVE ||
-        mMoves[ 3]!=INVALID_MOVE || mMoves[ 4]!=INVALID_MOVE || mMoves[ 5]!=INVALID_MOVE ||
-        mMoves[ 6]!=INVALID_MOVE || mMoves[ 7]!=INVALID_MOVE || mMoves[ 8]!=INVALID_MOVE   ) num++;
-
-    if( mMoves[ 9]!=INVALID_MOVE || mMoves[10]!=INVALID_MOVE || mMoves[11]!=INVALID_MOVE ||
-        mMoves[12]!=INVALID_MOVE || mMoves[13]!=INVALID_MOVE || mMoves[14]!=INVALID_MOVE ||
-        mMoves[15]!=INVALID_MOVE || mMoves[16]!=INVALID_MOVE || mMoves[17]!=INVALID_MOVE   ) num++;
-
-    if( mMoves[18]!=INVALID_MOVE || mMoves[19]!=INVALID_MOVE || mMoves[20]!=INVALID_MOVE ||
-        mMoves[21]!=INVALID_MOVE || mMoves[22]!=INVALID_MOVE || mMoves[23]!=INVALID_MOVE ||
-        mMoves[24]!=INVALID_MOVE || mMoves[25]!=INVALID_MOVE || mMoves[26]!=INVALID_MOVE   ) num++;
+    for(int x=mStartX; x<mStartY  ; x++) if( mMoves[x]!=null ) { num++; break; }
+    for(int y=mStartY; y<mStartZ  ; y++) if( mMoves[y]!=null ) { num++; break; }
+    for(int z=mStartZ; z<mNumMoves; z++) if( mMoves[z]!=null ) { num++; break; }
 
     return num;
     }
@@ -100,17 +117,7 @@ public class ScrambleStateBandagedCuboid
   private int numXMoves()
     {
     int num=0;
-
-    if( mMoves[ 0]!=INVALID_MOVE ) num++;
-    if( mMoves[ 1]!=INVALID_MOVE ) num++;
-    if( mMoves[ 2]!=INVALID_MOVE ) num++;
-    if( mMoves[ 3]!=INVALID_MOVE ) num++;
-    if( mMoves[ 4]!=INVALID_MOVE ) num++;
-    if( mMoves[ 5]!=INVALID_MOVE ) num++;
-    if( mMoves[ 6]!=INVALID_MOVE ) num++;
-    if( mMoves[ 7]!=INVALID_MOVE ) num++;
-    if( mMoves[ 8]!=INVALID_MOVE ) num++;
-
+    for(int x=mStartX; x<mStartY; x++) if( mMoves[x]!=null ) num++;
     return num;
     }
 
@@ -119,17 +126,7 @@ public class ScrambleStateBandagedCuboid
   private int numYMoves()
     {
     int num=0;
-
-    if( mMoves[ 9]!=INVALID_MOVE ) num++;
-    if( mMoves[10]!=INVALID_MOVE ) num++;
-    if( mMoves[11]!=INVALID_MOVE ) num++;
-    if( mMoves[12]!=INVALID_MOVE ) num++;
-    if( mMoves[13]!=INVALID_MOVE ) num++;
-    if( mMoves[14]!=INVALID_MOVE ) num++;
-    if( mMoves[15]!=INVALID_MOVE ) num++;
-    if( mMoves[16]!=INVALID_MOVE ) num++;
-    if( mMoves[17]!=INVALID_MOVE ) num++;
-
+    for(int y=mStartY; y<mStartZ; y++) if( mMoves[y]!=null ) num++;
     return num;
     }
 
@@ -138,17 +135,7 @@ public class ScrambleStateBandagedCuboid
   private int numZMoves()
     {
     int num=0;
-
-    if( mMoves[18]!=INVALID_MOVE ) num++;
-    if( mMoves[19]!=INVALID_MOVE ) num++;
-    if( mMoves[20]!=INVALID_MOVE ) num++;
-    if( mMoves[21]!=INVALID_MOVE ) num++;
-    if( mMoves[22]!=INVALID_MOVE ) num++;
-    if( mMoves[23]!=INVALID_MOVE ) num++;
-    if( mMoves[24]!=INVALID_MOVE ) num++;
-    if( mMoves[25]!=INVALID_MOVE ) num++;
-    if( mMoves[26]!=INVALID_MOVE ) num++;
-
+    for(int z=mStartZ; z<mNumMoves; z++) if( mMoves[z]!=null ) num++;
     return num;
     }
 
@@ -170,11 +157,7 @@ public class ScrambleStateBandagedCuboid
       case AXIS_Z: return numXMoves()+numYMoves();
       }
 
-    int ret= numXMoves()+numYMoves()+numZMoves();
-
-    //android.util.Log.e("D", "numMoves returning "+ret+" "+formatMoves() );
-
-    return ret;
+    return numXMoves()+numYMoves()+numZMoves();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -183,519 +166,87 @@ public class ScrambleStateBandagedCuboid
     {
     int num = 0;
 
-    for(int m=0; m<NUM_MOVES; m++)
+    if( excludedAxis!=0 )
       {
-      if( (m/9)!=excludedAxis && mMoves[m]!=INVALID_MOVE)
-        {
-        if( num==n ) return m;
-        num++;
-        }
-      }
-
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void removeMoves(long signature)
-    {
-    for(int m=0; m<NUM_MOVES; m++)
-      if( signature==mMoves[m] ) mMoves[m]=INVALID_MOVE;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setMove(int index, long newMove)
-    {
-    if( index>=0 && index<NUM_MOVES ) mMoves[index] = newMove;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public String formatMoves()
-    {
-    String x = getTable( 0);
-    String y = getTable( 9);
-    String z = getTable(18);
-
-    return mID+"\n"+x+"\n"+y+"\n"+z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private String getTable(int index)
-    {
-    long m0 = getMove(index  );
-    long m1 = getMove(index+1);
-    long m2 = getMove(index+2);
-    long m3 = getMove(index+3);
-    long m4 = getMove(index+4);
-    long m5 = getMove(index+5);
-    long m6 = getMove(index+6);
-    long m7 = getMove(index+7);
-    long m8 = getMove(index+8);
-
-    String ret = "";
-
-    if( m0!=INVALID_MOVE ) ret += formatRet(ret,0,-1,m0);
-    if( m1!=INVALID_MOVE ) ret += formatRet(ret,0, 2,m1);
-    if( m2!=INVALID_MOVE ) ret += formatRet(ret,0, 1,m2);
-    if( m3!=INVALID_MOVE ) ret += formatRet(ret,1,-1,m3);
-    if( m4!=INVALID_MOVE ) ret += formatRet(ret,1, 2,m4);
-    if( m5!=INVALID_MOVE ) ret += formatRet(ret,1, 1,m5);
-    if( m6!=INVALID_MOVE ) ret += formatRet(ret,2,-1,m6);
-    if( m7!=INVALID_MOVE ) ret += formatRet(ret,2, 2,m7);
-    if( m8!=INVALID_MOVE ) ret += formatRet(ret,2, 1,m8);
-
-    return formatL("{" + ret + "}");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int[] getMoves(int index)
-    {
-    int numValid = 0;
-    for(int i=index; i<index+9; i++) if( mMoves[i]!=INVALID_MOVE ) numValid++;
-
-    int[] ret = new int[3*numValid];
-
-    long m0 = getMove(index  );
-    long m1 = getMove(index+1);
-    long m2 = getMove(index+2);
-    long m3 = getMove(index+3);
-    long m4 = getMove(index+4);
-    long m5 = getMove(index+5);
-    long m6 = getMove(index+6);
-    long m7 = getMove(index+7);
-    long m8 = getMove(index+8);
-
-    int pointer=0;
-
-    if( m0!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]=-1; ret[pointer+2]= (int)m0; pointer+=3; }
-    if( m1!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 2; ret[pointer+2]= (int)m1; pointer+=3; }
-    if( m2!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 1; ret[pointer+2]= (int)m2; pointer+=3; }
-    if( m3!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]=-1; ret[pointer+2]= (int)m3; pointer+=3; }
-    if( m4!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 2; ret[pointer+2]= (int)m4; pointer+=3; }
-    if( m5!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 1; ret[pointer+2]= (int)m5; pointer+=3; }
-    if( m6!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]=-1; ret[pointer+2]= (int)m6; pointer+=3; }
-    if( m7!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 2; ret[pointer+2]= (int)m7; pointer+=3; }
-    if( m8!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 1; ret[pointer+2]= (int)m8; pointer+=3; }
-
-    return ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private ScrambleState produceScrambleState()
-    {
-    int[] xMoves = getMoves(0);
-    int[] yMoves = getMoves(9);
-    int[] zMoves = getMoves(18);
-
-    int[][] moves = { xMoves,yMoves,zMoves };
-
-    return new ScrambleState( moves );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// STATIC STUFF
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static ScrambleState[] computeGraph(long id)
-    {
-    ScrambleStateBandagedCuboid bsg = new ScrambleStateBandagedCuboid(id);
-    Map<Long, ScrambleStateBandagedCuboid> graph = new LinkedHashMap<>();
-    graph.put(id,bsg);
-
-    insertChildren(graph,id);
-    // if there's only one state, do not prune moves which point to itself
-    if(graph.size()>1) pruneGraph(graph,id);
-    computeDistance(graph,id,0);
-    removeDisconnectedParts(graph);
-    remapGraph(graph);
-
-    int num = graph.size();
-    ScrambleState[] ret = new ScrambleState[num];
-
-    for (Map.Entry<Long, ScrambleStateBandagedCuboid> entry : graph.entrySet())
-      {
-      ScrambleStateBandagedCuboid value = entry.getValue();
-      int mid = (int)value.mID;
-      ret[mid] = value.produceScrambleState();
-      }
-
-    return ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void insertChildren(Map<Long, ScrambleStateBandagedCuboid> map, long id)
-    {
-    ScrambleStateBandagedCuboid bsg = findState(map,id);
-
-    if( bsg==null )
-      {
-      android.util.Log.e("D", "error: "+id+" doesn't exist");
-      return;
-      }
-
-    for(int i=0; i<NUM_MOVES; i++)
-      {
-      long move = bsg.getMove(i);
-
-      if( move!=INVALID_MOVE )
-        {
-        ScrambleStateBandagedCuboid tmp = findState(map,move);
-
-        if( tmp==null )
+      for(int m=mStartX; m<mStartY; m++)
+        if( mMoves[m]!=null )
           {
-          tmp = new ScrambleStateBandagedCuboid(move);
-          map.put(move,tmp);
-          insertChildren(map,move);
+          if( num==n ) return m;
+          num++;
           }
-        }
       }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void pruneGraph(Map<Long, ScrambleStateBandagedCuboid> map, long startingID)
-    {
-    boolean pruned = false;
-    boolean startingIsSingle = false;
-
-    Iterator<Map.Entry<Long, ScrambleStateBandagedCuboid>> it = map.entrySet().iterator();
-
-    while (it.hasNext())
+    if( excludedAxis!=1 )
       {
-      Map.Entry<Long, ScrambleStateBandagedCuboid> entry = it.next();
-      ScrambleStateBandagedCuboid value = entry.getValue();
-
-      if( value.numAxis()<2 )
-        {
-        long prunedID = value.getID();
-
-        if( prunedID!=startingID ) // do not remove the starting point, even if it does have only 1 axis
-          {
-          it.remove();
-          pruned = true;
-          }
-        else
+      for(int m=mStartY; m<mStartZ; m++)
+        if( mMoves[m]!=null )
           {
-          startingIsSingle = true;
+          if( num==n ) return m;
+          num++;
           }
-        }
       }
 
-    for (Map.Entry<Long, ScrambleStateBandagedCuboid> entry : map.entrySet() )
+    if( excludedAxis!=2 )
       {
-      ScrambleStateBandagedCuboid value = entry.getValue();
-
-      for(int m=0; m<NUM_MOVES; m++)
-        {
-        long move = value.mMoves[m];
-        ScrambleStateBandagedCuboid tmp = findState(map,move);
-
-        if( tmp==null || (startingIsSingle && move==startingID) )
-          {
-          value.mMoves[m]=INVALID_MOVE;
-          }
-        }
-      }
-
-    if( pruned ) pruneGraph(map,startingID);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void remapGraph(Map<Long, ScrambleStateBandagedCuboid> map)
-    {
-    int id=0;
-
-    for (Map.Entry<Long, ScrambleStateBandagedCuboid> entry : map.entrySet())
-      {
-      ScrambleStateBandagedCuboid value = entry.getValue();
-      value.mID = id++;
-      }
-
-    for (Map.Entry<Long, ScrambleStateBandagedCuboid> entry : map.entrySet())
-      {
-      ScrambleStateBandagedCuboid value = entry.getValue();
-
-      for(int m=0; m<NUM_MOVES; m++)
-        {
-        long move = value.mMoves[m];
-
-        if( move!=INVALID_MOVE )
-          {
-          ScrambleStateBandagedCuboid tmp = map.get(move);
-          value.mMoves[m] = tmp.mID;
-          }
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void computeDistance(Map<Long, ScrambleStateBandagedCuboid> map, long id, int distance)
-    {
-    ScrambleStateBandagedCuboid state = findState(map,id);
-
-    if( state==null )
-      {
-      android.util.Log.e("D", "error: "+id+" doesn't exist");
-      return;
-      }
-
-    if( state.mDistance<0 )
-      {
-      state.mDistance = distance;
-
-      for(int i=0; i<NUM_MOVES; i++)
-        {
-        long move = state.getMove(i);
-
-        if( move!=INVALID_MOVE )
+      for(int m=mStartZ; m<mNumMoves; m++)
+        if( mMoves[m]!=null )
           {
-          computeDistance(map,move,distance+1);
+          if( num==n ) return m;
+          num++;
           }
-        }
       }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void removeDisconnectedParts(Map<Long, ScrambleStateBandagedCuboid> map)
-    {
-    Iterator<Map.Entry<Long, ScrambleStateBandagedCuboid>> it = map.entrySet().iterator();
-
-    while (it.hasNext())
-      {
-      Map.Entry<Long, ScrambleStateBandagedCuboid> entry = it.next();
-      ScrambleStateBandagedCuboid value = entry.getValue();
-      if( value.mDistance<0 ) it.remove();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static ScrambleStateBandagedCuboid findState(Map<Long, ScrambleStateBandagedCuboid> map, long id)
-    {
-    return map.get(id);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static String formatRet(String str, int row, int angle, long id)
-    {
-    String ret = str.length()!=0 ? ",":"";
-
-    ret += row;
-    ret += angle<0 ? "," : ", ";
-    ret += angle;
-
-         if( id< 10 ) ret += (",  "+id);
-    else if( id<100 ) ret += (", " +id);
-    else              ret += (","  +id);
-
-    return ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static final int LENGTH = 28;
 
-  private static String formatL(String input)
-    {
-    int len = input.length();
-    String ret = input;
-    for(int i=0 ;i<LENGTH-len; i++) ret += " ";
-    return ret;
+    return -1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static long[] createMoves(long id)
+  private ObjectSignature[] createMoves()
     {
-    long[] ret = new long[NUM_MOVES];
+    ObjectSignature[] ret = new ObjectSignature[mNumMoves];
     int index = 0;
 
     for(int axis=0; axis<3; axis++)
-      for(int layer=0; layer<3; layer++)
-        {
-        long x1 = turn(id,axis,layer);
+      for(int layer=0; layer<mLayer[axis]; layer++)
+        mIsUnblocked[axis][layer] = mSignature.isUnblockedFromLeft(axis,layer);
 
-        if( x1!=INVALID_MOVE )
-          {
-          long x2 = turn(x1,axis,layer);
-          long x3 = turn(x2,axis,layer);
-
-          ret[index  ] = x1;
-          ret[index+1] = x2;
-          ret[index+2] = x3;
-          }
-        else
+    for(int axis=0; axis<3; axis++)
+      if( mLayer[axis]>1 )
+        for(int turn=0; turn<mTurns[axis]; turn++)
           {
-          ret[index  ] = INVALID_MOVE;
-          ret[index+1] = INVALID_MOVE;
-          ret[index+2] = INVALID_MOVE;
+          boolean allLayersLocked = true;
+
+          for(int layer=0; layer<mLayer[axis]; layer++)
+            {
+            if( mIsUnblocked[axis][layer] )
+              {
+              if( layer>0 ) allLayersLocked = false;
+              ret[index] = mSignature.turn(axis,layer,turn);
+              }
+            else
+              {
+              ret[index] = ret[index-1].turn(axis,layer,turn);
+              ret[index-1] = null;
+              }
+
+            index++;
+            }
+
+          if( allLayersLocked ) ret[index-1] = null;
           }
 
-        index+=3;
-        }
-
     return ret;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Definition of the id: it's an 'Andreas signature' of a bandaged cube.
-// https://twistypuzzles.com/forum/viewtopic.php?t=24452&sid=f3b4ac0c611c4713e4d7840f1aabbb0b&start=350
-
-  private static long turn(long id, int axis, int layer)
-    {
-    switch(axis)
-      {
-      case AXIS_X: switch(layer)
-                     {
-                     case LAYER_L: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
-                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
-                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1  ) return INVALID_MOVE;
-
-                                   long x1 = cycle(id,9,14,46,41);
-                                   long x2 = cycle(x1,4,35,51,20);
-                                   return    cycle(x2,17,25,38,30);
-
-                     case LAYER_M: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
-                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
-                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1 ||
-                                       getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
-                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
-                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
-
-                                   long x4 = cycle(id,8,13,45,40);
-                                   long x5 = cycle(x4,3,34,50,19);
-                                   return    cycle(x5,16,24,37,29);
-
-                     case LAYER_R: if( getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
-                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
-                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
-
-                                   long x7 = cycle(id,7,12,44,39);
-                                   long x8 = cycle(x7,2,33,49,18);
-                                   return    cycle(x8,15,23,36,28);
-                     }
-
-      case AXIS_Y: switch(layer)
-                     {
-                     case LAYER_L: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
-                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
-                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1  ) return INVALID_MOVE;
-
-                                   long y1 = cycle(id,1,9,10,2);
-                                   long y2 = cycle(y1,0,4,11,7);
-                                   return    cycle(y2,3,6,8,5);
-
-                     case LAYER_M: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
-                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
-                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1 ||
-                                       getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
-                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
-                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
-
-                                   long y4 = cycle(id,21,25,32,28);
-                                   long y5 = cycle(y4,22,30,31,23);
-                                   return    cycle(y5,24,27,29,26);
-
-                     case LAYER_R: if( getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
-                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
-                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
-
-                                   long y7 = cycle(id,42,46,53,49);
-                                   long y8 = cycle(y7,43,51,52,44);
-                                   return    cycle(y8,45,48,50,47);
-                     }
-
-      case AXIS_Z: switch(layer)
-                     {
-                     case LAYER_L: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
-                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
-                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1  ) return INVALID_MOVE;
-
-                                   long z1 = cycle(id,10,20,53,39);
-                                   long z2 = cycle(z1,11,41,52,18);
-                                   return    cycle(z2,19,32,40,31);
-
-                     case LAYER_M: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
-                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
-                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1 ||
-                                       getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
-                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
-                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
-
-                                   long z4 = cycle(id,5,17,48,36);
-                                   long z5 = cycle(z4,6,38,47,15);
-                                   return    cycle(z5,16,27,37,26);
-
-                     case LAYER_R: if( getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
-                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
-                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
-
-                                   long z7 = cycle(id,0,14,43,33);
-                                   long z8 = cycle(z7,1,35,42,12);
-                                   return    cycle(z8,13,22,34,21);
-                     }
-      }
-
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// bit b1 in place of b2 etc.
-
-  private static long cycle(long id, int b1, int b2, int b3, int b4)
-    {
-    long bit1 = getBit(id,b1);
-    long bit2 = getBit(id,b2);
-    long bit3 = getBit(id,b3);
-    long bit4 = getBit(id,b4);
-
-    long i1 = setBit(id,b2,bit1);
-    long i2 = setBit(i1,b3,bit2);
-    long i3 = setBit(i2,b4,bit3);
-    return    setBit(i3,b1,bit4);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static long getBit(long id, int bit)
-    {
-    return (id>>bit)&0x1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static long setBit(long id, int bit, long value)
-    {
-    long old = getBit(id,bit);
-
-    if( old!=value )
-      {
-      long diff = (1L<<bit);
-      id += (value==0 ? -diff : diff);
-      }
-
-    return id;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void printMoves()
     {
     String moves = "";
 
-    for(int i=0; i<NUM_MOVES; i++)
+    for(int i=0; i<mNumMoves; i++)
       {
-      moves += (" " + mMoves[i]);
+      moves += (mMoves[i]!=null ? " "+mMoves[i].getString() : " NULL");
       }
 
     android.util.Log.e("D", moves);
@@ -721,18 +272,4 @@ public class ScrambleStateBandagedCuboid
 
     return ret + "]";
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void printGraph(Map<Long, ScrambleStateBandagedCuboid> map)
-    {
-    int num = map.size();
-    android.util.Log.e("D", "\n"+num+" states\n");
-
-    for (Map.Entry<Long, ScrambleStateBandagedCuboid> entry : map.entrySet())
-      {
-      ScrambleStateBandagedCuboid value = entry.getValue();
-      android.util.Log.e("D", value.formatMoves());
-      }
-    }
 }
