commit 6bd1dc641acad09941dc3b6fe45238a4f65a4015
Author: leszek <leszek@koltunski.pl>
Date:   Tue Nov 26 00:23:58 2024 +0100

    Progress with PhasedSolver.
    Introduce an abstracted TargetQuats.

diff --git a/src/main/java/org/distorted/objectlib/algphases/Phase.java b/src/main/java/org/distorted/objectlib/algphases/Phase.java
index b2db828f..9115df6d 100644
--- a/src/main/java/org/distorted/objectlib/algphases/Phase.java
+++ b/src/main/java/org/distorted/objectlib/algphases/Phase.java
@@ -11,6 +11,7 @@ package org.distorted.objectlib.algphases;
 
 import org.distorted.objectlib.algsolvers.MitmTable;
 import org.distorted.objectlib.algsolvers.SolvedObject;
+import org.distorted.objectlib.algsolvers.TargetQuats;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -20,8 +21,17 @@ abstract public class Phase
 
   protected int[][] mCubitGroups;
   protected int mNumGroups;
+  protected int mNumCubitsInThisPhase;
   protected int mNumCubits;
-  protected int[][] mEndQuats;
+  protected TargetQuats mTargetQuats;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Phase(SolvedObject object)
+    {
+    float[][] pos = object.getCubitPositions();
+    mNumCubits = pos.length;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -41,7 +51,7 @@ abstract public class Phase
   public int whichSubPhase(int cubit)
     {
     for(int g=0; g<mNumGroups; g++)
-      for( int j : mCubitGroups[g])
+      for(int j : mCubitGroups[g])
         if( cubit==j ) return g;
 
     return -1;
@@ -49,23 +59,27 @@ abstract public class Phase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int[] endQuatIndices(SolvedObject object, int cubitQuat, int cubit)
+  public int[] endQuatIndices(SolvedObject object, int cubit)
     {
     return INDICES;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void setEndQuats(int[][] endquats)
+  public void setTargetQuats(SolvedObject object, boolean[] inPreviousPhases)
     {
-    mEndQuats = endquats;
-    }
+    int len = inPreviousPhases.length;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    int[][] target = new int[len][];
 
-  public int[][] getEndQuats()
-    {
-    return mEndQuats;
+    for(int c=0; c<len; c++)
+      {
+      if( inPreviousPhases[c] ) target[c] = INDICES;
+      else if( contains(c) )    target[c] = endQuatIndices(object,c);
+      else                      target[c] = null;
+      }
+
+    mTargetQuats = new TargetQuats(target);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie1.java b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie1.java
index e441534c..64e5b9ac 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie1.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie1.java
@@ -23,13 +23,15 @@ public class PhaseCyclicOrie1 extends PhaseMitm
 
   public PhaseCyclicOrie1(SolvedObject object, int cubit, int quat)
     {
+    super(object);
+
     Static4D[] objectQuats = object.getQuats();
     Static4D q = objectQuats[quat];
     double alpha = 2*Math.acos(q.get3());
     double numTurns = 2*Math.PI / alpha;
-    mNumCubits = (int)(numTurns+0.5f);
+    mNumCubitsInThisPhase = (int)(numTurns+0.5f);
 
-    mCubitGroups  = new int[1][mNumCubits];
+    mCubitGroups  = new int[1][mNumCubitsInThisPhase];
     mNumGroups = 1;
     mCyclicQuat = quat;
 
@@ -43,7 +45,7 @@ public class PhaseCyclicOrie1 extends PhaseMitm
 
     mCubitGroups[0][0] = cubit;
 
-    for(int c=1; c<mNumCubits; c++)
+    for(int c=1; c<mNumCubitsInThisPhase; c++)
       {
       QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,q);
       x = tmp[0];
@@ -84,13 +86,13 @@ public class PhaseCyclicOrie1 extends PhaseMitm
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
-  public int[] endQuatIndices(SolvedObject object, int cubitQuat, int cubit)
+  public int[] endQuatIndices(SolvedObject object, int cubit)
     {
-    int[] retQuats = new int[mNumCubits];
+    int[] retQuats = new int[mNumCubitsInThisPhase];
     retQuats[0] = 0;
     retQuats[1] = mCyclicQuat;
 
-    for(int i=2; i<mNumCubits; i++)
+    for(int i=2; i<mNumCubitsInThisPhase; i++)
       {
       retQuats[i] = object.getMultQuat(mCyclicQuat,retQuats[i-1]);
       }
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie2.java b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie2.java
index b93008ef..bae98b65 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie2.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicOrie2.java
@@ -19,13 +19,15 @@ public class PhaseCyclicOrie2 extends PhaseMitm
 {
   public PhaseCyclicOrie2(SolvedObject object, int cubit, int quat)
     {
+    super(object);
+
     Static4D[] objectQuats = object.getQuats();
     Static4D q = objectQuats[quat];
     double alpha = 2*Math.acos(q.get3());
     double numTurns = 2*Math.PI / alpha;
-    mNumCubits = (int)(numTurns+0.5f);
+    mNumCubitsInThisPhase = (int)(numTurns+0.5f);
 
-    mCubitGroups  = new int[1][mNumCubits];
+    mCubitGroups  = new int[1][mNumCubitsInThisPhase];
     mNumGroups = 1;
 
     float[][] positions = object.getCubitPositions();
@@ -38,7 +40,7 @@ public class PhaseCyclicOrie2 extends PhaseMitm
 
     mCubitGroups[0][0] = cubit;
 
-    for(int c=1; c<mNumCubits; c++)
+    for(int c=1; c<mNumCubitsInThisPhase; c++)
       {
       QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,q);
       x = tmp[0];
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm1.java b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm1.java
index 7ad885d0..cb4287ac 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm1.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm1.java
@@ -24,13 +24,15 @@ public class PhaseCyclicPerm1 extends PhaseMitm
 
   public PhaseCyclicPerm1(SolvedObject object, int cubit, int quat)
     {
+    super(object);
+
     Static4D[] objectQuats = object.getQuats();
     Static4D q = objectQuats[quat];
     double alpha = 2*Math.acos(q.get3());
     double numTurns = 2*Math.PI / alpha;
-    mNumCubits = (int)(numTurns+0.5f);
+    mNumCubitsInThisPhase = (int)(numTurns+0.5f);
 
-    mCubitGroups  = new int[1][mNumCubits];
+    mCubitGroups  = new int[1][mNumCubitsInThisPhase];
     mNumGroups = 1;
     mObjectQuats = objectQuats;
 
@@ -45,7 +47,7 @@ public class PhaseCyclicPerm1 extends PhaseMitm
 
     mCubitGroups[0][0] = cubit;
 
-    for(int c=1; c<mNumCubits; c++)
+    for(int c=1; c<mNumCubitsInThisPhase; c++)
       {
       QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,q);
       x = tmp[0];
@@ -86,7 +88,7 @@ public class PhaseCyclicPerm1 extends PhaseMitm
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
-  public int[] endQuatIndices(SolvedObject object, int cubitQuat, int cubit)
+  public int[] endQuatIndices(SolvedObject object, int cubit)
     {
     float[] thisPos = mPositions[cubit];
     int numQuats = mObjectQuats.length;
@@ -100,10 +102,7 @@ public class PhaseCyclicPerm1 extends PhaseMitm
 
     for(int q=0; q<numQuats; q++)
       {
-      int qn = object.getMultQuat(q,cubitQuat);  // i.e. if we first rotate cubit by cubitQuat then it ends
-                                                 // up in (x,y,z) (current position) and then by q and it
-                                                 // ends up in final pos (but not necessarily final orientation)
-      QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,mObjectQuats[qn]);
+      QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,mObjectQuats[q]);
 
       float dx = tmp[0] - x;
       float dy = tmp[1] - y;
@@ -113,7 +112,7 @@ public class PhaseCyclicPerm1 extends PhaseMitm
       if( err < MAX_ERROR )
         {
         numGoodQuats++;
-        tmpQuats[qn] = 1;
+        tmpQuats[q] = 1;
         }
       }
 
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm2.java b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm2.java
index 00d83e57..b69c9d8a 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm2.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseCyclicPerm2.java
@@ -19,13 +19,15 @@ public class PhaseCyclicPerm2 extends PhaseMitm
 {
   public PhaseCyclicPerm2(SolvedObject object, int cubit, int quat)
     {
+    super(object);
+
     Static4D[] objectQuats = object.getQuats();
     Static4D q = objectQuats[quat];
     double alpha = 2*Math.acos(q.get3());
     double numTurns = 2*Math.PI / alpha;
-    mNumCubits = (int)(numTurns+0.5f);
+    mNumCubitsInThisPhase = (int)(numTurns+0.5f);
 
-    mCubitGroups  = new int[1][mNumCubits];
+    mCubitGroups  = new int[1][mNumCubitsInThisPhase];
     mNumGroups = 1;
 
     float[][] positions = object.getCubitPositions();
@@ -38,7 +40,7 @@ public class PhaseCyclicPerm2 extends PhaseMitm
 
     mCubitGroups[0][0] = cubit;
 
-    for(int c=1; c<mNumCubits; c++)
+    for(int c=1; c<mNumCubitsInThisPhase; c++)
       {
       QuatHelper.rotateVectorByQuat(tmp,x,y,z,1,q);
       x = tmp[0];
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseMitm.java b/src/main/java/org/distorted/objectlib/algphases/PhaseMitm.java
index 54d78cf7..18fc37a8 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseMitm.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseMitm.java
@@ -11,6 +11,7 @@ package org.distorted.objectlib.algphases;
 
 import org.distorted.objectlib.algsolvers.MitmTable;
 import org.distorted.objectlib.algsolvers.SolvedObject;
+import org.distorted.objectlib.algsolvers.TargetQuats;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -20,10 +21,16 @@ abstract public class PhaseMitm extends Phase
   private final static double MAX_SEARCH = 10000000.0;   // max 10mln positions back-searched
 
   private boolean[] mSolved;
-  private int[][] mTmp;
   private int[] mWhichSubphase;
   private int mForwardDepth, mBackwardDepth;
-  private int mDepth;
+  private int mDepth, mGroup;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  PhaseMitm(SolvedObject object)
+    {
+    super(object);
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Only attempt to find solutions which are not longer than maxlen; if not found, return null.
@@ -43,14 +50,14 @@ abstract public class PhaseMitm extends Phase
     int tmpBackward= depth-tmpForward;
 
     table.build(object,tmpForward);
-    return table.tryReaching(object,mEndQuats,tmpBackward,tmpForward);
+    return table.tryReaching(object,mTargetQuats,tmpBackward,tmpForward);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int prepareForSubPhase(SolvedObject object, MitmTable table)
     {
-    table.prepareForSubPhase(mEndQuats);
+    table.prepareForSubPhase(mTargetQuats);
     int bytesPerEntry = table.getBytesPerEntry();
     int branchingFactor = object.getBranchingFactor();
 
@@ -75,13 +82,8 @@ abstract public class PhaseMitm extends Phase
 
   private int computeBackwardDepth(int branchingFactor)
     {
-    int multiplier = 1;
-
-    for( int[] quats : mEndQuats )
-      if( quats!=null ) multiplier *= quats.length;
-
+    int multiplier = mTargetQuats.computeNumCombinations();
     double maxDepth = Math.log(MAX_SEARCH/multiplier) / Math.log(branchingFactor);
-
     return (int)maxDepth;
     }
 
@@ -106,14 +108,13 @@ abstract public class PhaseMitm extends Phase
   @Override
   public void prepareForSubphases()
     {
-    int numEndQuats = mEndQuats.length;
     mSolved = new boolean[mNumGroups];
-    mTmp = new int[numEndQuats][];
-    mWhichSubphase = new int[numEndQuats];
+    mWhichSubphase = new int[mNumCubits];
 
-    for(int c=0; c<numEndQuats; c++) mWhichSubphase[c] = whichSubPhase(c);
+    for(int c=0; c<mNumCubits; c++) mWhichSubphase[c] = whichSubPhase(c);
     for(int s=0; s<mNumGroups; s++) mSolved[s] = false;
     mDepth = 0;
+    mGroup = 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -150,37 +151,41 @@ abstract public class PhaseMitm extends Phase
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
-  public boolean solveAnySubphase( SolvedObject object, MitmTable table, int[][] solution, int solIndex)
+  public boolean solveAnySubphase( SolvedObject object, MitmTable table, int[][] solution, int subPhase)
     {
-    int maxDepth = mDepth;
-    int numQuats = mEndQuats.length;
-    System.arraycopy(mEndQuats, 0, mTmp, 0, numQuats);
+    int maxDepth = mDepth+1;  // this might mean that we do one extra depth, but if we set it to
+                              // mDepth then if after current mGroups there are no more unsolved
+                              // subgroups, we will simply give up and end solving altogether!
+
+    TargetQuats tmp = new TargetQuats(mTargetQuats);
 
-    for( ; mDepth<=maxDepth; mDepth++)
-      for(int p=0; p<mNumGroups; p++)
-        if( !mSolved[p] )
+    for( ; mDepth<=maxDepth; mDepth++,mGroup=0)
+      for(int g=mGroup; g<mNumGroups; g++)
+        if( !mSolved[g] )
           {
-          for(int q=0; q<numQuats; q++)
+          for(int q=0; q<mNumCubits; q++)
             {
-            // -1 means that the cubit belongs to the previous (or next) phase. Cubits belonging to the
-            // next phase already have their endQuats set to null (see PhasedSolver.figureOutEndQuatIndices)
-            // Here we set to null (i.e. - mark that we do not care about) the quats of all cubits which
+            // -1 means that the cubit doesn't belong to the current phase. Cubits belonging to
+            // the next phase already have their targetQuats set to null (see Phase.setTargetQuats)
+            // Here we set to null (mark that we do not care about) the quats of all cubits which
             // belong to the current phase and haven't been solved yet (except the current subphase!).
-            int sp = mWhichSubphase[q];
-            mEndQuats[q] = (sp==-1 || sp==p || mSolved[sp]) ? mTmp[q] : null;
+            int wsp = mWhichSubphase[q];
+            int[] newValue = (wsp==-1 || wsp==g || mSolved[wsp]) ? tmp.getQuat(q) : null;
+            mTargetQuats.setQuat(newValue,q);
             }
           maxDepth = prepareForSubPhase(object,table);
-          solution[solIndex] = findSol(object,table,mDepth);
+          solution[subPhase] = findSol(object,table,mDepth);
 
-          if( solution[solIndex]!=null )
+          if( solution[subPhase]!=null )
             {
-            System.arraycopy(mTmp, 0, mEndQuats, 0, numQuats);
-            mSolved[p] = true;
+            mTargetQuats.set(tmp);
+            mSolved[g] = true;
+            mGroup = g+1;   // when we get called next time, do not repeat the calculations
             return true;
             }
           }
 
-    System.arraycopy(mTmp, 0, mEndQuats, 0, numQuats);
+    mTargetQuats.set(tmp);
     return false;
     }
 }
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseNotCyclic.java b/src/main/java/org/distorted/objectlib/algphases/PhaseNotCyclic.java
index e381c7aa..ebdfe89f 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseNotCyclic.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseNotCyclic.java
@@ -9,18 +9,22 @@
 
 package org.distorted.objectlib.algphases;
 
+import org.distorted.objectlib.algsolvers.SolvedObject;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class PhaseNotCyclic extends PhaseMitm
 {
-  public PhaseNotCyclic(int[][] cubitgroups)
+  public PhaseNotCyclic(SolvedObject object, int[][] cubitgroups)
     {
+    super(object);
+
     mCubitGroups = cubitgroups;
     mNumGroups = cubitgroups.length;
 
     int numCubits = 0;
     for(int g=0; g<mNumGroups; g++) numCubits += mCubitGroups[g].length;
 
-    mNumCubits = numCubits;
+    mNumCubitsInThisPhase = numCubits;
     }
 }
diff --git a/src/main/java/org/distorted/objectlib/algphases/PhaseRegex.java b/src/main/java/org/distorted/objectlib/algphases/PhaseRegex.java
index f91f0322..7a71f3ba 100644
--- a/src/main/java/org/distorted/objectlib/algphases/PhaseRegex.java
+++ b/src/main/java/org/distorted/objectlib/algphases/PhaseRegex.java
@@ -12,6 +12,7 @@ package org.distorted.objectlib.algphases;
 import org.distorted.objectlib.algsolvers.MitmTable;
 import org.distorted.objectlib.algsolvers.MoveRegex;
 import org.distorted.objectlib.algsolvers.SolvedObject;
+import org.distorted.objectlib.algsolvers.TargetQuats;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -29,6 +30,8 @@ public class PhaseRegex extends Phase
 
   public PhaseRegex(SolvedObject object, String regex, int[][] cubitgroups)
     {
+    super(object);
+
     mCubitGroups = cubitgroups;
     mNumGroups = cubitgroups.length;
     mRegex = new MoveRegex(object,regex);
@@ -38,36 +41,11 @@ public class PhaseRegex extends Phase
     int numCubits = 0;
     for(int g=0; g<mNumGroups; g++) numCubits += mCubitGroups[g].length;
 
-    mNumCubits = numCubits;
+    mNumCubitsInThisPhase = numCubits;
 
     mSolved = new boolean[mNumGroups];
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean quatsMatchEndQuats(int[] quats)
-    {
-    int numQuats = quats.length;
-
-    for(int q=0; q<numQuats; q++)
-      {
-      int[] eq = mEndQuats[q];
-      if( eq!=null && doesntMatch(quats[q],eq) ) return false;
-      }
-
-    return true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean doesntMatch(int quat, int[] endquats)
-    {
-    for(int endquat : endquats)
-      if( quat==endquat ) return false;
-
-    return true;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int findMove(int ax, int row, int angle)
@@ -165,11 +143,10 @@ public class PhaseRegex extends Phase
 
   public void prepareForSubphases()
     {
-    int numEndQuats = mEndQuats.length;
-    mTmp = new int[numEndQuats][];
-    mWhichSubphase = new int[numEndQuats];
+    mTmp = new int[mNumCubits][];
+    mWhichSubphase = new int[mNumCubits];
 
-    for(int c=0; c<numEndQuats; c++) mWhichSubphase[c] = whichSubPhase(c);
+    for(int c=0; c<mNumCubits; c++) mWhichSubphase[c] = whichSubPhase(c);
     for(int s=0; s<mNumGroups; s++) mSolved[s] = false;
     }
 
@@ -186,7 +163,7 @@ public class PhaseRegex extends Phase
       int numMoves = (moveSeq==null ? 0 : moveSeq.length);
       for(int m=1; m<numMoves; m++) object.makeMove(moveSeq[m]);
       int[] qts = object.getCubitQuats();
-      boolean solved = quatsMatchEndQuats(qts);
+      boolean solved = mTargetQuats.matchesQuats(qts);
       object.setUpStartingQuats(quats);
       if( solved ) return simplify(moveSeq);
       moveSeq = mRegex.getNext();
@@ -200,33 +177,33 @@ public class PhaseRegex extends Phase
 
   public boolean solveAnySubphase(SolvedObject object, MitmTable table, int[][] solution, int solIndex)
     {
-    int numQuats = mEndQuats.length;
-    System.arraycopy(mEndQuats, 0, mTmp, 0, numQuats);
+    TargetQuats tmp = new TargetQuats(mTargetQuats);
 
     for(int p=0; p<mNumGroups; p++)
       if( !mSolved[p] )
         {
-        for(int q=0; q<numQuats; q++)
+        for(int q=0; q<mNumCubits; q++)
           {
           // -1 means that the cubit belongs to the previous (or next) phase. Cubits belonging to the
           // next phase already have their endQuats set to null (see PhasedSolver.figureOutEndQuatIndices)
           // Here we set to null (i.e. - mark that we do not care about) the quats of all cubits which
           // belong to the current phase and haven't been solved yet (except the current subphase!).
           int sp = mWhichSubphase[q];
-          mEndQuats[q] = (sp==-1 || sp==p || mSolved[sp]) ? mTmp[q] : null;
+          int[] newValue = (sp==-1 || sp==p || mSolved[sp]) ? mTmp[q] : null;
+          mTargetQuats.setQuat(newValue,q);
           }
 
         solution[solIndex] = findSolution(object,table);
 
         if( solution[solIndex]!=null )
           {
-          System.arraycopy(mTmp, 0, mEndQuats, 0, numQuats);
+          mTargetQuats.set(tmp);
           mSolved[p] = true;
           return true;
           }
         }
 
-    System.arraycopy(mTmp, 0, mEndQuats, 0, numQuats);
+    mTargetQuats.set(tmp);
     return false;
     }
 }
diff --git a/src/main/java/org/distorted/objectlib/algsolvers/MitmTable.java b/src/main/java/org/distorted/objectlib/algsolvers/MitmTable.java
index ab3fd84a..cb8ac8de 100644
--- a/src/main/java/org/distorted/objectlib/algsolvers/MitmTable.java
+++ b/src/main/java/org/distorted/objectlib/algsolvers/MitmTable.java
@@ -124,23 +124,9 @@ public class MitmTable
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void prepareForSubPhase(int[][] endQuats)
+  public void prepareForSubPhase(TargetQuats quats)
     {
-    int numUsedCubits = 0;
-
-    for(int e=0; e<mNumCubits; e++)
-      {
-      if( endQuats[e]!=null )
-        {
-        numUsedCubits++;
-        mUseCubit[e] = true;
-        }
-      else
-        {
-        mUseCubit[e] = false;
-        }
-      }
-
+    int numUsedCubits = quats.computeUsedCubitTable(mUseCubit);
     double numBytes = numUsedCubits*mBitsPerQuat/8.0;
     mBytesPerEntry = (int)Math.ceil(numBytes);  // this many bytes will be needed to store one position
     mBuildDepth = -1;
@@ -169,17 +155,16 @@ public class MitmTable
 // {0} means solution found and it's empty (position already solved)
 // {2,1,3} means the solution is 2 moves long, and the indices of moves to make are 1 and 3.
 
-  public int[] tryReaching(SolvedObject object, int[][] endQuats, int lenb, int lenf)
+  public int[] tryReaching(SolvedObject object, TargetQuats target, int lenb, int lenf)
     {
-    int[] indices = new int[mNumCubits];
-    boolean thereIsNext;
     int branching = object.getBranchingFactor();
     int[] backward = new int[lenb];
     int[] rem = object.getCopiedCubitQuats();
+    int[] quats = target.getFirst();
 
     do
       {
-      object.setUpStartingQuats(endQuats,indices);
+      object.setUpStartingQuats(quats);
       byte[] reached = tryReachingRecursive(object,branching,lenb,backward);
 
       if( reached!=null )
@@ -190,9 +175,9 @@ public class MitmTable
         return joinSequences(forward,numMoves,backward);
         }
 
-      thereIsNext = getNext(endQuats,indices);
+      quats = target.getNext();
       }
-    while( thereIsNext );
+    while( quats!=null );
 
     object.setUpStartingQuats(rem);
     return null;
@@ -337,25 +322,6 @@ public class MitmTable
           }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean getNext(int[][] quats, int[] indices)
-    {
-    for(int i=indices.length-1; i>=0; i--)
-      {
-      int[] q = quats[i];
-      int len = q!=null ? q.length-1 : 0;
-
-      if( indices[i]<len )
-        {
-        indices[i]++;
-        return true;
-        }
-      }
-
-    return false;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Find index'th entry in the table 'output'; it contains 8 bits. Write 'value' to it, but only
 // its 'toBeWritten' first bits (out of the 'remaining' bits), and at offset 'offset' in the table.
diff --git a/src/main/java/org/distorted/objectlib/algsolvers/PhasedSolver.java b/src/main/java/org/distorted/objectlib/algsolvers/PhasedSolver.java
index e9c07474..715df40d 100644
--- a/src/main/java/org/distorted/objectlib/algsolvers/PhasedSolver.java
+++ b/src/main/java/org/distorted/objectlib/algsolvers/PhasedSolver.java
@@ -16,8 +16,6 @@ import org.distorted.objectlib.algphases.Phase;
 
 public class PhasedSolver
 {
-  private static final int[] INDICES = {0};
-
   private final Phase[] mPhases;
   private final SolvedObject mObject;
   private final MitmTable mTable;
@@ -58,29 +56,27 @@ public class PhasedSolver
 
   int[] findSolutionSubphases(Phase phase, int numSubphases)
     {
-    int solIndex=0;
-    int[][] endQuats = phase.getEndQuats();
-    int numEndQuats = endQuats.length;
-    int[][] solution = new int[numEndQuats][];
+    int subPhase=0;
+    int[][] solution = new int[numSubphases][];
 
     phase.prepareForSubphases();
 
-    for(; solIndex<numSubphases; solIndex++)
-      if( !phase.solveAnySubphase(mObject,mTable,solution,solIndex) )
+    for(; subPhase<numSubphases; subPhase++)
+      if( !phase.solveAnySubphase(mObject,mTable,solution,subPhase) )
         {
-        System.out.println("findSolutionSubphases: failed to solve subphase "+solIndex);
+        System.out.println("findSolutionSubphases: failed to solve subphase "+subPhase);
         break;
         }
 
-    if( solIndex>0 )
+    if( subPhase>0 )
       {
       int index=1, num=1;
-      for(int i=0; i<solIndex; i++) num += (solution[i].length-1);
+      for(int i=0; i<subPhase; i++) num += (solution[i].length-1);
 
       int[] ret = new int[num];
       ret[0] = num-1;
 
-      for(int i=0; i<solIndex; i++)
+      for(int i=0; i<subPhase; i++)
         {
         int len = solution[i].length;
         for(int j=1; j<len; j++) ret[index++] = solution[i][j];
@@ -94,42 +90,30 @@ public class PhasedSolver
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int[] figureOutEndQuatIndices(int cubitQuat, int phase, int cubit)
+  private int[] solutionOfPhase(int currentPhase)
     {
-    for(int p=0; p<phase; p++)
-      if( mPhases[p].contains(cubit) ) return INDICES;
-
-    if( mPhases[phase].contains(cubit) )
-      return mPhases[phase].endQuatIndices(mObject,cubitQuat,cubit);
-
-    return null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// quats get overwritten at the end of the phase
-// return null on error
-
-  private int[] solutionOfPhase(int phase, int[] quats)
-    {
-    Phase p = mPhases[phase];
-    int numSubphases = p.numSubphases();
     int numCubits = mPositions.length;
-    int[][] endQuats = new int[numCubits][];
+    boolean[] previousPhases = new boolean[numCubits];
 
     for(int c=0; c<numCubits; c++)
-      {
-      endQuats[c] = figureOutEndQuatIndices(quats[c],phase,c);
-      }
+      for(int p=0; p<currentPhase; p++)
+        if( mPhases[p].contains(c) )
+          {
+          previousPhases[c] = true;
+          break;
+          }
 
-    p.setEndQuats(endQuats);
+    Phase phase = mPhases[currentPhase];
+    int numSubphases = phase.numSubphases();
+    phase.setTargetQuats(mObject,previousPhases);
 
     if( numSubphases>1 )
       {
-      return findSolutionSubphases(p,numSubphases);
+      return findSolutionSubphases(phase,numSubphases);
       }
     else
       {
-      return p.findSolution(mObject,mTable);
+      return phase.findSolution(mObject,mTable);
       }
     }
 
@@ -143,19 +127,19 @@ public class PhasedSolver
     int[][] solution_moves = new int[mNumPhases][];
     prepareForSolution(quats);
 
-    for(int i=0; i<mNumPhases; i++)
+    for(int p=0; p<mNumPhases; p++)
       {
-      solution_moves[i] = solutionOfPhase(i,quats);
+      solution_moves[p] = solutionOfPhase(p);
 
-      if( solution_moves[i]==null )
+      if( solution_moves[p]==null )
         {
-        System.out.println("AlgSolver: error solving phase "+i);
+        System.out.println("AlgSolver: error solving phase "+p);
         break;
         }
       else
         {
         long next = System.currentTimeMillis();
-        System.out.println("AlgSolver: phase "+i+" solution len: "+solution_moves[i][0]+" time: "+(next-millis));
+        System.out.println("AlgSolver: phase "+p+" solution len: "+solution_moves[p][0]+" time: "+(next-millis));
         millis = next;
         }
       }
diff --git a/src/main/java/org/distorted/objectlib/algsolvers/SolvedObject.java b/src/main/java/org/distorted/objectlib/algsolvers/SolvedObject.java
index d70a12d8..5e03463d 100644
--- a/src/main/java/org/distorted/objectlib/algsolvers/SolvedObject.java
+++ b/src/main/java/org/distorted/objectlib/algsolvers/SolvedObject.java
@@ -69,7 +69,6 @@ public class SolvedObject
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void setUpStartingQuats(int[] quats)                  { mCubits.setUpQuats(quats); }
-  public void setUpStartingQuats(int[][] quats, int[] indices) { mCubits.setUpQuats(quats,indices); }
   public void makeMove(int moveIndex)                          { mCubits.makeMove(moveIndex); }
   public void backMove(int moveIndex)                          { mCubits.backMove(moveIndex); }
   public int[] getCubitQuats()                                 { return mCubits.getRotQuats(); }
diff --git a/src/main/java/org/distorted/objectlib/algsolvers/SolvedObjectCubit.java b/src/main/java/org/distorted/objectlib/algsolvers/SolvedObjectCubit.java
index 35c4d950..ae92c019 100644
--- a/src/main/java/org/distorted/objectlib/algsolvers/SolvedObjectCubit.java
+++ b/src/main/java/org/distorted/objectlib/algsolvers/SolvedObjectCubit.java
@@ -119,18 +119,6 @@ class SolvedObjectCubit
     for(int c=0; c<mNumCubits; c++) mRotQuats[c] = quats[c];
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// here we set the indices of cubits we don't care about at the moment to -1.
-
-  void setUpQuats(int[][] quats, int[] indices)
-    {
-    for(int c=0; c<mNumCubits; c++)
-      {
-      int[] q = quats[c];
-      mRotQuats[c] = q!=null ? q[indices[c]] : -1;
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int[] getRotQuats()
diff --git a/src/main/java/org/distorted/objectlib/algsolvers/TargetQuats.java b/src/main/java/org/distorted/objectlib/algsolvers/TargetQuats.java
new file mode 100644
index 00000000..c3ef07fb
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/algsolvers/TargetQuats.java
@@ -0,0 +1,179 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2024 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objectlib.algsolvers;
+
+public class TargetQuats
+{
+  private final int[][] mEndQuats;
+  private final int[] mTmp;
+  private final int[] mIndices;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TargetQuats(int[][] endQuats)
+    {
+    int len = endQuats.length;
+    mEndQuats = endQuats;
+    mTmp = new int[len];
+    mIndices = new int[len];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TargetQuats(TargetQuats source)
+    {
+    int len = source.mEndQuats.length;
+    mEndQuats = new int[len][];
+    System.arraycopy(source.mEndQuats,0,mEndQuats,0,len);
+    mTmp = new int[len];
+    mIndices = new int[len];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String debug()
+    {
+    return MitmTable.getMultiQuatString(mEndQuats);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void set(TargetQuats source)
+    {
+    int len = mEndQuats.length;
+    System.arraycopy(source.mEndQuats,0,mEndQuats,0,len);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setQuat(int[] source, int quat)
+    {
+    mEndQuats[quat] = source;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getQuat(int quat)
+    {
+    return mEndQuats[quat];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// how many combinations are we going to have to start from when reachingBack to the MitmTable?
+
+  public int computeNumCombinations()
+    {
+    int num = 1;
+
+    for( int[] quats : mEndQuats )
+      if( quats!=null ) num *= quats.length;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int computeUsedCubitTable(boolean[] table)
+    {
+    int len = mEndQuats.length;
+    int numUsedCubits = 0;
+
+    for(int e=0; e<len; e++)
+      {
+      if( mEndQuats[e]!=null )
+        {
+        numUsedCubits++;
+        table[e] = true;
+        }
+      else
+        {
+        table[e] = false;
+        }
+      }
+
+    return numUsedCubits;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getFirst()
+    {
+    int len = mEndQuats.length;
+    for(int q=0; q<len; q++) mIndices[q] = 0;
+    return getCurrent();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getNext()
+    {
+    if( incIndices() ) return getCurrent();
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean matchesQuats(int[] quats)
+    {
+    int numQuats = quats.length;
+
+    for(int q=0; q<numQuats; q++)
+      {
+      int[] eq = mEndQuats[q];
+      if( eq!=null && doesntMatch(quats[q],eq) ) return false;
+      }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean incIndices()
+    {
+    for(int i=mIndices.length-1; i>=0; i--)
+      {
+      int[] q = mEndQuats[i];
+      int len = q!=null ? q.length-1 : 0;
+
+      if( mIndices[i]<len )
+        {
+        mIndices[i]++;
+        return true;
+        }
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean doesntMatch(int quat, int[] endquats)
+    {
+    for(int endquat : endquats)
+      if( quat==endquat ) return false;
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] getCurrent()
+    {
+    int len = mEndQuats.length;
+
+    for(int q=0; q<len; q++)
+      {
+      int[] qs = mEndQuats[q];
+      mTmp[q] = (qs==null ? -1 : qs[mIndices[q]]);
+      }
+
+    return mTmp;
+    }
+}
