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 java.util.Random;
|
13
|
|
14
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
15
|
|
16
|
public class TablebaseHelpers
|
17
|
{
|
18
|
|
19
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
20
|
|
21
|
public static void displayTable(int[] table, String marker)
|
22
|
{
|
23
|
StringBuilder sb = new StringBuilder();
|
24
|
|
25
|
for( int t : table )
|
26
|
{
|
27
|
sb.append(' ');
|
28
|
sb.append(t);
|
29
|
}
|
30
|
|
31
|
android.util.Log.e("D", marker+" : "+sb);
|
32
|
}
|
33
|
|
34
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
35
|
|
36
|
private static String print(int[][] move)
|
37
|
{
|
38
|
StringBuilder sb = new StringBuilder();
|
39
|
|
40
|
if( move==null )
|
41
|
{
|
42
|
return ("move NULL!!");
|
43
|
}
|
44
|
|
45
|
for (int[] ints : move)
|
46
|
{
|
47
|
sb.append(' ');
|
48
|
sb.append('(');
|
49
|
sb.append(ints[0]);
|
50
|
sb.append(' ');
|
51
|
sb.append(ints[1]);
|
52
|
sb.append(' ');
|
53
|
sb.append(ints[2]);
|
54
|
sb.append(')');
|
55
|
}
|
56
|
|
57
|
return sb.toString();
|
58
|
}
|
59
|
|
60
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
61
|
|
62
|
public static void test(TablebasesAbstract tb)
|
63
|
{
|
64
|
int size = tb.getSize();
|
65
|
|
66
|
for(int i=0; i<size; i++)
|
67
|
{
|
68
|
boolean success = tb.test(i);
|
69
|
|
70
|
if( (!success && i>0) || (success && i==0) )
|
71
|
{
|
72
|
android.util.Log.e("D", "--------> FAILED: "+i);
|
73
|
break;
|
74
|
}
|
75
|
|
76
|
if( (i%1000)==0 ) android.util.Log.e("D", "trying "+i);
|
77
|
}
|
78
|
}
|
79
|
|
80
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
81
|
|
82
|
private final static int MAX_PERM=12; // biggest N for which N! is still less than 2^32
|
83
|
private final static int[] avail = new int[MAX_PERM];
|
84
|
private final static int[] pos = new int[MAX_PERM];
|
85
|
private final static int[] fact = new int[MAX_PERM];
|
86
|
|
87
|
static
|
88
|
{
|
89
|
fact[0] = 1;
|
90
|
for(int i=1; i<MAX_PERM; i++) fact[i] = fact[i-1]*i;
|
91
|
}
|
92
|
|
93
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
94
|
|
95
|
public static void getPermutationFromNum(int[] buffer,int permSize, int permNum)
|
96
|
{
|
97
|
avail[0] = pos[0] = 0;
|
98
|
for(int i=1; i<permSize; i++) avail[i] = pos[i] = permSize-i;
|
99
|
|
100
|
for(int i=0; i<permSize-1; i++)
|
101
|
{
|
102
|
int m = fact[permSize-1-i];
|
103
|
int k = permNum/m;
|
104
|
permNum %= m;
|
105
|
buffer[i] = avail[k];
|
106
|
int y = avail[permSize-1-i];
|
107
|
pos[y] = k;
|
108
|
avail[k] = y;
|
109
|
}
|
110
|
|
111
|
buffer[permSize-1] = avail[0];
|
112
|
}
|
113
|
|
114
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
115
|
// Enumeration of permutations in linear time.
|
116
|
// 1) notice that 'avail' and 'pos' are always inverses: avail[m]=n iff pos[n]=m
|
117
|
// 2) notice that that implies that after the i-th execution of the loop, avail[0 ... n-1-i]
|
118
|
// contains the n-i elements of the permutation we haven't scanned yet
|
119
|
// ( that is because this is true initially, and during each loop, we remove avail[k] from
|
120
|
// this list - but k = pos[buffer[i]] implies buffer[i] = avail[k] so we are really removing
|
121
|
// buffer[i] - i.e. exactly the permutation element we have just scanned! )
|
122
|
// 3) each 'k' is a digit of the permutation in a strange ( n-1,n-2,...,2 ) notation where the most
|
123
|
// significant digit can be one of the {0...,n-1}, the next most significant - one of the {0,...,n-2},
|
124
|
// etc.
|
125
|
// 4) the ret = ret*(permSize-i) + k is a transform from this ( n-1,n-2,...,2 ) notation to an index.
|
126
|
//
|
127
|
// 'avail' is initially set to {0,n-1,n-2,...,2,1} because that implies that an 'empty' permutation
|
128
|
// gets mapped to index=0.
|
129
|
|
130
|
public static int computePermutationNum(int[] buffer)
|
131
|
{
|
132
|
int permSize = buffer.length;
|
133
|
avail[0] = pos[0] = 0;
|
134
|
for(int i=1; i<permSize; i++) avail[i] = pos[i] = permSize-i;
|
135
|
|
136
|
int ret = 0;
|
137
|
|
138
|
for(int i=0; i<permSize-1; i++)
|
139
|
{
|
140
|
int k = pos[buffer[i]];
|
141
|
ret = ret*(permSize-i) + k;
|
142
|
int y = avail[permSize-1-i];
|
143
|
pos[y] = k;
|
144
|
avail[k] = y;
|
145
|
}
|
146
|
|
147
|
return ret;
|
148
|
}
|
149
|
|
150
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
151
|
|
152
|
public static int computeEvenPermutationNum(int[] buffer)
|
153
|
{
|
154
|
int index = computePermutationNum(buffer);
|
155
|
return index/2;
|
156
|
}
|
157
|
|
158
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
159
|
|
160
|
public static void getEvenPermutationFromNum(int[] buffer, int permNum)
|
161
|
{
|
162
|
getPermutationFromNum(buffer, buffer.length, 2*permNum);
|
163
|
boolean even = permutationIsEven(buffer);
|
164
|
if( !even ) getPermutationFromNum(buffer, buffer.length, 2*permNum+1 );
|
165
|
}
|
166
|
|
167
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
168
|
|
169
|
private static int swaps(int val, int[] buffer, int len)
|
170
|
{
|
171
|
int ret = 0;
|
172
|
|
173
|
for(int i=0; i<len; i++)
|
174
|
{
|
175
|
if( buffer[i] >val ) ret++;
|
176
|
else if( buffer[i]==val ) return ret;
|
177
|
}
|
178
|
|
179
|
return -1;
|
180
|
}
|
181
|
|
182
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
183
|
|
184
|
public static boolean permutationIsEven(int[] buffer)
|
185
|
{
|
186
|
int len = buffer.length;
|
187
|
int numOfSwaps = 0;
|
188
|
for(int i=0; i<len-1; i++) numOfSwaps += swaps(i,buffer,len);
|
189
|
return (numOfSwaps%2==0);
|
190
|
}
|
191
|
|
192
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
193
|
|
194
|
public static void invertPermutation(int[] perm, int[] output)
|
195
|
{
|
196
|
int len = perm.length;
|
197
|
for(int i=0; i<len; i++) output[i] = -1;
|
198
|
for(int i=0; i<len; i++) output[perm[i]] = i;
|
199
|
}
|
200
|
}
|