commit a480ee8025accded278b86bb2d737b06f8a7fc73
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Aug 26 01:13:10 2021 +0200

    Standarize the 'isSolved()' method: now all objects, except one (Dino4) have a standard isSolved().
    This incidentally also fixes detection of the solved state in case of Diamond4, i.e. a Master FTO.

diff --git a/src/main/java/org/distorted/objects/MovementSquare.java b/src/main/java/org/distorted/objects/MovementSquare.java
index 94c3da22..8be74804 100644
--- a/src/main/java/org/distorted/objects/MovementSquare.java
+++ b/src/main/java/org/distorted/objects/MovementSquare.java
@@ -46,8 +46,6 @@ class MovementSquare extends Movement
 
   int computeRowFromOffset(int face, int axisIndex, int numLayers, float offset)
     {
-    //android.util.Log.e("D", "face="+face+" size="+numLayers+" offset="+offset);
-
     return offset>DIST2D? 2-axisIndex : 0;
     }
 
diff --git a/src/main/java/org/distorted/objects/TwistyBandagedAbstract.java b/src/main/java/org/distorted/objects/TwistyBandagedAbstract.java
index 66465ef9..7f3136c5 100644
--- a/src/main/java/org/distorted/objects/TwistyBandagedAbstract.java
+++ b/src/main/java/org/distorted/objects/TwistyBandagedAbstract.java
@@ -161,6 +161,14 @@ abstract class TwistyBandagedAbstract extends TwistyObject
   abstract float[][] getPositions();
   abstract int[] getQuatIndices();
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementCube.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int getNumCubits()
@@ -461,18 +469,4 @@ abstract class TwistyBandagedAbstract extends TwistyObject
     {
     return BASIC_ANGLE;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
-      }
-
-    return true;
-    }
 }
diff --git a/src/main/java/org/distorted/objects/TwistyCube.java b/src/main/java/org/distorted/objects/TwistyCube.java
index 1c01159e..695603b1 100644
--- a/src/main/java/org/distorted/objects/TwistyCube.java
+++ b/src/main/java/org/distorted/objects/TwistyCube.java
@@ -136,6 +136,14 @@ class TwistyCube extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementCube.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   ObjectShape getObjectShape(int cubit, int numLayers)
@@ -356,20 +364,6 @@ class TwistyCube extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyDiamond.java b/src/main/java/org/distorted/objects/TwistyDiamond.java
index 7d1f0eae..3a5af363 100644
--- a/src/main/java/org/distorted/objects/TwistyDiamond.java
+++ b/src/main/java/org/distorted/objects/TwistyDiamond.java
@@ -57,6 +57,8 @@ public class TwistyDiamond extends TwistyObject
            COLOR_GREEN , COLOR_GREY
          };
 
+  private static final int[] mFaceMap = new int[] {4,0,6,2,7,3,5,1};
+
   // All legal rotation quats of a Diamond: unit + three 180 deg turns + 8 generators
   private static final Static4D[] QUATS = new Static4D[]
          {
@@ -77,18 +79,6 @@ public class TwistyDiamond extends TwistyObject
 
   private static final float DIST = 0.50f;
 
-  private static final int[][] mFaceNeutralQuatIndex = new int[][]
-         {
-             {6,10},
-             {4, 8},
-             {7,11},
-             {5, 9},
-             {7,11},
-             {5, 9},
-             {6,10},
-             {4, 8}
-         };
-
   private static final int[] mTetraToFaceMap = new int[] {1,2,3,0,5,6,7,4};
 
   private static final double[][] VERTICES_TETRA = new double[][]
@@ -153,6 +143,14 @@ public class TwistyDiamond extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.DIAM, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementDiamond.FACE_AXIS[mFaceMap[status]],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -541,40 +539,6 @@ public class TwistyDiamond extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Diamond is solved if and only if:
-//
-// - all cubits are rotated by the same quat
-// - those which are internal to the side, i.e. those which have only one 'non-black' face, might
-//   also be optionally rotated by one of the two quats whose axis is perpendicular to the face.
-//   (including some octahedrons if numLayers>=4)
-
-  public boolean isSolved()
-    {
-    int q = CUBITS[0].mQuatIndex;
-    int layers = getNumLayers();
-    int numO = getNumOctahedrons(layers);
-
-    for(int i=1; i<numO; i++)
-      {
-      if( CUBITS[i].mQuatIndex != q ) return false;
-      }
-
-    int qI, q1Index, q2Index, face;
-
-    for(int i=numO; i<NUM_CUBITS; i++)
-      {
-      face    = retFaceTetraBelongsTo(i-numO,layers);
-      q1Index = mFaceNeutralQuatIndex[face][0];
-      q2Index = mFaceNeutralQuatIndex[face][1];
-      qI      = CUBITS[i].mQuatIndex;
-
-      if( qI != q && qI != mulQuat(q,q1Index) && qI != mulQuat(q,q2Index) ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyDino4.java b/src/main/java/org/distorted/objects/TwistyDino4.java
index b9782617..f075b4f2 100644
--- a/src/main/java/org/distorted/objects/TwistyDino4.java
+++ b/src/main/java/org/distorted/objects/TwistyDino4.java
@@ -88,6 +88,13 @@ public class TwistyDino4 extends TwistyDino
     super(size, quat, texture, mesh, effects, moves, ObjectList.DIN4, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    return null;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int getFaceColor(int cubit, int cubitface, int size)
diff --git a/src/main/java/org/distorted/objects/TwistyDino6.java b/src/main/java/org/distorted/objects/TwistyDino6.java
index 2c4ddaaa..d17eabbe 100644
--- a/src/main/java/org/distorted/objects/TwistyDino6.java
+++ b/src/main/java/org/distorted/objects/TwistyDino6.java
@@ -45,6 +45,36 @@ public class TwistyDino6 extends TwistyDino
     super(size, quat, texture, mesh, effects, moves, ObjectList.DINO, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Dino6 is solved if and only if:
+//
+// All four 'X' cubits (i.e. those whose longest edge goes along the X axis) are rotated
+// by the same quaternion qX, similarly all four 'Y' cubits by the same qY and all four 'Z'
+// by the same qZ, and then either:
+//
+// a) qX = qY = qZ
+// b) qY = qX*Q2 and qZ = qX*Q8  (i.e. swap of WHITE and YELLOW faces)
+// c) qX = qY*Q2 and qZ = qY*Q10 (i.e. swap of BLUE and GREEN faces)
+// d) qX = qZ*Q8 and qY = qZ*Q10 (i.e. swap of RED and BROWN faces)
+//
+// BUT: cases b), c) and d) are really the same - it's all just a mirror image of the original.
+//
+// X cubits: 0, 2, 8, 10
+// Y cubits: 1, 3, 9, 11
+// Z cubits: 4, 5, 6, 7
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    switch(cubit)
+      {
+      case 0: case 2: case 8: case 10: return null;
+      case 1: case 3: case 9: case 11: return new int[] {2};
+      case 4: case 5: case 6: case  7: return new int[] {8};
+      }
+
+    return null;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int getFaceColor(int cubit, int cubitface, int size)
@@ -87,40 +117,6 @@ public class TwistyDino6 extends TwistyDino
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Dino6 is solved if and only if:
-//
-// All four 'X' cubits (i.e. those whose longest edge goes along the X axis) are rotated
-// by the same quaternion qX, similarly all four 'Y' cubits by the same qY and all four 'Z'
-// by the same qZ, and then either:
-//
-// a) qX = qY = qZ
-// b) qY = qX*Q2 and qZ = qX*Q8  (i.e. swap of WHITE and YELLOW faces)
-// c) qX = qY*Q2 and qZ = qY*Q10 (i.e. swap of BLUE and GREEN faces)
-// d) qX = qZ*Q8 and qY = qZ*Q10 (i.e. swap of RED and BROWN faces)
-//
-// BUT: cases b), c) and d) are really the same - it's all just a mirror image of the original.
-//
-// X cubits: 0, 2, 8, 10
-// Y cubits: 1, 3, 9, 11
-// Z cubits: 4, 5, 6, 7
-
-  public boolean isSolved()
-    {
-    int qX = CUBITS[0].mQuatIndex;
-    int qY = CUBITS[1].mQuatIndex;
-    int qZ = CUBITS[4].mQuatIndex;
-
-    if( CUBITS[2].mQuatIndex != qX || CUBITS[8].mQuatIndex != qX || CUBITS[10].mQuatIndex != qX ||
-        CUBITS[3].mQuatIndex != qY || CUBITS[9].mQuatIndex != qY || CUBITS[11].mQuatIndex != qY ||
-        CUBITS[5].mQuatIndex != qZ || CUBITS[6].mQuatIndex != qZ || CUBITS[ 7].mQuatIndex != qZ  )
-      {
-      return false;
-      }
-
-    return ( qX==qY && qX==qZ ) || ( qY==mulQuat(qX,2) && qZ==mulQuat(qX,8) );
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyHelicopter.java b/src/main/java/org/distorted/objects/TwistyHelicopter.java
index e2bce122..0ecbd399 100644
--- a/src/main/java/org/distorted/objects/TwistyHelicopter.java
+++ b/src/main/java/org/distorted/objects/TwistyHelicopter.java
@@ -243,6 +243,14 @@ public class TwistyHelicopter extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.HELI, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementHelicopter.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -426,67 +434,6 @@ public class TwistyHelicopter extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Helicopter is solved if and only if:
-//
-// 1) all of its corner cubits are rotated with the same quat
-// 2) all its face cubits are rotated with the same quat like the corner ones,
-//    and optionally they also might be turned by a multiple of 90 degrees along
-//    a vector perpendicular to the face they lie on.
-//
-// i.e.
-// cubits  8, 9,10,11,12,13,14,15 - might be extra QUAT 1,8,9
-// cubits 16,17,18,19,20,21,22,23 - might be extra QUAT 2,12,13
-// cubits 24,25,26,27,28,29,30,31 - might be extra QUAT 3,14,15
-
-  public boolean isSolved()
-    {
-    int q = CUBITS[0].mQuatIndex;
-
-    if ( CUBITS[1].mQuatIndex == q &&
-         CUBITS[2].mQuatIndex == q &&
-         CUBITS[3].mQuatIndex == q &&
-         CUBITS[4].mQuatIndex == q &&
-         CUBITS[5].mQuatIndex == q &&
-         CUBITS[6].mQuatIndex == q &&
-         CUBITS[7].mQuatIndex == q  )
-      {
-      int q1 = mulQuat(q,1);
-      int q2 = mulQuat(q,8);
-      int q3 = mulQuat(q,9);
-
-      for(int index=8; index<16; index++)
-        {
-        int qIndex = CUBITS[index].mQuatIndex;
-        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
-        }
-
-      q1 = mulQuat(q, 2);
-      q2 = mulQuat(q,12);
-      q3 = mulQuat(q,13);
-
-      for(int index=16; index<24; index++)
-        {
-        int qIndex = CUBITS[index].mQuatIndex;
-        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
-        }
-
-      q1 = mulQuat(q, 3);
-      q2 = mulQuat(q,14);
-      q3 = mulQuat(q,15);
-
-      for(int index=24; index<32; index++)
-        {
-        int qIndex = CUBITS[index].mQuatIndex;
-        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
-        }
-
-      return true;
-      }
-
-    return false;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyIvy.java b/src/main/java/org/distorted/objects/TwistyIvy.java
index f844f5e8..9e3910f4 100644
--- a/src/main/java/org/distorted/objects/TwistyIvy.java
+++ b/src/main/java/org/distorted/objects/TwistyIvy.java
@@ -124,6 +124,14 @@ public class TwistyIvy extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.IVY, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementIvy.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -456,44 +464,6 @@ public class TwistyIvy extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Ivy is solved if and only if:
-//
-// 1) all 4 of its corner cubits are rotated with the same quat
-// 2) all its face cubits are rotated with the same quat like the corner ones,
-//    and optionally they also might be upside down.
-//
-// i.e.
-// cubits [4] and [5] - might be extra QUAT[1]
-// cubits [6] and [7] - might be extra QUAT[2]
-// cubits [8] and [9] - might be extra QUAT[3]
-
-  public boolean isSolved()
-    {
-    int q1,q = CUBITS[0].mQuatIndex;
-
-    if( CUBITS[1].mQuatIndex == q &&
-        CUBITS[2].mQuatIndex == q &&
-        CUBITS[3].mQuatIndex == q  )
-      {
-      q1 = mulQuat(q,1);
-      if( CUBITS[4].mQuatIndex != q && CUBITS[4].mQuatIndex != q1 ) return false;
-      if( CUBITS[5].mQuatIndex != q && CUBITS[5].mQuatIndex != q1 ) return false;
-
-      q1 = mulQuat(q,2);
-      if( CUBITS[6].mQuatIndex != q && CUBITS[6].mQuatIndex != q1 ) return false;
-      if( CUBITS[7].mQuatIndex != q && CUBITS[7].mQuatIndex != q1 ) return false;
-
-      q1 = mulQuat(q,3);
-      if( CUBITS[8].mQuatIndex != q && CUBITS[8].mQuatIndex != q1 ) return false;
-      if( CUBITS[9].mQuatIndex != q && CUBITS[9].mQuatIndex != q1 ) return false;
-
-      return true;
-      }
-
-    return false;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyJing.java b/src/main/java/org/distorted/objects/TwistyJing.java
index db65f312..3090a69e 100644
--- a/src/main/java/org/distorted/objects/TwistyJing.java
+++ b/src/main/java/org/distorted/objects/TwistyJing.java
@@ -223,6 +223,14 @@ public class TwistyJing extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.JING, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementJing.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float[][] getCubitPositions(int size)
@@ -406,43 +414,6 @@ public class TwistyJing extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// JingPyraminx is solved iff
-// a) all of its corner and edge cubits are rotated with the same quat
-// b) its 4 face cubits might also be rotated along the axis perpendicular to the face.
-//
-// So:
-// [10] might be extra QUAT[4] or QUAT[8]
-// [11] might be extra QUAT[5] or QUAT[9]
-// [12] might be extra QUAT[2] or QUAT[6]
-// [13] might be extra QUAT[3] or QUAT[7]
-
-  public boolean isSolved()
-    {
-    int q1, q = CUBITS[0].mQuatIndex;
-
-    if( CUBITS[1].mQuatIndex != q ) return false;
-    if( CUBITS[2].mQuatIndex != q ) return false;
-    if( CUBITS[3].mQuatIndex != q ) return false;
-    if( CUBITS[4].mQuatIndex != q ) return false;
-    if( CUBITS[5].mQuatIndex != q ) return false;
-    if( CUBITS[6].mQuatIndex != q ) return false;
-    if( CUBITS[7].mQuatIndex != q ) return false;
-    if( CUBITS[8].mQuatIndex != q ) return false;
-    if( CUBITS[9].mQuatIndex != q ) return false;
-
-    q1 = CUBITS[10].mQuatIndex;
-    if( q1!=q && q1!=mulQuat(q,4) && q1!=mulQuat(q,8) ) return false;
-    q1 = CUBITS[11].mQuatIndex;
-    if( q1!=q && q1!=mulQuat(q,5) && q1!=mulQuat(q,9) ) return false;
-    q1 = CUBITS[12].mQuatIndex;
-    if( q1!=q && q1!=mulQuat(q,2) && q1!=mulQuat(q,6) ) return false;
-    q1 = CUBITS[13].mQuatIndex;
-    if( q1!=q && q1!=mulQuat(q,3) && q1!=mulQuat(q,7) ) return false;
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyKilominx.java b/src/main/java/org/distorted/objects/TwistyKilominx.java
index f05d6e4d..87e3868c 100644
--- a/src/main/java/org/distorted/objects/TwistyKilominx.java
+++ b/src/main/java/org/distorted/objects/TwistyKilominx.java
@@ -695,21 +695,6 @@ public class TwistyKilominx extends TwistyMinx
     return 2;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyMegaminx.java b/src/main/java/org/distorted/objects/TwistyMegaminx.java
index 319de7ee..f76dc38f 100644
--- a/src/main/java/org/distorted/objects/TwistyMegaminx.java
+++ b/src/main/java/org/distorted/objects/TwistyMegaminx.java
@@ -608,21 +608,6 @@ public class TwistyMegaminx extends TwistyMinx
     return 4;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyMinx.java b/src/main/java/org/distorted/objects/TwistyMinx.java
index 85031d93..441f0fbc 100644
--- a/src/main/java/org/distorted/objects/TwistyMinx.java
+++ b/src/main/java/org/distorted/objects/TwistyMinx.java
@@ -81,6 +81,8 @@ abstract class TwistyMinx extends TwistyObject
            MINX_DBLUE , MINX_DYELLOW, MINX_WHITE , MINX_GREY
          };
 
+  private static final int[] mFaceMap = new int[] {8,10,3,7,1,11,9,2,4,0,5,6};
+
   // All 60 legal rotation quats of a Minx
   static final Static4D[] QUATS = new Static4D[]
          {
@@ -336,6 +338,14 @@ abstract class TwistyMinx extends TwistyObject
     super(numLayers, realSize, quat, texture, mesh, effects, moves, obj, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementMinx.FACE_AXIS[mFaceMap[status]],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   Static4D[] getQuats()
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index e62ed1a8..3660bdb1 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -95,9 +95,6 @@ public abstract class TwistyObject extends DistortedNode
   final int NUM_CUBITS;
   final int NUM_AXIS;
 
-  private static final float[] mTmp1 = new float[4];
-  private static final float[] mTmp2 = new float[4];
-
   private final int mNumCubitFaces;
   private final Static3D[] mAxis;
   private final float[][] mCuts;
@@ -119,6 +116,9 @@ public abstract class TwistyObject extends DistortedNode
   private final DistortedTexture mTexture;
   private final float mInitScreenRatio;
   private float mObjectScreenRatio;
+  private int[][] mSolvedQuats;
+  private int[][] mQuatMult;
+  private int[] mTmpQuats;
   private int mNumTexRows, mNumTexCols;
   private int mRotRowBitmap;
   private int mRotAxis;
@@ -186,6 +186,7 @@ public abstract class TwistyObject extends DistortedNode
 
     CUBITS = new Cubit[NUM_CUBITS];
     createMeshAndCubits(list,res);
+    createDataStructuresForSolved(numLayers);
 
     mTexture = new DistortedTexture();
     mEffects = new DistortedEffects();
@@ -316,6 +317,147 @@ public abstract class TwistyObject extends DistortedNode
     return mesh;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createDataStructuresForSolved(int numLayers)
+    {
+    mTmpQuats = new int[QUATS.length];
+    mSolvedQuats = new int[NUM_CUBITS][];
+
+    for(int c=0; c<NUM_CUBITS; c++)
+      {
+      mSolvedQuats[c] = getSolvedQuats(c,numLayers);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// This is used to build internal data structures for the generic 'isSolved()'
+//
+// if this is an internal cubit (all faces black): return -1
+// if this is a face cubit (one non-black face): return the color index of the only non-black face.
+// Color index, i.e. the index into the 'FACE_COLORS' table.
+// else (edge or corner cubit, more than one non-black face): return -2.
+
+  int retCubitSolvedStatus(int cubit, int numLayers)
+    {
+    int numNonBlack=0, nonBlackIndex=-1, color;
+
+    for(int face=0; face<mNumCubitFaces; face++)
+      {
+      color = getFaceColor(cubit,face,numLayers);
+
+      if( color<NUM_TEXTURES )
+        {
+        numNonBlack++;
+        nonBlackIndex = color%NUM_FACES;
+        }
+      }
+
+    if( numNonBlack==0 ) return -1;
+    if( numNonBlack>=2 ) return -2;
+
+    return nonBlackIndex;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] buildSolvedQuats(Static3D faceAx, Static4D[] quats)
+    {
+    final float MAXD = 0.0001f;
+    float x = faceAx.get0();
+    float y = faceAx.get1();
+    float z = faceAx.get2();
+    float a,dx,dy,dz,qx,qy,qz;
+    Static4D quat;
+
+    int len = quats.length;
+    int place = 0;
+
+    for(int q=1; q<len; q++)
+      {
+      quat = quats[q];
+      qx = quat.get0();
+      qy = quat.get1();
+      qz = quat.get2();
+
+           if( x!=0.0f ) { a = qx/x; }
+      else if( y!=0.0f ) { a = qy/y; }
+      else               { a = qz/z; }
+
+      dx = a*x-qx;
+      dy = a*y-qy;
+      dz = a*z-qz;
+
+      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
+        {
+        mTmpQuats[place++] = q;
+        }
+      }
+
+    if( place!=0 )
+      {
+      int[] ret = new int[place];
+      System.arraycopy(mTmpQuats,0,ret,0,place);
+      return ret;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getMultQuat(int index1, int index2)
+    {
+    if( mQuatMult==null )
+      {
+      int len = QUATS.length;
+      mQuatMult = new int[len][len];
+
+      for(int i=0; i<len; i++)
+        for(int j=0; j<len; j++) mQuatMult[i][j] = -1;
+      }
+
+    if( mQuatMult[index1][index2]==-1 )
+      {
+      mQuatMult[index1][index2] = mulQuat(index1,index2);
+      }
+
+    return mQuatMult[index1][index2];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean isSolved()
+    {
+    int len, q1,q = CUBITS[0].mQuatIndex;
+    int[] solved;
+    boolean skip;
+
+    for(int c=1; c<NUM_CUBITS; c++)
+      {
+      q1 = CUBITS[c].mQuatIndex;
+
+      if( q1==q ) continue;
+
+      skip = false;
+      solved = mSolvedQuats[c];
+      len = solved==null ? 0:solved.length;
+
+      for(int i=0; i<len; i++)
+        {
+        if( q1==getMultQuat(q,solved[i]) )
+          {
+          skip = true;
+          break;
+          }
+        }
+
+      if( !skip ) return false;
+      }
+
+    return true;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void setObjectRatio(float sizeChange)
@@ -523,73 +665,6 @@ public abstract class TwistyObject extends DistortedNode
     return -1;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
-// then if it were rotated by quaternion 'quat'.
-// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
-// middle squares get interchanged. No visible difference!
-//
-// So: this is true iff the cubit
-// a) is a corner or edge and the quaternions are the same
-// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
-
-  boolean thereIsVisibleDifference(Cubit cubit, int quatIndex)
-    {
-    if ( cubit.mQuatIndex == quatIndex ) return false;
-
-    int belongsToHowManyFaces = 0;
-    int bitmap = (1<<(getNumLayers()-1)) + 1;
-
-    for(int i = 0; i< NUM_AXIS; i++)
-      {
-      if( (cubit.mRotationRow[i] & bitmap) != 0 ) belongsToHowManyFaces++;
-      }
-
-    switch(belongsToHowManyFaces)
-      {
-      case 0 : return false;  // 'inside' cubit that does not lie on any face
-      case 1 :                // cubit that lies inside one of the faces
-               float[] orig   = cubit.getOrigPosition();
-               Static4D quat1 = QUATS[quatIndex];
-               Static4D quat2 = QUATS[cubit.mQuatIndex];
-
-               Static4D cubitCenter = new Static4D( orig[0], orig[1], orig[2], 0);              // not used for bandaged objects,
-               Static4D rotated1 = QuatHelper.rotateVectorByQuat( cubitCenter, quat1 );   // only check the first position
-               Static4D rotated2 = QuatHelper.rotateVectorByQuat( cubitCenter, quat2 );
-
-               rotated1.get(mTmp1, 0, 0, 0);
-               rotated2.get(mTmp2, 0, 0, 0);
-
-               for(int i = 0; i< NUM_AXIS; i++)
-                 {
-                 if( (computeRow(mTmp1,i) & computeRow(mTmp2,i) & bitmap) != 0 ) return false;
-                 }
-               return true;
-
-      default: return true;  // edge or corner
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// create StickerCoord and FaceTransform data structures
-
-  void createFaceDataStructures(double[][][] verts, int[][][] indices)
-    {
-    int numCubitTypes = verts.length;
-    FactoryCubit factory = FactoryCubit.getInstance();
-    factory.clear();
-
-    for(int cubit=0; cubit<numCubitTypes; cubit++)
-      {
-      double[][] vertices = verts[cubit];
-      int[][] vertIndices = indices[cubit];
-      factory.createNewFaceTransform(vertices,vertIndices);
-      }
-
-    factory.printFaceTransform();
-    factory.printStickerCoords();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getCubitFaceColorIndex(int cubit, int face)
@@ -1013,9 +1088,9 @@ public abstract class TwistyObject extends DistortedNode
   abstract int getNumCubitVariants(int numLayers);
   abstract Static4D getQuat(int cubit, int numLayers);
   abstract ObjectShape getObjectShape(int cubit, int numLayers);
+  abstract int[] getSolvedQuats(int cubit, int numLayers);
 
   public abstract Static3D[] getRotationAxis();
-  public abstract boolean isSolved();
   public abstract int[] getBasicAngle();
   public abstract void randomizeNewScramble(int[][] scramble, Random rnd, int curScramble, int totScrambles);
   public abstract int getObjectName(int numLayers);
diff --git a/src/main/java/org/distorted/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objects/TwistyPyraminx.java
index 4a70cce0..4531b073 100644
--- a/src/main/java/org/distorted/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objects/TwistyPyraminx.java
@@ -135,6 +135,14 @@ public class TwistyPyraminx extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementPyraminx.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private float[] getRowChances(int numLayers)
@@ -441,20 +449,6 @@ public class TwistyPyraminx extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyRedi.java b/src/main/java/org/distorted/objects/TwistyRedi.java
index 293c85c9..64d1e080 100644
--- a/src/main/java/org/distorted/objects/TwistyRedi.java
+++ b/src/main/java/org/distorted/objects/TwistyRedi.java
@@ -213,6 +213,14 @@ public class TwistyRedi extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.REDI, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementRedi.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -478,34 +486,6 @@ public class TwistyRedi extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Redi is solved if and only if all cubits are rotated with the same quat.
-
-  public boolean isSolved()
-    {
-    int q = CUBITS[0].mQuatIndex;
-
-    return ( CUBITS[ 1].mQuatIndex == q &&
-             CUBITS[ 2].mQuatIndex == q &&
-             CUBITS[ 3].mQuatIndex == q &&
-             CUBITS[ 4].mQuatIndex == q &&
-             CUBITS[ 5].mQuatIndex == q &&
-             CUBITS[ 6].mQuatIndex == q &&
-             CUBITS[ 7].mQuatIndex == q &&
-             CUBITS[ 8].mQuatIndex == q &&
-             CUBITS[ 9].mQuatIndex == q &&
-             CUBITS[10].mQuatIndex == q &&
-             CUBITS[11].mQuatIndex == q &&
-             CUBITS[12].mQuatIndex == q &&
-             CUBITS[13].mQuatIndex == q &&
-             CUBITS[14].mQuatIndex == q &&
-             CUBITS[15].mQuatIndex == q &&
-             CUBITS[16].mQuatIndex == q &&
-             CUBITS[17].mQuatIndex == q &&
-             CUBITS[18].mQuatIndex == q &&
-             CUBITS[19].mQuatIndex == q  );
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyRex.java b/src/main/java/org/distorted/objects/TwistyRex.java
index 162968c6..b435fcc7 100644
--- a/src/main/java/org/distorted/objects/TwistyRex.java
+++ b/src/main/java/org/distorted/objects/TwistyRex.java
@@ -160,6 +160,14 @@ public class TwistyRex extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.REX, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementRex.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -487,60 +495,6 @@ public class TwistyRex extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Rex is solved if and only if:
-//
-// 1) all 12 of its edge cubits are rotated with the same quat
-// 2) all its face & corner cubits are rotated with the same quat like the edge ones,
-//    and optionally they also might be upside down.
-//
-// i.e.
-// corners ( 0, 1, 2, 3, 4, 5, 6, 7) and faces (24,25) - might be extra QUAT[1]
-// corners ( 8, 9,10,11,12,13,14,15) and faces (26,27) - might be extra QUAT[2]
-// corners (16,17,18,19,20,21,22,23) and faces (28,29) - might be extra QUAT[3]
-
-  public boolean isSolved()
-    {
-    int q1,q = CUBITS[30].mQuatIndex;
-
-    for(int i=31; i<42; i++)
-      {
-      if( CUBITS[i].mQuatIndex != q) return false;
-      }
-
-    q1 = mulQuat(q,1);
-
-    for(int i=0; i<8; i++)
-      {
-      if( CUBITS[i].mQuatIndex != q && CUBITS[i].mQuatIndex != q1 ) return false;
-      }
-
-    if( CUBITS[24].mQuatIndex != q && CUBITS[24].mQuatIndex != q1 ) return false;
-    if( CUBITS[25].mQuatIndex != q && CUBITS[25].mQuatIndex != q1 ) return false;
-
-    q1 = mulQuat(q,2);
-
-    for(int i=8; i<16; i++)
-      {
-      if( CUBITS[i].mQuatIndex != q && CUBITS[i].mQuatIndex != q1 ) return false;
-      }
-
-    if( CUBITS[26].mQuatIndex != q && CUBITS[26].mQuatIndex != q1 ) return false;
-    if( CUBITS[27].mQuatIndex != q && CUBITS[27].mQuatIndex != q1 ) return false;
-
-    q1 = mulQuat(q,3);
-
-    for(int i=16; i<24; i++)
-      {
-      if( CUBITS[i].mQuatIndex != q && CUBITS[i].mQuatIndex != q1 ) return false;
-      }
-
-    if( CUBITS[28].mQuatIndex != q && CUBITS[28].mQuatIndex != q1 ) return false;
-    if( CUBITS[29].mQuatIndex != q && CUBITS[29].mQuatIndex != q1 ) return false;
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistySkewb.java b/src/main/java/org/distorted/objects/TwistySkewb.java
index b5ffb0e3..8c63ae96 100644
--- a/src/main/java/org/distorted/objects/TwistySkewb.java
+++ b/src/main/java/org/distorted/objects/TwistySkewb.java
@@ -197,6 +197,14 @@ public class TwistySkewb extends TwistyObject
     super(size, 2*size-2, quat, texture, mesh, effects, moves, ObjectList.SKEW, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementSkewb.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int getNumCorners()
@@ -605,48 +613,6 @@ public class TwistySkewb extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Skewb is solved if and only if:
-//
-// 1) all of its corner and edge cubits are rotated with the same quat
-// 2) all its face cubits are rotated with the same quat like the corner ones,
-//    and optionally they also might be upside down.
-//
-// i.e.
-// cubits [ 8] and [ 9] - might be extra QUAT[1]
-// cubits [10] and [11] - might be extra QUAT[2]
-// cubits [12] and [13] - might be extra QUAT[3]
-
-  public boolean isSolved()
-    {
-    int q = CUBITS[0].mQuatIndex;
-
-    int numLayers      = getNumLayers();
-    int numCorners     = getNumCorners();
-    int numEdges       = getNumEdges(numLayers);
-    int cornersAndEdges= numCorners + numEdges;
-    int centersPerFace = getNumCentersPerFace(numLayers);
-    int cubit, q1=q;
-
-    for(cubit=0; cubit<cornersAndEdges; cubit++)
-      {
-      if( CUBITS[cubit].mQuatIndex != q ) return false;
-      }
-
-    for(int face=0; face<6; face++)
-      {
-      if( face%2==0 ) q1 = mulQuat(q, (face/2)+1);
-
-      for(int center=0; center<centersPerFace; center++)
-        {
-        if( CUBITS[cubit].mQuatIndex != q && CUBITS[cubit].mQuatIndex != q1 ) return false;
-        cubit++;
-        }
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistySquare1.java b/src/main/java/org/distorted/objects/TwistySquare1.java
index 77d84ecd..fd436eb4 100644
--- a/src/main/java/org/distorted/objects/TwistySquare1.java
+++ b/src/main/java/org/distorted/objects/TwistySquare1.java
@@ -176,6 +176,14 @@ class TwistySquare1 extends TwistySquare
     mCornerQuat = new int[8];
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementSquare.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   ObjectShape getObjectShape(int cubit, int numLayers)
@@ -513,20 +521,6 @@ class TwistySquare1 extends TwistySquare
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( CUBITS[i].mQuatIndex != index ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistySquare2.java b/src/main/java/org/distorted/objects/TwistySquare2.java
index d50aee71..f16fd775 100644
--- a/src/main/java/org/distorted/objects/TwistySquare2.java
+++ b/src/main/java/org/distorted/objects/TwistySquare2.java
@@ -181,6 +181,33 @@ class TwistySquare2 extends TwistySquare
     super(size, quat, texture, mesh, effects, moves, ObjectList.SQU2, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Square-2 is solved iff
+// a) all of its cubits are rotated with the same quat
+// b) its two 'middle' cubits are rotated with the same quat, the 6 'front' and 6 'back'
+// edges and corners with this quat multiplied by QUATS[18] (i.e. those are upside down)
+// and all the 12 left and right edges and corners also with the same quat multiplied by
+// QUATS[12] - i.e. also upside down.
+
+  private static final int[] S18 = new int[] {18};
+  private static final int[] S12 = new int[] {12};
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    switch(cubit)
+      {
+      case  0: case  1: return null;
+      case  2: case  4: case  6: case  8:
+      case 10: case 12: case 14: case 16:
+      case 19: case 21: case 23: case 25: return S18;
+      case  3: case  5: case  7: case  9:
+      case 11: case 13: case 15: case 17:
+      case 18: case 20: case 22: case 24: return S12;
+      }
+
+    return null;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   ObjectShape getObjectShape(int cubit, int numLayers)
@@ -327,43 +354,6 @@ class TwistySquare2 extends TwistySquare
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Square-2 is solved iff
-// a) all of its cubits are rotated with the same quat
-// b) its two 'middle' cubits are rotated with the same quat, the 6 'front' and 6 'back'
-// edges and corners with this quat multiplied by QUATS[18] (i.e. those are upside down)
-// and all the 12 left and right edges and corners also with the same quat multiplied by
-// QUATS[12] - i.e. also upside down.
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    if( CUBITS[1].mQuatIndex!=index ) return false;
-
-    int indexX = mulQuat(index,12);  // QUATS[12] = 180deg (1,0,0)
-    int indexZ = mulQuat(index,18);  // QUATS[18] = 180deg (0,0,1)
-
-    for(int i=2; i<18; i+=2)
-      {
-      if( CUBITS[i].mQuatIndex != index && CUBITS[i].mQuatIndex != indexZ ) return false;
-      }
-    for(int i=3; i<18; i+=2)
-      {
-      if( CUBITS[i].mQuatIndex != index && CUBITS[i].mQuatIndex != indexX ) return false;
-      }
-    for(int i=18; i<NUM_CUBITS; i+=2)
-      {
-      if( CUBITS[i].mQuatIndex != index && CUBITS[i].mQuatIndex != indexX ) return false;
-      }
-    for(int i=19; i<NUM_CUBITS; i+=2)
-      {
-      if( CUBITS[i].mQuatIndex != index && CUBITS[i].mQuatIndex != indexZ ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyUltimate.java b/src/main/java/org/distorted/objects/TwistyUltimate.java
index 3f96ddf9..13a19c9c 100644
--- a/src/main/java/org/distorted/objects/TwistyUltimate.java
+++ b/src/main/java/org/distorted/objects/TwistyUltimate.java
@@ -240,6 +240,14 @@ class TwistyUltimate extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.ULTI, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementUltimate.FACE_AXIS[status],QUATS);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   ObjectShape getObjectShape(int cubit, int numLayers)
@@ -422,20 +430,6 @@ class TwistyUltimate extends TwistyObject
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean isSolved()
-    {
-    int index = CUBITS[0].mQuatIndex;
-
-    for(int i=1; i<NUM_CUBITS; i++)
-      {
-      if( CUBITS[i].mQuatIndex != index ) return false;
-      }
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
