aboutsummaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/arch.h41
-rw-r--r--src/arch/avx2.h319
-rw-r--r--src/arch/common.h19
-rw-r--r--src/arch/neon.h348
-rw-r--r--src/arch/portable.h272
5 files changed, 999 insertions, 0 deletions
diff --git a/src/arch/arch.h b/src/arch/arch.h
new file mode 100644
index 0000000..d5e0218
--- /dev/null
+++ b/src/arch/arch.h
@@ -0,0 +1,41 @@
1#if defined(CUBE_AVX2)
2
3#include <immintrin.h>
4
5typedef __m256i cube_t;
6
7#if !defined(TEST_H)
8#include "common.h"
9#include "avx2.h"
10#endif
11
12#elif defined(CUBE_NEON)
13
14#include <stdlib.h>
15#include <arm_neon.h>
16
17typedef struct {
18 uint8x16_t corner;
19 uint8x16_t edge;
20} cube_t;
21
22#if !defined(TEST_H)
23#include "common.h"
24#include "neon.h"
25#endif
26
27#else
28
29#include <stdlib.h>
30
31typedef struct {
32 uint8_t corner[8];
33 uint8_t edge[12];
34} cube_t;
35
36#if !defined(TEST_H)
37#include "common.h"
38#include "portable.h"
39#endif
40
41#endif
diff --git a/src/arch/avx2.h b/src/arch/avx2.h
new file mode 100644
index 0000000..59a0d6c
--- /dev/null
+++ b/src/arch/avx2.h
@@ -0,0 +1,319 @@
1#define _co2_avx2 _mm256_set_epi64x(0, 0, 0, INT64_C(0x6060606060606060))
2#define _cocw_avx2 _mm256_set_epi64x(0, 0, 0, INT64_C(0x2020202020202020))
3#define _cp_avx2 _mm256_set_epi64x(0, 0, 0, INT64_C(0x0707070707070707))
4#define _ep_avx2 \
5 _mm256_set_epi64x(INT64_C(0x0F0F0F0F), INT64_C(0x0F0F0F0F0F0F0F0F), 0, 0)
6#define _eo_avx2 \
7 _mm256_set_epi64x(INT64_C(0x10101010), INT64_C(0x1010101010101010), 0, 0)
8
9#define static_cube(c_ufr, c_ubl, c_dfl, c_dbr, c_ufl, c_ubr, c_dfr, c_dbl, \
10 e_uf, e_ub, e_db, e_df, e_ur, e_ul, e_dl, e_dr, e_fr, e_fl, e_bl, e_br) \
11 _mm256_set_epi8(0, 0, 0, 0, e_br, e_bl, e_fl, e_fr, \
12 e_dr, e_dl, e_ul, e_ur, e_df, e_db, e_ub, e_uf, \
13 0, 0, 0, 0, 0, 0, 0, 0, \
14 c_dbl, c_dfr, c_ubr, c_ufl, c_dbr, c_dfl, c_ubl, c_ufr)
15#define zero _mm256_set_epi64x(0, 0, 0, 0)
16#define solved static_cube( \
17 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
18
19_static void
20pieces(cube_t *cube, uint8_t c[static 8], uint8_t e[static 12])
21{
22 uint8_t aux[32];
23
24 _mm256_storeu_si256((__m256i_u *)aux, *cube);
25 memcpy(c, aux, 8);
26 memcpy(e, aux+16, 12);
27}
28
29_static_inline bool
30equal(cube_t c1, cube_t c2)
31{
32 int32_t mask;
33 __m256i cmp;
34
35 cmp = _mm256_cmpeq_epi8(c1, c2);
36 mask = _mm256_movemask_epi8(cmp);
37
38 return mask == ~0;
39}
40
41_static_inline cube_t
42invertco(cube_t c)
43{
44 cube_t co, shleft, shright, summed, newco, cleanco, ret;
45
46 co = _mm256_and_si256(c, _co2_avx2);
47 shleft = _mm256_slli_epi32(co, 1);
48 shright = _mm256_srli_epi32(co, 1);
49 summed = _mm256_or_si256(shleft, shright);
50 newco = _mm256_and_si256(summed, _co2_avx2);
51 cleanco = _mm256_xor_si256(c, co);
52 ret = _mm256_or_si256(cleanco, newco);
53
54 return ret;
55}
56
57_static_inline cube_t
58compose_epcpeo(cube_t c1, cube_t c2)
59{
60 cube_t b, s, eo2;
61
62 /* Permute and clean unused bits */
63 s = _mm256_shuffle_epi8(c1, c2);
64 b = _mm256_set_epi8(
65 ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0
67 );
68 s = _mm256_andnot_si256(b, s);
69
70 /* Change EO */
71 eo2 = _mm256_and_si256(c2, _eo_avx2);
72 s = _mm256_xor_si256(s, eo2);
73
74 return s;
75}
76
77_static_inline cube_t
78compose_edges(cube_t c1, cube_t c2)
79{
80 return compose_epcpeo(c1, c2);
81}
82
83_static_inline cube_t
84compose_corners(cube_t c1, cube_t c2)
85{
86 /*
87 * We do a full compose. Minor optimizations are possible, like
88 * saving one instruction by not doing EO, but it should not
89 * be significant.
90 */
91 return compose(c1, c2);
92}
93
94_static_inline cube_t
95compose(cube_t c1, cube_t c2)
96{
97 cube_t s, co1, co2, aux, auy1, auy2, auz1, auz2;
98
99 s = compose_epcpeo(c1, c2);
100
101 /* Change CO */
102 co1 = _mm256_and_si256(s, _co2_avx2);
103 co2 = _mm256_and_si256(c2, _co2_avx2);
104 aux = _mm256_add_epi8(co1, co2);
105 auy1 = _mm256_add_epi8(aux, _cocw_avx2);
106 auy2 = _mm256_srli_epi32(auy1, 2);
107 auz1 = _mm256_add_epi8(aux, auy2);
108 auz2 = _mm256_and_si256(auz1, _co2_avx2);
109
110 /* Put together */
111 s = _mm256_andnot_si256(_co2_avx2, s);
112 s = _mm256_or_si256(s, auz2);
113
114 return s;
115}
116
117_static_inline cube_t
118cleanaftershuffle(cube_t c)
119{
120 __m256i b;
121
122 b = _mm256_set_epi8(
123 ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0
125 );
126
127 return _mm256_andnot_si256(b, c);
128}
129
130_static_inline cube_t
131inverse(cube_t c)
132{
133 /* Method taken from Andrew Skalski's vcube[1]. The addition sequence
134 * was generated using [2].
135 * [1] https://github.com/Voltara/vcube
136 * [2] http://wwwhomes.uni-bielefeld.de/achim/addition_chain.html
137 */
138 cube_t v3, vi, vo, vp, ret;
139
140 v3 = _mm256_shuffle_epi8(c, c);
141 v3 = _mm256_shuffle_epi8(v3, c);
142 vi = _mm256_shuffle_epi8(v3, v3);
143 vi = _mm256_shuffle_epi8(vi, vi);
144 vi = _mm256_shuffle_epi8(vi, vi);
145 vi = _mm256_shuffle_epi8(vi, v3);
146 vi = _mm256_shuffle_epi8(vi, vi);
147 vi = _mm256_shuffle_epi8(vi, vi);
148 vi = _mm256_shuffle_epi8(vi, vi);
149 vi = _mm256_shuffle_epi8(vi, vi);
150 vi = _mm256_shuffle_epi8(vi, c);
151 vi = _mm256_shuffle_epi8(vi, vi);
152 vi = _mm256_shuffle_epi8(vi, vi);
153 vi = _mm256_shuffle_epi8(vi, vi);
154 vi = _mm256_shuffle_epi8(vi, vi);
155 vi = _mm256_shuffle_epi8(vi, vi);
156 vi = _mm256_shuffle_epi8(vi, v3);
157 vi = _mm256_shuffle_epi8(vi, vi);
158 vi = _mm256_shuffle_epi8(vi, c);
159
160 vo = _mm256_and_si256(c, _mm256_or_si256(_eo_avx2, _co2_avx2));
161 vo = _mm256_shuffle_epi8(vo, vi);
162 vp = _mm256_andnot_si256(_mm256_or_si256(_eo_avx2, _co2_avx2), vi);
163 ret = _mm256_or_si256(vp, vo);
164 ret = cleanaftershuffle(ret);
165
166 return invertco(ret);
167}
168
169_static_inline int64_t
170coord_co(cube_t c)
171{
172 cube_t co;
173 int64_t mem[4], ret, i, p;
174
175 co = _mm256_and_si256(c, _co2_avx2);
176 _mm256_storeu_si256((__m256i *)mem, co);
177
178 mem[0] >>= 5;
179 for (i = 0, ret = 0, p = 1; i < 7; i++, mem[0] >>= 8, p *= 3)
180 ret += (mem[0] & 3) * p;
181
182 return ret;
183}
184
185_static_inline int64_t
186coord_csep(cube_t c)
187{
188 cube_t cp, shifted;
189 int64_t mask;
190
191 cp = _mm256_and_si256(c, _cp_avx2);
192 shifted = _mm256_slli_epi32(cp, 5);
193 mask = _mm256_movemask_epi8(shifted);
194
195 return mask & 0x7F;
196}
197
198_static_inline int64_t
199coord_cocsep(cube_t c)
200{
201 return (coord_co(c) << 7) + coord_csep(c);
202}
203
204_static_inline int64_t
205coord_eo(cube_t c)
206{
207 cube_t eo, shifted;
208 int64_t mask;
209
210 eo = _mm256_and_si256(c, _eo_avx2);
211 shifted = _mm256_slli_epi32(eo, 3);
212 mask = _mm256_movemask_epi8(shifted);
213
214 return mask >> 17;
215}
216
217_static_inline int64_t
218coord_esep(cube_t c)
219{
220 cube_t ep;
221 int64_t e, mem[4], i, j, jj, k, l, ret1, ret2, bit1, bit2, is1;
222
223 ep = _mm256_and_si256(c, _ep_avx2);
224 _mm256_storeu_si256((__m256i *)mem, ep);
225
226 mem[3] <<= 8;
227 ret1 = ret2 = 0;
228 k = l = 4;
229 for (i = 0, j = 0; i < 12; i++, mem[i/8 + 2] >>= 8) {
230 e = mem[i/8 + 2];
231
232 bit1 = (e & _esepbit1) >> 2;
233 bit2 = (e & _esepbit2) >> 3;
234 is1 = (1 - bit2) * bit1;
235
236 ret1 += bit2 * binomial[11-i][k];
237 k -= bit2;
238
239 jj = j < 8;
240 ret2 += jj * is1 * binomial[7-(j*jj)][l];
241 l -= is1;
242 j += (1-bit2);
243 }
244
245 return ret1 * 70 + ret2;
246}
247
248_static_inline void
249copy_corners(cube_t *dest, cube_t src)
250{
251 *dest = _mm256_blend_epi32(*dest, src, 0x0F);
252}
253
254_static_inline void
255copy_edges(cube_t *dest, cube_t src)
256{
257 *dest = _mm256_blend_epi32(*dest, src, 0xF0);
258}
259
260_static_inline void
261set_eo(cube_t *cube, int64_t eo)
262{
263 int64_t eo12, eotop, eobot;
264 __m256i veo;
265
266 eo12 = (eo << 1) + (_mm_popcnt_u64(eo) % 2);
267 eotop = (eo12 & (1 << 11)) << 17 |
268 (eo12 & (1 << 10)) << 10 |
269 (eo12 & (1 << 9)) << 3 |
270 (eo12 & (1 << 8)) >> 4;
271 eobot = (eo12 & (1 << 7)) << 53 |
272 (eo12 & (1 << 6)) << 46 |
273 (eo12 & (1 << 5)) << 39 |
274 (eo12 & (1 << 4)) << 32 |
275 (eo12 & (1 << 3)) << 25 |
276 (eo12 & (1 << 2)) << 18 |
277 (eo12 & (1 << 1)) << 11 |
278 (eo12 & 1) << 4;
279 veo = _mm256_set_epi64x(eotop, eobot, 0, 0);
280
281 *cube = _mm256_andnot_si256(_eo_avx2, *cube);
282 *cube = _mm256_or_si256(*cube, veo);
283}
284
285_static_inline cube_t
286invcoord_esep(int64_t esep)
287{
288 cube_t eee, ret;
289 int64_t bit1, bit2, i, j, jj, k, l, s, v, w, is1, set1, set2;
290 uint8_t mem[32];
291 uint8_t slice[3] = {0};
292
293 set1 = esep % 70;
294 set2 = esep / 70;
295
296 for (i = 0, j = 0, k = 4, l = 4; i < 12; i++) {
297 v = binomial[11-i][k];
298 jj = j < 8;
299 w = jj * binomial[7-(j*jj)][l];
300 bit2 = set2 >= v;
301 bit1 = set1 >= w;
302 is1 = (1 - bit2) * bit1;
303
304 set2 -= bit2 * v;
305 k -= bit2;
306 set1 -= is1 * w;
307 l -= is1;
308 j += (1-bit2);
309 s = 2*bit2 + (1-bit2)*bit1;
310
311 mem[i+16] = (slice[s]++) | (uint8_t)(s << 2);
312 }
313
314 ret = solved;
315 eee = _mm256_loadu_si256((__m256i_u *)&mem);
316 copy_edges(&ret, eee);
317
318 return ret;
319}
diff --git a/src/arch/common.h b/src/arch/common.h
new file mode 100644
index 0000000..cd2d36e
--- /dev/null
+++ b/src/arch/common.h
@@ -0,0 +1,19 @@
1_static void pieces(cube_t *, uint8_t [static 8], uint8_t [static 12]);
2_static_inline bool equal(cube_t, cube_t);
3_static_inline cube_t invertco(cube_t);
4_static_inline cube_t compose_epcpeo(cube_t, cube_t);
5_static_inline cube_t compose_edges(cube_t, cube_t);
6_static_inline cube_t compose_corners(cube_t, cube_t);
7_static_inline cube_t compose(cube_t, cube_t);
8_static_inline cube_t inverse(cube_t);
9
10_static_inline int64_t coord_co(cube_t);
11_static_inline int64_t coord_csep(cube_t);
12_static_inline int64_t coord_cocsep(cube_t);
13_static_inline int64_t coord_eo(cube_t);
14_static_inline int64_t coord_esep(cube_t);
15
16_static_inline void copy_corners(cube_t *, cube_t);
17_static_inline void copy_edges(cube_t *, cube_t);
18_static_inline void set_eo(cube_t *, int64_t);
19_static_inline cube_t invcoord_esep(int64_t);
diff --git a/src/arch/neon.h b/src/arch/neon.h
new file mode 100644
index 0000000..a75f86d
--- /dev/null
+++ b/src/arch/neon.h
@@ -0,0 +1,348 @@
1#define _co2_neon vdupq_n_u8(0x60)
2#define _cocw_neon vdupq_n_u8(0x20)
3#define _cp_neon vdupq_n_u8(0x07)
4#define _ep_neon vcombine_u8(vdupq_n_u8(0x0F), vdupq_n_u8(0x0F))
5#define _eo_neon vcombine_u8(vdupq_n_u8(0x10), vdupq_n_u8(0x10))
6
7// static cube
8#define static_cube(c_ufr, c_ubl, c_dfl, c_dbr, c_ufl, c_ubr, c_dfr, c_dbl, \
9 e_uf, e_ub, e_db, e_df, e_ur, e_ul, e_dl, e_dr, e_fr, e_fl, e_bl, e_br) \
10 ((cube_t){ \
11 .corner = {c_ufr, c_ubl, c_dfl, c_dbr, c_ufl, c_ubr, c_dfr, c_dbl, 0, 0, 0, 0, 0, 0, 0, 0}, \
12 .edge = {e_uf, e_ub, e_db, e_df, e_ur, e_ul, e_dl, e_dr, e_fr, e_fl, e_bl, e_br, 0, 0, 0, 0}})
13
14// zero cube
15#define zero \
16 (cube_t) \
17 { \
18 .corner = vdupq_n_u8(0), \
19 .edge = vdupq_n_u8(0) \
20 }
21
22// solved cube
23#define solved static_cube( \
24 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
25
26_static void
27pieces(cube_t *cube, uint8_t c[static 8], uint8_t e[static 12])
28{
29 // First 8 bytes of the corner vector are copied from the c array
30 vst1_u8(c, vget_low_u8(cube->corner));
31
32 // 12 bytes of the edge vector are copied from the e array
33 // First 8 bytes
34 vst1_u8(e, vget_low_u8(cube->edge));
35 // Next 4 bytes
36 vst1_lane_u32((uint32_t *)(e + 8), vreinterpret_u32_u8(vget_high_u8(cube->edge)), 0);
37}
38
39_static_inline bool
40equal(cube_t c1, cube_t c2)
41{
42 uint8x16_t cmp_corner, cmp_edge;
43 uint64x2_t cmp_corner_u64, cmp_edge_u64;
44 uint64x2_t cmp_result;
45
46 // compare the corner vectors
47 cmp_corner = vceqq_u8(c1.corner, c2.corner);
48 // compare the edge vectors
49 cmp_edge = vceqq_u8(c1.edge, c2.edge);
50
51 // convert the comparison vectors to 64-bit vectors
52 cmp_corner_u64 = vreinterpretq_u64_u8(cmp_corner);
53 cmp_edge_u64 = vreinterpretq_u64_u8(cmp_edge);
54
55 // combine the comparison vectors
56 cmp_result = vandq_u64(cmp_corner_u64, cmp_edge_u64);
57
58 // check if all the bits are set
59 return vgetq_lane_u64(cmp_result, 0) == ~0ULL && vgetq_lane_u64(cmp_result, 1) == ~0ULL;
60}
61
62_static_inline cube_t
63invertco(cube_t c)
64{
65 cube_t ret;
66 uint8x16_t co, shleft, shright, summed, newco, cleanco;
67
68 co = vandq_u8(c.corner, _co2_neon);
69 shleft = vshlq_n_u8(co, 1);
70 shright = vshrq_n_u8(co, 1);
71 summed = vorrq_u8(shleft, shright);
72 newco = vandq_u8(summed, _co2_neon);
73 cleanco = veorq_u8(c.corner, co);
74 ret.corner = vorrq_u8(cleanco, newco);
75 ret.edge = c.edge;
76
77 return ret;
78}
79
80_static_inline cube_t
81compose_edges(cube_t c1, cube_t c2)
82{
83 cube_t ret = {0};
84 ret.edge = compose_edges_slim(c1.edge, c2.edge);
85 return ret;
86}
87
88_static_inline cube_t
89compose_corners(cube_t c1, cube_t c2)
90{
91 cube_t ret = {0};
92 ret.corner = compose_corners_slim(c1.corner, c2.corner);
93 return ret;
94}
95
96_static_inline uint8x16_t
97compose_edges_slim(uint8x16_t edge1, uint8x16_t edge2)
98{
99 // Masks
100 uint8x16_t p_bits = vdupq_n_u8(_pbits);
101 uint8x16_t eo_bit = vdupq_n_u8(_eobit);
102
103 // Find the index and permutation
104 uint8x16_t p = vandq_u8(edge2, p_bits);
105 uint8x16_t piece1 = vqtbl1q_u8(edge1, p);
106
107 // Calculate the orientation through XOR
108 uint8x16_t orien = vandq_u8(veorq_u8(edge2, piece1), eo_bit);
109
110 // Combine the results
111 uint8x16_t ret = vorrq_u8(vandq_u8(piece1, p_bits), orien);
112
113 // Mask to clear the last 32 bits of the result
114 uint8x16_t mask_last_32 = vsetq_lane_u32(0, vreinterpretq_u32_u8(ret), 3);
115 ret = vreinterpretq_u8_u32(mask_last_32);
116
117 return ret;
118}
119
120_static_inline uint8x16_t
121compose_corners_slim(uint8x16_t corner1, uint8x16_t corner2)
122{
123 // Masks
124 uint8x16_t p_bits = vdupq_n_u8(_pbits);
125 uint8x16_t cobits = vdupq_n_u8(_cobits);
126 uint8x16_t cobits2 = vdupq_n_u8(_cobits2);
127 uint8x16_t twist_cw = vdupq_n_u8(_ctwist_cw);
128
129 // Find the index and permutation
130 uint8x16_t p = vandq_u8(corner2, p_bits);
131 uint8x16_t piece1 = vqtbl1q_u8(corner1, p);
132
133 // Calculate the orientation
134 uint8x16_t aux = vaddq_u8(vandq_u8(corner2, cobits), vandq_u8(piece1, cobits));
135 uint8x16_t auy = vshrq_n_u8(vaddq_u8(aux, twist_cw), 2);
136 uint8x16_t orien = vandq_u8(vaddq_u8(aux, auy), cobits2);
137
138 // Combine the results
139 uint8x16_t ret = vorrq_u8(vandq_u8(piece1, p_bits), orien);
140
141 // Mask to clear the last 64 bits of the result
142 uint8x16_t mask_last_64 = vsetq_lane_u64(0, vreinterpretq_u64_u8(ret), 1);
143 ret = vreinterpretq_u8_u64(mask_last_64);
144
145 return ret;
146}
147
148_static_inline cube_t
149compose(cube_t c1, cube_t c2)
150{
151 cube_t ret = {0};
152
153 ret.edge = compose_edges_slim(c1.edge, c2.edge);
154 ret.corner = compose_corners_slim(c1.corner, c2.corner);
155
156 return ret;
157}
158
159_static_inline cube_t
160inverse(cube_t cube)
161{
162 uint8_t i, piece, orien;
163 cube_t ret;
164
165 // Temp arrays to store the NEON vectors
166 uint8_t edges[16];
167 uint8_t corners[16];
168
169 // Copy the NEON vectors to the arrays
170 vst1q_u8(edges, cube.edge);
171 vst1q_u8(corners, cube.corner);
172
173 uint8_t edge_result[16] = {0};
174 uint8_t corner_result[16] = {0};
175
176 // Process the edges
177 for (i = 0; i < 12; i++)
178 {
179 piece = edges[i];
180 orien = piece & _eobit;
181 edge_result[piece & _pbits] = i | orien;
182 }
183
184 // Process the corners
185 for (i = 0; i < 8; i++)
186 {
187 piece = corners[i];
188 orien = ((piece << 1) | (piece >> 1)) & _cobits2;
189 corner_result[piece & _pbits] = i | orien;
190 }
191
192 // Copy the results back to the NEON vectors
193 ret.edge = vld1q_u8(edge_result);
194 ret.corner = vld1q_u8(corner_result);
195
196 return ret;
197}
198
199_static_inline int64_t
200coord_co(cube_t c)
201{
202 // Temp array to store the NEON vector
203 uint8_t mem[16];
204 vst1q_u8(mem, c.corner);
205
206 int i, p;
207 int64_t ret;
208
209 for (ret = 0, i = 0, p = 1; i < 7; i++, p *= 3)
210 ret += p * (mem[i] >> _coshift);
211
212 return ret;
213}
214
215_static_inline int64_t
216coord_csep(cube_t c)
217{
218 // Temp array to store the NEON vector
219 uint8_t mem[16];
220 vst1q_u8(mem, c.corner);
221
222 int64_t ret = 0;
223 int i, p;
224 for (ret = 0, i = 0, p = 1; i < 7; i++, p *= 2)
225 ret += p * ((mem[i] & _csepbit) >> 2);
226
227 return ret;
228 return 0;
229}
230
231_static_inline int64_t
232coord_cocsep(cube_t c)
233{
234 return (coord_co(c) << 7) + coord_csep(c);
235}
236
237_static_inline int64_t
238coord_eo(cube_t c)
239{
240 int64_t ret = 0;
241 int64_t p = 1;
242
243 // Temp array to store the NEON vector
244 uint8_t mem[16];
245 vst1q_u8(mem, c.edge);
246
247 for (int i = 1; i < 12; i++, p *= 2)
248 {
249 ret += p * (mem[i] >> _eoshift);
250 }
251
252 return ret;
253}
254
255_static_inline int64_t
256coord_esep(cube_t c)
257{
258 int64_t i, j, jj, k, l, ret1, ret2, bit1, bit2, is1;
259
260 // Temp array to store the NEON vector
261 uint8_t mem[16];
262 vst1q_u8(mem, c.edge);
263
264 for (i = 0, j = 0, k = 4, l = 4, ret1 = 0, ret2 = 0; i < 12; i++)
265 {
266 bit1 = (mem[i] & _esepbit1) >> 2;
267 bit2 = (mem[i] & _esepbit2) >> 3;
268 is1 = (1 - bit2) * bit1;
269
270 ret1 += bit2 * binomial[11 - i][k];
271 k -= bit2;
272
273 jj = j < 8;
274 ret2 += jj * is1 * binomial[7 - (j * jj)][l];
275 l -= is1;
276 j += (1 - bit2);
277 }
278
279 return ret1 * 70 + ret2;
280}
281
282_static_inline void
283copy_corners(cube_t *dst, cube_t src)
284{
285 dst->corner = src.corner;
286}
287
288_static_inline void
289copy_edges(cube_t *dst, cube_t src)
290{
291 dst->edge = src.edge;
292}
293
294_static_inline void
295set_eo(cube_t *cube, int64_t eo)
296{
297 // Temp array to store the NEON vector
298 uint8_t mem[16];
299 vst1q_u8(mem, cube->edge);
300 uint8_t i, sum, flip;
301
302 for (sum = 0, i = 1; i < 12; i++, eo >>= 1)
303 {
304 flip = eo % 2;
305 sum += flip;
306 mem[i] = (mem[i] & ~_eobit) | (_eobit * flip);
307 }
308 mem[0] = (mem[0] & ~_eobit) | (_eobit * (sum % 2));
309
310 // Copy the results back to the NEON vector
311 cube->edge = vld1q_u8(mem);
312 return;
313}
314
315_static_inline cube_t
316invcoord_esep(int64_t esep)
317{
318 cube_t ret;
319 int64_t bit1, bit2, i, j, jj, k, l, s, v, w, is1, set1, set2;
320 uint8_t slice[3] = {0};
321
322 ret = solved;
323 uint8_t mem[16];
324 set1 = esep % 70;
325 set2 = esep / 70;
326
327 for (i = 0, j = 0, k = 4, l = 4; i < 12; i++)
328 {
329 v = binomial[11 - i][k];
330 jj = j < 8;
331 w = jj * binomial[7 - (j * jj)][l];
332 bit2 = set2 >= v;
333 bit1 = set1 >= w;
334 is1 = (1 - bit2) * bit1;
335
336 set2 -= bit2 * v;
337 k -= bit2;
338 set1 -= is1 * w;
339 l -= is1;
340 j += (1 - bit2);
341 s = 2 * bit2 + (1 - bit2) * bit1;
342
343 mem[i] = (slice[s]++) | (uint8_t)(s << 2);
344 }
345
346 ret.edge = vld1q_u8(mem);
347 return ret;
348}
diff --git a/src/arch/portable.h b/src/arch/portable.h
new file mode 100644
index 0000000..b8330af
--- /dev/null
+++ b/src/arch/portable.h
@@ -0,0 +1,272 @@
1#define static_cube(c_ufr, c_ubl, c_dfl, c_dbr, c_ufl, c_ubr, c_dfr, c_dbl, \
2 e_uf, e_ub, e_db, e_df, e_ur, e_ul, e_dl, e_dr, e_fr, e_fl, e_bl, e_br) \
3 ((cube_t) { \
4 .corner = { c_ufr, c_ubl, c_dfl, c_dbr, c_ufl, c_ubr, c_dfr, c_dbl }, \
5 .edge = { e_uf, e_ub, e_db, e_df, e_ur, e_ul, \
6 e_dl, e_dr, e_fr, e_fl, e_bl, e_br } })
7#define zero static_cube( \
8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
9#define solved static_cube( \
10 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
11
12_static void
13pieces(cube_t *cube, uint8_t c[static 8], uint8_t e[static 12])
14{
15 memcpy(c, cube->corner, 8);
16 memcpy(e, cube->edge, 12);
17}
18
19_static_inline bool
20equal(cube_t c1, cube_t c2)
21{
22 uint8_t i;
23 bool ret;
24
25 ret = true;
26 for (i = 0; i < 8; i++)
27 ret = ret && c1.corner[i] == c2.corner[i];
28 for (i = 0; i < 12; i++)
29 ret = ret && c1.edge[i] == c2.edge[i];
30
31 return ret;
32}
33
34_static_inline cube_t
35invertco(cube_t c)
36{
37 uint8_t i, piece, orien;
38 cube_t ret;
39
40 ret = c;
41 for (i = 0; i < 8; i++) {
42 piece = c.corner[i];
43 orien = ((piece << 1) | (piece >> 1)) & _cobits2;
44 ret.corner[i] = (piece & _pbits) | orien;
45 }
46
47 return ret;
48}
49
50_static_inline void
51compose_edges_inplace(cube_t c1, cube_t c2, cube_t *ret)
52{
53 uint8_t i, piece1, piece2, p, orien;
54
55 for (i = 0; i < 12; i++) {
56 piece2 = c2.edge[i];
57 p = piece2 & _pbits;
58 piece1 = c1.edge[p];
59 orien = (piece2 ^ piece1) & _eobit;
60 ret->edge[i] = (piece1 & _pbits) | orien;
61 }
62}
63
64_static_inline void
65compose_corners_inplace(cube_t c1, cube_t c2, cube_t *ret)
66{
67 uint8_t i, piece1, piece2, p, orien, aux, auy;
68
69 for (i = 0; i < 8; i++) {
70 piece2 = c2.corner[i];
71 p = piece2 & _pbits;
72 piece1 = c1.corner[p];
73 aux = (piece2 & _cobits) + (piece1 & _cobits);
74 auy = (aux + _ctwist_cw) >> 2;
75 orien = (aux + auy) & _cobits2;
76 ret->corner[i] = (piece1 & _pbits) | orien;
77 }
78}
79
80_static_inline cube_t
81compose_edges(cube_t c1, cube_t c2)
82{
83 cube_t ret = zero;
84
85 compose_edges_inplace(c1, c2, &ret);
86
87 return ret;
88}
89
90_static_inline cube_t
91compose_corners(cube_t c1, cube_t c2)
92{
93 cube_t ret = zero;
94
95 compose_corners_inplace(c1, c2, &ret);
96
97 return ret;
98}
99
100_static_inline cube_t
101compose(cube_t c1, cube_t c2)
102{
103 cube_t ret = zero;
104
105 compose_edges_inplace(c1, c2, &ret);
106 compose_corners_inplace(c1, c2, &ret);
107
108 return ret;
109}
110
111cube_t
112inverse(cube_t cube)
113{
114 uint8_t i, piece, orien;
115 cube_t ret;
116
117 for (i = 0; i < 12; i++) {
118 piece = cube.edge[i];
119 orien = piece & _eobit;
120 ret.edge[piece & _pbits] = i | orien;
121 }
122
123 for (i = 0; i < 8; i++) {
124 piece = cube.corner[i];
125 orien = ((piece << 1) | (piece >> 1)) & _cobits2;
126 ret.corner[piece & _pbits] = i | orien;
127 }
128
129 return ret;
130}
131
132_static_inline int64_t
133coord_co(cube_t c)
134{
135 int i, p;
136 int64_t ret;
137
138 for (ret = 0, i = 0, p = 1; i < 7; i++, p *= 3)
139 ret += p * (c.corner[i] >> _coshift);
140
141 return ret;
142}
143
144/*
145For corner separation, we consider the axis (a.k.a. tetrad) each
146corner belongs to as 0 or 1 and we translate this sequence into binary.
147Ignoring the last bit, we have a value up to 2^7, but not all values are
148possible. Encoding this as a number from 0 to C(8,4) would save about 40%
149of space, but we are not going to use this coordinate in large tables.
150*/
151_static_inline int64_t
152coord_csep(cube_t c)
153{
154 int i, p;
155 int64_t ret;
156
157 for (ret = 0, i = 0, p = 1; i < 7; i++, p *= 2)
158 ret += p * ((c.corner[i] & _csepbit) >> 2);
159
160 return ret;
161}
162
163_static_inline int64_t
164coord_cocsep(cube_t c)
165{
166 return (coord_co(c) << 7) + coord_csep(c);
167}
168
169_static_inline int64_t
170coord_eo(cube_t c)
171{
172 int i, p;
173 int64_t ret;
174
175 for (ret = 0, i = 1, p = 1; i < 12; i++, p *= 2)
176 ret += p * (c.edge[i] >> _eoshift);
177
178 return ret;
179}
180
181/*
182We encode the edge separation as a number from 0 to C(12,4)*C(8,4).
183It can be seen as the composition of two "subset index" coordinates.
184*/
185_static_inline int64_t
186coord_esep(cube_t c)
187{
188 int64_t i, j, jj, k, l, ret1, ret2, bit1, bit2, is1;
189
190 for (i = 0, j = 0, k = 4, l = 4, ret1 = 0, ret2 = 0; i < 12; i++) {
191 /* Simple version:
192 if (c.edge[i] & _esepbit2) {
193 ret1 += binomial[11-i][k--];
194 } else {
195 if (c.edge[i] & _esepbit1)
196 ret2 += binomial[7-j][l--];
197 j++;
198 }
199 */
200
201 bit1 = (c.edge[i] & _esepbit1) >> 2;
202 bit2 = (c.edge[i] & _esepbit2) >> 3;
203 is1 = (1 - bit2) * bit1;
204
205 ret1 += bit2 * binomial[11-i][k];
206 k -= bit2;
207
208 jj = j < 8;
209 ret2 += jj * is1 * binomial[7-(j*jj)][l];
210 l -= is1;
211 j += (1-bit2);
212 }
213
214 return ret1 * 70 + ret2;
215}
216
217_static_inline void
218copy_corners(cube_t *dest, cube_t src)
219{
220 memcpy(&dest->corner, src.corner, sizeof(src.corner));
221}
222
223_static_inline void
224copy_edges(cube_t *dest, cube_t src)
225{
226 memcpy(&dest->edge, src.edge, sizeof(src.edge));
227}
228
229_static_inline void
230set_eo(cube_t *cube, int64_t eo)
231{
232 uint8_t i, sum, flip;
233
234 for (sum = 0, i = 1; i < 12; i++, eo >>= 1) {
235 flip = eo % 2;
236 sum += flip;
237 cube->edge[i] = (cube->edge[i] & ~_eobit) | (_eobit * flip);
238 }
239 cube->edge[0] = (cube->edge[0] & ~_eobit) | (_eobit * (sum % 2));
240}
241
242_static_inline cube_t
243invcoord_esep(int64_t esep)
244{
245 cube_t ret;
246 int64_t bit1, bit2, i, j, jj, k, l, s, v, w, is1, set1, set2;
247 uint8_t slice[3] = {0};
248
249 ret = solved;
250 set1 = esep % 70;
251 set2 = esep / 70;
252
253 for (i = 0, j = 0, k = 4, l = 4; i < 12; i++) {
254 v = binomial[11-i][k];
255 jj = j < 8;
256 w = jj * binomial[7-(j*jj)][l];
257 bit2 = set2 >= v;
258 bit1 = set1 >= w;
259 is1 = (1 - bit2) * bit1;
260
261 set2 -= bit2 * v;
262 k -= bit2;
263 set1 -= is1 * w;
264 l -= is1;
265 j += (1-bit2);
266 s = 2*bit2 + (1-bit2)*bit1;
267
268 ret.edge[i] = (slice[s]++) | (uint8_t)(s << 2);
269 }
270
271 return ret;
272}

Generated with cgit - Back to sebastiano.tronto.net