Project

General

Profile

Download (20.6 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesPruning.java @ ca2ba7a1

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.tablebases;
11

    
12
import org.distorted.objectlib.helpers.OperatingSystemInterface;
13

    
14
import java.io.ByteArrayOutputStream;
15
import java.io.IOException;
16
import java.io.InputStream;
17
import java.util.ArrayList;
18

    
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
public abstract class TablebasesPruning extends TablebasesAbstract
22
{
23
  private final int mGodsNumber;
24

    
25
  private PruningTable[] mHighTables;
26
  private PruningTable[] mMidTables;
27
  private int mLowestHigh, mHighestMid, mLowestMid, mMaxDistance;
28

    
29
///////////////////////////////////////////////////////////////////////////////////////////////////
30

    
31
  abstract int[] getMidPruningLevels();
32
  abstract int[] getHighPruningLevels();
33
  abstract int getGodsNumber();
34
  abstract boolean moveCanProceed(int lastA, int lastL, int currA, int currL);
35

    
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37

    
38
  private PruningTable createPruningTable(OperatingSystemInterface os, int id)
39
    {
40
    InputStream stream = os.openLocalFile(id);
41
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
42

    
43
    int nRead;
44
    byte[] tmp = new byte[16384];
45

    
46
    try
47
      {
48
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
49
        {
50
        buffer.write(tmp, 0, nRead);
51
        }
52
      stream.close();
53
      byte[] data = buffer.toByteArray();
54
      buffer.close();
55
      return new PruningTable(data);
56
      }
57
    catch(IOException ex)
58
      {
59
      mInitialized = false;
60
      return null;
61
      }
62
    }
63

    
64
///////////////////////////////////////////////////////////////////////////////////////////////////
65

    
66
  public TablebasesPruning()
67
    {
68
    super();
69
    mGodsNumber = getGodsNumber();
70
    mInitialized = false;
71
    }
72

    
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74

    
75
  public TablebasesPruning(OperatingSystemInterface os, int[] midIDs, int[] highIDs)
76
    {
77
    super();
78

    
79
    int mid = midIDs !=null ? midIDs.length  : 0;
80
    int high= highIDs!=null ? highIDs.length : 0;
81

    
82
    mMidTables = mid >0 ? new PruningTable[mid] : null;
83
    mHighTables= high>0 ? new PruningTable[high]: null;
84
    mGodsNumber = getGodsNumber();
85
    mInitialized = true;
86

    
87
    for(int i=0; i<mid ; i++) mMidTables[i] = createPruningTable(os,midIDs[i] );
88
    for(int i=0; i<high; i++) mHighTables[i]= createPruningTable(os,highIDs[i]);
89

    
90
    computeLowHigh();
91
    }
92

    
93
///////////////////////////////////////////////////////////////////////////////////////////////////
94

    
95
  public byte[][] getPacked()
96
    {
97
    if( !mInitialized )
98
      {
99
      int[] midLevels = getMidPruningLevels();
100
      int numMidLevels= midLevels!=null ? midLevels.length : 0;
101
      int[] highLevels = getHighPruningLevels();
102
      int numHighLevels= highLevels!=null ? highLevels.length : 0;
103
      int maxLevel = 0;
104

    
105
      if( highLevels!=null )
106
        {
107
        for( int l : highLevels )
108
          if( l>maxLevel ) maxLevel = l;
109
        }
110
      else
111
        {
112
        if( midLevels!=null )
113
          for( int l : midLevels )
114
            if( l>maxLevel ) maxLevel = l;
115
        }
116

    
117
      createTablebase(maxLevel);
118
      mMidTables = numMidLevels >0 ? new PruningTable[numMidLevels ] : null;
119
      mHighTables= numHighLevels>0 ? new PruningTable[numHighLevels] : null;
120

    
121
      for(int i=0; i<numMidLevels; i++)
122
        mMidTables[i] = new PruningTable(mTablebase,midLevels[i]);
123
      for(int i=0; i<numHighLevels; i++)
124
        mHighTables[i] = new PruningTable(mTablebase,highLevels[i]);
125

    
126
      computeLowHigh();
127

    
128
      mInitialized = true;
129
      }
130

    
131
    int midNum  = mMidTables !=null ? mMidTables.length  : 0;
132
    int highNum = mHighTables!=null ? mHighTables.length : 0;
133
    byte[][] data = new byte[midNum+highNum][];
134

    
135
    for(int i=0; i<midNum ; i++) data[i       ] = mMidTables[i].getPacked();
136
    for(int i=0; i<highNum; i++) data[i+midNum] = mHighTables[i].getPacked();
137

    
138
    return data;
139
    }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  private void computeLowHigh()
144
    {
145
    mLowestHigh = mGodsNumber+1;
146
    int numHigh = mHighTables==null ? 0 : mHighTables.length;
147

    
148
    for( int i=0; i<numHigh; i++ )
149
      {
150
      int level = mHighTables[i].getLevel();
151
      if( i==0 || level<mLowestHigh ) mLowestHigh=level;
152
      }
153

    
154
    mLowestMid = 0;
155
    mHighestMid= 0;
156
    int numMid = mMidTables==null ? 0 : mMidTables.length;
157

    
158
    for( int i=0; i<numMid; i++ )
159
      {
160
      int level = mMidTables[i].getLevel();
161
      if( i==0 || level<mLowestMid  ) mLowestMid =level;
162
      if( i==0 || level>mHighestMid ) mHighestMid=level;
163
      }
164

    
165
    int maxHigh = (mLowestHigh-mHighestMid)/2;
166
    int maxMid  = mLowestMid/2;
167

    
168
    mMaxDistance = Math.max(maxHigh,maxMid);
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172

    
173
  public int traverseDownTable(int index, PruningTable[] tables, int tableIndex, int lastA, int lastR, int[] move, int[] tmpQuats)
174
    {
175
    int[] quats = getQuats(index);
176
    int numQuats = quats.length;
177

    
178
    move[0]=0;
179
    move[1]=0;
180
    move[2]=1;
181
    move[3]=1;
182

    
183
    for(int ax=0; ax<mNumAxis; ax++)
184
      for(int cubit=0; cubit<mNumCubits; cubit++)
185
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
186

    
187
    for(int s=0; s<mScalingFactor; s++)
188
      {
189
      int ax    = move[0];
190
      int layer = move[1];
191
      int quat  = move[3];
192

    
193
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
194
        {
195
        int bitLayer = (1<<layer);
196
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
197

    
198
        for(int cubit=0; cubit<mNumCubits; cubit++)
199
          if( mRotRow[cubit][ax]==bitLayer )
200
            {
201
            int currQuat = tmpQuats[cubit];
202
            int newQuat = getMultQuat(quat,currQuat);
203
            tmpQuats[cubit] = newQuat;
204
            }
205

    
206
        int childIndex = getIndex(tmpQuats);
207

    
208
        if( tableIndex>0 )
209
          {
210
          if( tables[tableIndex-1].contains(childIndex) ) return childIndex;
211
          }
212
        else if( !tables[0].contains(childIndex) )
213
          {
214
          if( tables.length<=1 || !tables[1].contains(childIndex) ) return childIndex;
215
          }
216
        }
217

    
218
      getNextAxisLayerAngleQuat(move);
219
      }
220

    
221
    return -1;
222
    }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
  private int traversePruningBlock(int[][] moves, PruningTable[] tables, int tableIndex, int index, int lastA, int lastR)
227
    {
228
    int movesIndex = 1;
229
    int[] move, tmpQuats = new int[mNumCubits];
230

    
231
    do
232
      {
233
      move  = moves[movesIndex++];
234
      index = traverseDownTable(index,tables,tableIndex,lastA,lastR,move,tmpQuats);
235
      lastA = move[0];
236
      lastR = move[1];
237
      tableIndex--;
238
      }
239
    while( tableIndex>=0 );
240

    
241
    return index;
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245
// ret: [0][] --> (new index,new depth,?,?) ; [1...N-1][] --> moves. (or null if index not in High)
246

    
247
  private int[][] traverseBlock(int index, PruningTable[] tables, int lastA, int lastR)
248
    {
249
    int num = (tables==null) ? 0 : tables.length;
250
    int tableIndex = -1;
251

    
252
    for( int i=0; i<num; i++ )
253
      if( tables[i].contains(index) )
254
        {
255
        tableIndex = i;
256
        break;
257
        }
258

    
259
    if( tableIndex<0 ) return null;
260

    
261
    int[][] ret = new int[tableIndex+2][4];
262
    ret[0][1] = tables[0].getLevel()-1;
263
    ret[0][0] = traversePruningBlock(ret,tables,tableIndex,index,lastA, lastR);
264

    
265
    return ret;
266
    }
267

    
268
///////////////////////////////////////////////////////////////////////////////////////////////////
269

    
270
  private int midTablesContain(int index)
271
    {
272
    int num = mMidTables.length;
273

    
274
    for( int i=0; i<num; i++ )
275
      if( mMidTables[i].contains(index) )
276
        return i;
277

    
278
    return -1;
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

    
283
  private boolean jumpMidSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR, int[][] solution)
284
    {
285
    int numQuats  = quats.length;
286
    int[] move    = solution[depth];
287
    int[] tmp     = new int[mNumCubits];
288
    int[][] rotRow= new int[mNumCubits][mNumAxis];
289

    
290
    move[0]=0;
291
    move[1]=0;
292
    move[2]=1;
293
    move[3]=1;
294

    
295
    for(int ax=0; ax<mNumAxis; ax++)
296
      for(int cubit=0; cubit<mNumCubits; cubit++)
297
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
298

    
299
    for(int s=0; s<mScalingFactor; s++)
300
      {
301
      int ax    = move[0];
302
      int layer = move[1];
303
      int quat  = move[3];
304

    
305
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,ax,layer) )
306
        {
307
        int bitLayer = (1<<layer);
308
        System.arraycopy(quats, 0, tmp, 0, numQuats);
309

    
310
        for(int cubit=0; cubit<mNumCubits; cubit++)
311
          if( rotRow[cubit][ax]==bitLayer )
312
            {
313
            int currQuat = tmp[cubit];
314
            int newQuat = getMultQuat(quat,currQuat);
315
            tmp[cubit] = newQuat;
316
            }
317

    
318
        int childIndex = getIndex(tmp);
319

    
320
        if( isSolved(childIndex) )
321
          {
322
          solution[0][0] = childIndex;
323
          solution[0][1] = 0;
324
          return true;
325
          }
326

    
327
        int containingTable = midTablesContain(childIndex);
328

    
329
        if( containingTable>=0 )
330
          {
331
          solution[0][0] = childIndex;
332
          solution[0][1] = mMidTables[containingTable].getLevel();
333
          return true;
334
          }
335

    
336
        if( jump>1 && jumpMidSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution) )
337
          {
338
          return true;
339
          }
340
        }
341

    
342
      getNextAxisLayerAngleQuat(move);
343
      }
344

    
345
    return false;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349
// ret: [0][] --> (new index,new depth,num moves,?) ; [1...N-1][] --> moves.
350

    
351
  private int[][] jumpToMidOrSolved(int index, int maxJump, int lastA, int lastR)
352
    {
353
    if( midTablesContain(index)>=0 ) return null;
354

    
355
    int[][] solution = new int[maxJump+1][4];
356
    int[] quats = getQuats(index);
357

    
358
    for(int i=1; i<=maxJump; i++)
359
      if( jumpMidSolvedRecursive(quats,i,1,lastA,lastR,solution) )
360
        {
361
        solution[0][2] = i;
362
        return solution;
363
        }
364

    
365
    return null;
366
    }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

    
370
  private boolean jumpToSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR, int[][] solution)
371
    {
372
    int numQuats = quats.length;
373
    int[] move   = solution[depth];
374
    int[] tmp    = new int[mNumCubits];
375
    int[][]rotRow= new int[mNumCubits][mNumAxis];
376

    
377
    move[0]=0;
378
    move[1]=0;
379
    move[2]=1;
380
    move[3]=1;
381

    
382
    for(int ax=0; ax<mNumAxis; ax++)
383
      for(int cubit=0; cubit<mNumCubits; cubit++)
384
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
385

    
386
    for(int s=0; s<mScalingFactor; s++)
387
      {
388
      int ax    = move[0];
389
      int layer = move[1];
390
      int quat  = move[3];
391

    
392
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
393
        {
394
        int bitLayer = (1<<layer);
395
        System.arraycopy(quats, 0, tmp, 0, numQuats);
396

    
397
        for(int cubit=0; cubit<mNumCubits; cubit++)
398
          if( rotRow[cubit][ax]==bitLayer )
399
            {
400
            int currQuat = tmp[cubit];
401
            int newQuat = getMultQuat(quat,currQuat);
402
            tmp[cubit] = newQuat;
403
            }
404

    
405
        int childIndex = getIndex(tmp);
406
        if( isSolved(childIndex) ) return true;
407
        if( jump>1 && jumpToSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution) ) return true;
408
        }
409

    
410
      getNextAxisLayerAngleQuat(move);
411
      }
412

    
413
    return false;
414
    }
415

    
416
///////////////////////////////////////////////////////////////////////////////////////////////////
417
// ret: [0][] --> (numMoves,old depth,?,?) ; [1...N-1][] --> moves.
418

    
419
  private int[][] jumpToSolved(int index, int maxJump, int lastA, int lastR)
420
    {
421
    int[][] solution = new int[maxJump+1][4];
422
    int[] quats = getQuats(index);
423

    
424
    for(int i=1; i<=maxJump; i++)
425
      if( jumpToSolvedRecursive(quats,i,1,lastA,lastR,solution) )
426
        {
427
        solution[0][0] = i;
428
        return solution;
429
        }
430

    
431
    return null;
432
    }
433

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435

    
436
  private int[][] concatenateMoves(int[][] high, int[][] jump1, int[][] mid, int[][] jump2)
437
    {
438
    int len1 = high ==null ? 0 : high.length-1;
439
    int len2 = jump1==null ? 0 : jump1[0][2];
440
    int len3 = mid  ==null ? 0 : mid.length-1;
441
    int len4 = jump2==null ? 0 : jump2[0][0];
442

    
443
    int[][] moves = new int[len1+len2+len3+len4][];
444
    int index = 0;
445

    
446
    for(int i=0; i<len1; i++) moves[index++] =  high[i+1];
447
    for(int i=0; i<len2; i++) moves[index++] = jump1[i+1];
448
    for(int i=0; i<len3; i++) moves[index++] =   mid[i+1];
449
    for(int i=0; i<len4; i++) moves[index++] = jump2[i+1];
450

    
451
    convertMoves(moves);
452

    
453
    return moves;
454
    }
455

    
456
///////////////////////////////////////////////////////////////////////////////////////////////////
457

    
458
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
459
    {
460
    if( isSolved(index) ) return null;
461
    int lastA=-1, lastR=0;
462
    int[][] highMoves = traverseBlock(index,mHighTables,lastA,lastR);
463

    
464
    if( highMoves!=null )
465
      {
466
      index = highMoves[0][0];
467
      int len = highMoves.length;
468
      lastA = highMoves[len-1][0];
469
      lastR = highMoves[len-1][1];
470
      }
471

    
472
    int maxJump = Math.max(mLowestHigh-1-mHighestMid,mLowestMid/2);
473
    int[][] jump1Moves = jumpToMidOrSolved(index,maxJump,lastA,lastR);
474

    
475
    if( jump1Moves!=null )
476
      {
477
      if( isSolved(jump1Moves[0][0]) )
478
        {
479
        return concatenateMoves(null,jump1Moves,null,null);
480
        }
481
      if( jump1Moves[0][1]==mLowestMid )
482
        {
483
        int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,-1,0);
484
        if( jump2Moves==null )
485
          {
486
          if( osi!=null ) osi.reportError("1 error jumping to Solved: "+index);
487
          return null;
488
          }
489
        return concatenateMoves(null,null,null,jump2Moves);
490
        }
491

    
492
      index = jump1Moves[0][0];
493
      int len = jump1Moves[0][2];
494
      lastA = jump1Moves[len][0];
495
      lastR = jump1Moves[len][1];
496
      }
497

    
498
    int[][] midMoves = traverseBlock(index,mMidTables,lastA,lastR);
499
    if( midMoves!=null )
500
      {
501
      index = midMoves[0][0];
502
      int len = midMoves.length;
503
      lastA = midMoves[len-1][0];
504
      lastR = midMoves[len-1][1];
505
      }
506
    else
507
      {
508
      if( osi!=null ) osi.reportError("error traversing mid Tables: "+index);
509
      return null;
510
      }
511
    int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,lastA,lastR);
512
    if( jump2Moves==null )
513
      {
514
      if( osi!=null ) osi.reportError("2 error jumping to Solved: "+index);
515
      return null;
516
      }
517
    return concatenateMoves(highMoves,jump1Moves,midMoves,jump2Moves);
518
    }
519
/*
520
///////////////////////////////////////////////////////////////////////////////////////////////////
521

    
522
  private int getDistanceSingle(int[] quats)
523
    {
524
    int index = getIndex(quats);
525

    
526
    if( isSolved(index) ) return 0;
527

    
528
    int numMid = mMidTables.length;
529
    for(int i=0; i<numMid; i++)
530
      if( mMidTables[i].contains(index) ) return mLowestMid+i;
531

    
532
    if( mHighTables!=null )
533
      {
534
      int numHigh = mHighTables.length;
535
      for(int i=0; i<numHigh; i++)
536
        if( mHighTables[i].contains(index) )
537
          {
538
          //android.util.Log.e("D", "high tables contain "+index);
539
          return mLowestHigh+i;
540
          }
541

    
542
      //android.util.Log.e("D", "high tables do not contain "+index);
543
      }
544

    
545
    return -1;
546
    }
547

    
548
///////////////////////////////////////////////////////////////////////////////////////////////////
549

    
550
  private int getDistance(int[] quats, int depth)
551
    {
552
    if( depth==0 ) return getDistanceSingle(quats);
553

    
554
    final int MAX = 1000;
555
    int[] data = new int[4];
556
    int numQuats = quats.length;
557
    int[] tmpQuats = new int[numQuats];
558
    int[][] rotRow = new int[mNumCubits][mNumAxis];
559
    int ret = MAX;
560

    
561
    data[0]=0;
562
    data[1]=0;
563
    data[2]=1;
564
    data[3]=1;
565

    
566
    for(int ax=0; ax<mNumAxis; ax++)
567
      for(int cubit=0; cubit<mNumCubits; cubit++)
568
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
569

    
570
    for(int s=0; s<mScalingFactor; s++)
571
      {
572
      int ax    = data[0];
573
      int layer = data[1];
574
      int quat  = data[3];
575

    
576
      if( mRotatable[ax][layer] )
577
        {
578
        int bitLayer = (1<<layer);
579
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
580

    
581
        for(int cubit=0; cubit<mNumCubits; cubit++)
582
          if( rotRow[cubit][ax]==bitLayer )
583
            {
584
            int currQuat = tmpQuats[cubit];
585
            int newQuat = getMultQuat(quat,currQuat);
586
            tmpQuats[cubit] = newQuat;
587
            }
588

    
589
//android.util.Log.e("D", "getDistance: trying ax="+ax+" layer="+layer+" angle="+data[2]+" quat="+quat);
590
//TablebaseHelpers.displayTable(tmpQuats, "tmpQuats");
591

    
592
        int dist = getDistance(tmpQuats,depth-1);
593

    
594
//android.util.Log.e("D", "dist="+dist);
595

    
596
        if( dist>=0 && dist<ret ) ret=dist;
597
        }
598

    
599
      getNextAxisLayerAngleQuat(data);
600
      }
601

    
602
    return ret<MAX ? ret : -1;
603
    }
604

    
605
///////////////////////////////////////////////////////////////////////////////////////////////////
606

    
607
  private int getDepth(int[] quats, OperatingSystemInterface osi)
608
    {
609
    for(int d=0; d<=mMaxDistance; d++)
610
      {
611
//TablebaseHelpers.displayTable(quats, "quats, d="+d);
612

    
613
      int res = getDistance(quats,d);
614

    
615
//osi.reportError("getDistance res="+res+" d="+d);
616

    
617
      if( res>=0 )
618
        {
619
        if( res==0 ) return d;
620
        if( res==mLowestMid ) return mLowestMid-d;
621
        if( res>mLowestMid && res<mHighestMid )
622
          {
623
          if( d>0 )
624
            {
625
            osi.reportError("1 IMPOSSIBLE!!");
626
            return -1;
627
            }
628
          else return res;
629
          }
630
        if( res==mHighestMid ) return mHighestMid+d;
631
        if( res==mLowestHigh ) return mLowestHigh-d;
632
        if( res >mLowestHigh )
633
          {
634
          if( d>0 )
635
            {
636
            osi.reportError("2 IMPOSSIBLE!!");
637
            return -1;
638
            }
639
          else return res;
640
          }
641
        }
642
      }
643

    
644
    int index=getIndex(quats);
645
    osi.reportError("3 IMPOSSIBLE "+index);
646
    return -1;
647
    }
648

    
649
///////////////////////////////////////////////////////////////////////////////////////////////////
650

    
651
  public int[][] solutionNew(int index, int[] extra, OperatingSystemInterface osi)
652
    {
653
    int[] data = new int[4];
654
    ArrayList<int[]> moves = new ArrayList<>();
655
    int[] quats = getQuats(index);
656
    int depth = getDepth(quats,osi);
657
    int numQuats = quats.length;
658
    int[] tmpQuats = new int[numQuats];
659

    
660
    while( depth>0 )
661
      {
662
      boolean found = false;
663

    
664
      data[0]=0;
665
      data[1]=0;
666
      data[2]=1;
667
      data[3]=1;
668

    
669
      for(int ax=0; ax<mNumAxis; ax++)
670
        for(int cubit=0; cubit<mNumCubits; cubit++)
671
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
672

    
673
      for(int s=0; s<mScalingFactor && !found; s++)
674
        {
675
        int ax    = data[0];
676
        int layer = data[1];
677
        int angle = data[2];
678
        int quat  = data[3];
679

    
680
        if( mRotatable[ax][layer] )
681
          {
682
          int bitLayer = (1<<layer);
683
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
684

    
685
          for(int cubit=0; cubit<mNumCubits; cubit++)
686
            if( mRotRow[cubit][ax]==bitLayer )
687
              {
688
              int currQuat = tmpQuats[cubit];
689
              int newQuat = getMultQuat(quat,currQuat);
690
              tmpQuats[cubit] = newQuat;
691
              }
692

    
693
          int newDepth = getDepth(tmpQuats,osi);
694

    
695
          if( newDepth==depth-1 )
696
            {
697
            int[] tmpMove = newMove(ax,layer,angle);
698
            moves.add(tmpMove);
699
            quats = new int[numQuats];
700
            System.arraycopy(tmpQuats, 0, quats, 0, numQuats);
701
            depth = newDepth;
702
            found = true;
703
            }
704
          }
705

    
706
        getNextAxisLayerAngleQuat(data);
707
        }
708
      }
709

    
710
    int[][] ret = convertMovesFromArray(moves);
711

    
712
    return extraInfo(ret,extra);
713
    }
714

    
715
 */
716
}
(19-19/19)