commit 3568412f8f230774d0d11d7ed1c897424f95d3ef
parent 67e1b5e6e6a2c917a2fe58a37a1382c982b1e5c5
Author: Sebastiano Tronto <sebastiano.tronto@gmail.com>
Date: Thu, 11 Nov 2021 21:37:34 +0100
Rewritten from scratch. Welocme nissy 2.0!
Diffstat:
217 files changed, 59483 insertions(+), 5284 deletions(-)
diff --git a/INSTALL b/INSTALL
@@ -0,0 +1,27 @@
+# Requirements
+
+A full installation of nissy requires about 1.8Gb of space, of which 1.6Gb are
+occupied by the huge pruning table for optimal solving, and running it requires
+the same amount of RAM.
+One can choose to never use the optimal solver and not to install the relative
+pruning table. If so, about 200Mb should be enough.
+
+# Installation
+
+## On a UNIX system:
+
+Edit the Makefile to match your local configuration (usually not necessary, but you
+may want to change the PREFIX variable) and run make, followed by make install.
+After that, you need to download some files and manually save them to your NISSYDATA
+folder (se manual page, ENVIRONMENT section). If you don't do it nissy can compute
+these files when needed and save them in the correct folders, but some of them
+take hours to generate even for a powerful computer (for reference, the huge pruning
+table takes about 20 hours on my laptop).
+Choose one of the following:
+ https://math.uni.lu/tronto/nissy/nissy-tables-full.zip
+ https://math.uni.lu/tronto/nissy/nissy-tables-full.tar.gz
+ https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.zip
+ https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.tar.gz
+extract the archive and copy the tables folder into NISSIDATA (paste there
+the whole folder, not file by file). The "nohuge" files are much smaller and do not
+contain the huge pruning table for the optimal solver.
diff --git a/LICENSE b/LICENSE
@@ -1,4 +1,4 @@
- GNU GENERAL PUBLIC LICENSE
+ GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
diff --git a/Makefile b/Makefile
@@ -0,0 +1,54 @@
+# See LICENSE file for copyright and license details.
+
+VERSION = 2.0beta1
+
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+CPPFLAGS = -DVERSION=\"${VERSION}\"
+CFLAGS = -pedantic -Wall -Wextra -Wno-unused-parameter -O3 ${CPPFLAGS}
+DBGFLAGS = -pedantic -Wall -Wextra -Wno-unused-parameter -g ${CPPFLAGS}
+
+CC = cc
+
+
+all: options nissy
+
+options:
+ @echo nissy build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "CC = ${CC}"
+
+nissy:
+ ${CC} ${CFLAGS} -o nissy.o src/*.c
+
+debug:
+ ${CC} ${DBGFLAGS} -o nissy.o src/*.c
+
+clean:
+ rm -rf nissy.o nissy-${VERSION}.tar.gz
+
+dist: clean
+ mkdir -p nissy-${VERSION}
+ cp -R LICENSE Makefile INSTALL doc src nissy-${VERSION}
+ groff -Tpdf -mandoc doc/nissy.1 > nissy-${VERSION}/doc/nissy.pdf
+ groff -Thtml -mandoc doc/nissy.1 > nissy-${VERSION}/doc/nissy.html
+ tar -cf nissy-${VERSION}.tar nissy-${VERSION}
+ gzip nissy-${VERSION}.tar
+ rm -rf nissy-${VERSION}
+
+install: nissy
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp -f nissy.o ${DESTDIR}${PREFIX}/bin/nissy
+ chmod 755 ${DESTDIR}${PREFIX}/bin/nissy
+ mkdir -p ${DESTDIR}${MANPREFIX}/man1
+ sed "s/VERSION/${VERSION}/g" < doc/nissy.1 \
+ > ${DESTDIR}${MANPREFIX}/man1/nissy.1
+ chmod 644 ${DESTDIR}${MANPREFIX}/man1/nissy.1
+
+uninstall:
+ rm -rf ${DESTDIR}${PREFIX}/bin/nissy ${DESTDIR}${MANPREFIX}/man1/nissy.1
+ for s in ${SCRIPTS}; do rm -rf ${DESTDIR}${PREFIX}/bin/$$s; done
+
+.PHONY: all options nissy debug clean dist install uninstall
+
diff --git a/README.md b/README.md
@@ -1,41 +1,128 @@
-# nissy
-A Rubik's cube solver and FMC assistant.
+# Nissy
-## Just another cube solver?
-Yes, pretty much. I wanted to write one and I started coding without any
-specific goal in mind. It is not more efficient than [CubeExplorer](http://kociemba.org/cube.htm), nor it is
-particularly user-friendly.
+A Rubik's cube solver and FMC assistant. For optimal HTM solving nissy is about as
+fast as Herbert Kociemba's [Cube Explorer](http://kociemba.org/cube.htm), and it
+uses the same method. Nissy can also solve many different substeps of
+Thistlethwaite's algorithm (DR/HTR), and can use NISS (Normal-Inverse Scramble Switch).
-## But does it do something unique?
-Yes, actually it does, but only for a very small niche of people. It allows to produce step-by-step solutions using DR
-([Thistlethwaite](/https://www.speedsolving.com/wiki/index.php/Thistlethwaite%27s_algorithm)/[Kociemba](https://www.speedsolving.com/wiki/index.php/Kociemba%27s_Algorithm) algorithm)
-combined with [NISS](https://www.speedsolving.com/wiki/index.php/Fewest_Moves_techniques). This makes it somewhat useful for [FMC](https://www.speedsolving.com/wiki/index.php/Fewest_Moves_Challenge) solvers who want to analyze a scramble and see if they missed something,
-or what was the optimal way to solve a certain substep at a given point, and so on.
+It can be useful to analyze your DR solves (and more, once I implement more features).
-## How to use it
-**Update:** the file nissy-win.exe should work as a Windows executable, but
-I have not tested it (I don't have a Windows machine).
+## Why should I use nissy?
-Check out the help pages in the docs folder. They are also available from
-within nissy with the command "help".
+You should use nissy if you:
+* Want to analyze your DR solutions or check for multiple optimal (or sub-optimal)
+solutions for EO/DR/HTR or similar substeps.
+* You just want a Rubik's cube solver and you like command line interfaces.
+* You want an alternative to Cube Explorer.
-I will add more examples and maybe screenshots when I feel like.
+## Requirements
+
+A full installation of nissy requires about 1.8Gb of space, of which 1.6Gb are
+occupied by the huge pruning table for optimal solving, and running it requires
+the same amount of RAM.
+One can choose to never use the optimal solver and not to install the relative
+pruning table. If so, about 200Mb should be enough.
## Installation
-For now you have to download all the files and compile the source
-code yourself. Remember to tell your
-compiler to use the [C99 standard](https://en.wikipedia.org/wiki/C99). For
-example, on a Linux system with GCC installed:
-
-```
-cd path/to/nissy
-gcc -O2 -std=c99 -o nissy ./src/*.c
-./nissy
-```
-
-You can also use the script compile.sh, which executes that
-gcc line (with a few extra options).
-
-## Tips
-You can use a tool such as [rlwrap](https://github.com/hanslub42/rlwrap) to allow
-for infinte command history within nissy!
+
+### On a UNIX system:
+
+You can download the source code for the latest version from git or simply clone
+the repo with `git clone https://github.com/sebastianotronto/nissy`.
+
+Edit the Makefile to match your local configuration (usually not necessary, but you
+may want to change the `PREFIX` variable) and run `make`, followed by `make install`.
+After that, you need to download some files and manually save them to your `NISSYDATA`
+folder (see manual page, ENVIRONMENT section). If you don't do it nissy can compute
+these files when needed and save them in the correct folders, but some of them
+take hours to generate even for a powerful computer (for reference, the huge pruning
+table takes about 20 hours on my laptop).
+
+Choose one of the following:
+
+| |.zip|.tar.gz|
+|-|-|-|
+|Full (~720Mb)|[full.zip](https://math.uni.lu/tronto/nissy/nissy-tables-full.zip)|[full.tar.gz](https://math.uni.lu/tronto/nissy/nissy-tables-full.tar.gz)|
+|No huge table (~90Mb)|[nohuge.zip](https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.zip)|[nohuge.tar.gz](https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.tar.gz)|
+
+extract the archive and copy the `tables` folder into `NISSIDATA` (paste there
+the whole folder, not the single files). The `nohuge` files are much smaller and
+do not contain the huge pruning table for the optimal solver.
+
+### On Windows
+
+Coming soon.
+
+## Structure of the code
+
+You can find all the source code in the `src` folder.
+I strived to keep it legible but I did not write many comments (barely any at all).
+I'll try to explain here the main parts of the program.
+
+### Cube, moves and transformations
+
+There are many ways to represent a cube. In nissy I use two:
+* An array representation `CubeArray`: 3 arrays representing the permutation
+of corners, edges and centers and 2 arrays for the orientation of corners and edges.
+* An 11-integers representation `Cube`: 3 integers for edge orientation (with respect
+to the three axes), 3 for corner orientation, and so on. Edge permutation is a bit
+complicated because encoding 12 factorial as a single number is too large for some
+practical reasons, so I use 3 integers for that.
+
+Moves are easy to apply on the array form, but they are slow. So `moves.c`
+contains the instructions to create all the transition tables necessary
+to get the next position for the cube with just 11 lookup operations
+(one for each of the 11 integers in the second representation).
+These transition tables are saved in the `mtables` file in the
+`tables` folder in binary format.
+
+The 11 integers are obviously redundant, but keeping all of them makes it easy
+to apply transformations. A transformation is a rotation of the whole cube, possibly
+combined with a mirror operation. Applying a transformation to a cube (say obtained
+by applying a scramble to the solved cube) means applying the transformation to a
+solved cube, then the scramble and then the inverse of the transformation
+(i.e. conjugating by it).
+
+### Coordinates and pruning tables
+
+A *coordinate* consists of a function that takes a cube (in the 11-integer
+representation) and return an (unsigned, 64-bit) integer. They are used
+to "linearize" a cube and build pruning tables, which speed up significantly the
+solving process. To be able to access the pruning table quickly, the function
+needs to be very fast (e.g. it should not convert between the two representations
+of the cube if not necessary).
+
+Some coordinates make use of symmetries to reduce the size of the resulting
+pruning table. Unfortunately this complicates the code a lot, but it is a huge
+advantage: it reduces by a factor of about 16 the huge pruning table, which
+results in around 1.6Gb instead of 24 or so.
+
+Pruning tables are related to a specific step, a moveset and a coordinate. They
+contain one value from 0 to 15 (4 bits) for each possible value for the coordinate,
+which is less or equal than the minimum number of moves required to solve the
+given step with the given moveset for a cube which has the given coordinate. For example,
+say the coordinate `neo` gives the number of non-oriented edges (say with respect to
+F/B). Then the possible values for the coordinate are 0,2,4,...,12. An associate pruning
+table to solving EO with HTM moveset and this coordinate would have values 0 (for
+`neo=0`), 3 (for `neo=2`), 1 (for `neo=4`)...
+
+There is one caveat: each coordinates also needs an inverse function that takes a
+coordinate value and returns a cube which has that coordinate. This is in general
+more complicated, but luckily the cube does not need to be fully built or consistent.
+This inverse-coordinate is used only in one specific step when building pruning tables
+to avoid using up hundreds of Gb of memory.
+
+Note: this part is different from what Cube Explorer does. Overall I think it is
+conceptually easier, although in practice it was still hard to implement.
+If anything it is more generalizable and one can use it to build any coordinate
+they might like.
+
+### Solving
+
+Solving is implemented as a generic function that takes both a step and
+a (scrambled) cube as input, as well as some extra parameters that say e.g.
+how many solution one wants. A step consists, among other things, of
+an estimator function that, given a cube, gives a lower bound for the number
+of moves needed to complete the step. Many of these estimators simply
+look up the corresponding values in the appropriate pruning table.
+
diff --git a/TODO.md b/TODO.md
@@ -0,0 +1,53 @@
+# TODO list
+
+This is a list of things that I would like to add or change at some point.
+It's more of a personal reminder than anything else.
+
+## Commands
+
+### Commands that are available in nissy 1.0, but not in this version (yet):
+* drcorners (solve corners after dr)
+* search and improve non-optimal subsequences
+* unniss (rewrite A (B) -> B' A)
+* scramble [dr, corners only, edges only, htr, ...]
+* save and edit algs as "variables"
+* invert an alg
+
+### More steps for `solve`
+* QTM optimal solving
+* Block-building steps (cross, roux blocks, ...)
+* Other common steps (LSE, ...)
+
+### Improvements to currently implemented commands
+* solve should re-orient first if needed and not just give up if centers are off
+* solve should try up to a small bound without loading the large pruning table
+* drfin for HTR scrambles should try all 3 axis and pick the best solutions;
+ in general every step that automatically detects orientation should do this
+
+### New features
+* cleanup: translate an alg to the standard HTM moveset + reorient at the end
+* batch mode: read list of commands from stdin or a file and exec them
+ one after the other non-interactively
+* configurability: add an `alias` command, run config file at startup
+
+## Distribution
+
+* make env.c compatible with Windows (and check that it works with
+ BSD/MacOS)
+* better internal help page for each command (take it from man page)
+* better man page
+* find a better way to distribute the large tables, especially khuge
+
+## Technical stuff
+
+### Memory management
+* fail gracefully when there is not enough memory to load a large table
+* free tables from memory when not used
+* optionally run in low-memory friendly version (no tables above a few Mb);
+ this can be useful e.g. for embedded devices
+
+### Structural changes
+* client/server architecture: run a server process in the background so that
+ multiple client processess can send it queries and get results; this would
+ open up the door for a web-based version or graphical clients
+* use multiple threads to search for solutions in parallel
diff --git a/compile.sh b/compile.sh
@@ -1 +0,0 @@
-gcc -Wall -Wextra -O2 -std=c99 -o nissy -g ./src/*.c
diff --git a/doc/nissy.1 b/doc/nissy.1
@@ -0,0 +1,153 @@
+.Dd November 2021
+.Dt NISSY 1
+.Os
+.Sh NAME
+.Nm nissy
+.Nd a Rubik's cube solver and FMC assistant
+.
+.Sh SYNOPSIS
+.Nm
+.Nm
+.Ar command
+.Op options...
+.
+When run without any argument an interactive shell is launched, otherwise
+the provided
+.Ar command
+is executed and nissy terminates.
+The commands that can be run in the interactive shell are the same that can
+be run non-interactively and are provided below.
+.
+.Sh DESCRIPTION
+.Nm
+is a Rubik's Cube solver. Its optimal solver function uses Kociemba's one-step
+algorithm (huge optimal solver), and its performance is comparable to that
+of Kociemba's implementation in Cube Explorer.
+nissy can also solve different substeps of the Thistlethwaite's algorithm
+and more.
+.
+.Sh COMMANDS
+The available
+.Ar commands
+are the following:
+.
+.Bl -tag -width Ds
+.
+.It Nm commands
+List all available commands.
+.
+.It Nm help Op Ar command
+Display help. If no
+.Ar command
+is given, a generic help message is printed, otherwise a specific help
+relative to
+.Ar command
+is returned.
+.
+.It Nm print Ar scramble
+Display a text-only description of the cube obtained after applying
+.Ar scramble .
+.
+.It Nm quit
+Quit nissy.
+.
+.It Nm solve Ar step Oo Ar options Oc Ar scramble
+Solve the given
+.Ar step
+on the given
+.Ar scramble.
+By default it finds only one (shortest) solution, without using niss, and it
+displays the number of moves at the end of the line.
+.
+The options for the
+.Ar solve
+command are the following:
+.
+.Bl -tag -width Ds
+.
+.It Fl m Ar min
+Only look for solution that are at least
+.Ar min
+moves long.
+.
+.It Fl M Ar MAX
+Only look for solution that are at most
+.Ar MAX
+moves long.
+.
+.It Fl s Ar n
+Try to find
+.Ar n
+solutions.
+.
+.It Fl a
+Print all solutions: some solutions are filtered out by default for some
+steps, for examples EOs that finish with F\(aq, with this options they are not.
+.
+.It Fl n
+Allow use of NISS.
+.
+.It Fl o
+Only find solutions that require the minimum number of moves.
+.
+.It Fl p
+Plain style: do not print the number of moves.
+.
+.It Fl v
+Verbose mode: print some information during the search and print each solution
+as it is found instead of only printing them all together at the end.
+.
+.
+.El
+.
+.It Nm steps
+List all available
+.Ar steps
+for the
+.Ar solve
+command.
+.
+.It Nm version
+Display version information.
+.
+.El
+.
+.Sh ENVIRONMENT
+Data is stored in the folder pointed to by
+.Nm $NISSYDATA.
+If that variable is unset the folder
+.Nm $XDG_DATA_HOME/nissy
+or
+.Nm $HOME/.nissy
+is used instead.
+.
+.Sh EXAMPLES
+.
+The command:
+.Dl nissy solve -v \(dqR\(aqU\(aqFD2L2FR2U2R2BD2LB2D\(aqB2L\(aqR\(aqBD2BU2LU2R\(aqU\(aqF\(dq
+.
+Returns:
+.Dl Found 0 solutions, searching depth 0...
+.Dl Found 0 solutions, searching depth 1...
+.Dl (some more lines)
+.Dl Found 0 solutions, searching depth 16...
+.Dl D2 F\(aq U2 D2 F\(aq L2 D R2 D F B2 R\(aq L2 F\(aq U\(aq D
+.Dl D2 F\(aq U2 D2 F\(aq L2 D R2 D F B2 R\(aq L2 F\(aq U\(aq D (16)
+.
+The command:
+.Dl nissy solve eofb -m 4 -M 5 -n -s 6 \(dqR\(aqU\(aqFD2L2 FR2 U2R2BD2 L B2 D\(aq B2 L\(aq R\(aq\(dq
+.
+Returns:
+.Dl U B U\(aq B (4)
+.Dl U2 D2 B (B) (4)
+.Dl U2 F R2 F (4)
+.Dl U2 B U2 B (4)
+.Dl U2 B (U B) (4)
+.Dl U\(aq B U B (4)
+.
+.Sh AUTHORS
+.An Sebastiano Tronto Aq Mt sebastiano.tronto@gmail.com
+.
+.Sh SOURCE CODE
+Source code is available at
+.Lk https://github.com/sebastianotronto/nissy
diff --git a/docs/add.txt b/docs/add.txt
@@ -1,18 +0,0 @@
-
-HELP PAGE FOR COMMAND add
-
-SYNTAX
-add [MOVES|$ID1|@ID1] $ID2
-
-DESCRIPTION
-Appends either MOVES, the scramble memorized under $ID1 or the output sequence
-memorized under @ID1 at the end of the scramble memorized under $ID2. If none
-of MOVES, $ID1 or @ID1 is specified, the user will be asked to type the moves.
-Menmonic: "add x to y" or just "add to y".
-
-EXAMPLES
-add $1
- The user is required to type the moves that will be appended to $1.
-add F R B $1
- Appends the moves F R B to scramble $1. Now scramble $1 ends with F R B.
-
diff --git a/docs/change.txt b/docs/change.txt
@@ -1,21 +0,0 @@
-
-HELP PAGE FOR COMMAND change
-
-SYNTAX
-change $ID1 [MOVES|$ID2|@ID2]
-
-DESCRIPTION
-Changes the scramble $ID1 to either MOVES, the scramble $ID2, the output @ID2
-or, if none is specified, the moves entered by the user. The scramble that was
-memorized under $ID1 is then lost.
-Mnemonic: "change x to y", or just "change x".
-
-EXAMPLES
-change $1
- The user is required to type the moves that will replace $1.
-change $2 $3
- Saves the scramble that was saved under $3 in $2. Now $2 and $3 are the same
- scrambles, and the old $2 is lost.
-change $1 U R
- Saves U R as scramble $1.
-
diff --git a/docs/clear.txt b/docs/clear.txt
@@ -1,9 +0,0 @@
-
-HELP PAGE FOR COMMAND clear
-
-SYNTAX
-clear
-
-DESCRIPTION
-Resets all saved scrambles and output sequences.
-
diff --git a/docs/co.txt b/docs/co.txt
@@ -1,39 +0,0 @@
-
-HELP PAGE FOR COMMAND co
-
-SYNTAX
-co [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Solves CO for a given scramble. A scramble can be given as last argument of the
-command, or an ID of a saved scramble can be provided. If none of the two is
-given, a prompt will ask the user to input a new scramble.
-
-OPTIONS
-axis={fb,rl,ud} Specify the axis for the CO. One to three axes can be given,
- comma separated, no spaces.
- Default: CO on any of the three axis (omitting the option is
- the same as specifying axis=fb,rl,ud).
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-h Show hidden COs.
- Default, if an CO ending in e.g. F is shown, the equivalent
- one ending in F' is hidden.
-i Ignore centers. By default the CO is aligned with centers.
-niss Use NISS.
- Default: does not use NISS.
-n=N Specify a maximum number of COs to be output. N must be a
- number.
- Default value: 1.
-
-EXAMPLES
-co axis=fb $1
- Finds one optimal CO on fb for the first saved scramble.
-
-co n=5 b=4 U R F
- Finds up to 5 COs of length at most 4 for scramble U R F.
-
-co n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F
- Finds up to 100 COs of lenth at most 4, possibly using NISS, including
- "hidden" COs, excluding the rl axis.
-
diff --git a/docs/dr.txt b/docs/dr.txt
@@ -1,45 +0,0 @@
-
-HELP PAGE FOR COMMAND dr
-
-SYNTAX
-dr [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Solves DR for a given scramble. A scramble can be given as last argument of the
-command, or an ID of a saved scramble can be provided. If none of the two is
-given, a prompt will ask the user to input a new scramble.
-If the option "from" is given (see below), it solves DR from an EO (if edges
-are oriented) without breaking that EO.
-The first time this command is called without the option from (and, to some
-extent, also the first time it is called with the option from), nissy loads
-some pruning tables that were not loaded on startup, causing a small but
-noticeable delay.
-
-OPTIONS
-axis={fb,rl,ud} Specify the axis for the DR. One to three axes can be given,
- comma separated, no spaces.
- Default: DR on any of the three axis (omitting the option is
- the same as specifying axis=fb,rl,ud).
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-h Show hidden DRs.
- Default, if an DR ending in e.g. R is shown, the equivalent
- one ending in R' is hidden.
-from {fb|rl|ud} Solve DR from the specified EO, which must be solved,
- without breaking the EO.
-niss Use NISS. It works only if solving DR from EO.
- Default: does not use NISS.
-n=N Specify a maximum number of EOs to be output. N must be a
- number.
- Default value: 1.
-
-EXAMPLES
-dr from rl axis=ud $1
- Finds optimal DR on ud, starting from EO on rl, for the first saved scramble.
-
-dr niss from fb n=10 m=6 F2 R L B' F D U' R2 L' F D B
- Finds up to 10 DRs of length at most 6 from EO on fb, possibly using NISS.
-
-dr n=100 axis=ud h
- Finds 100 DRs on ud, including "hidden" DRs.
-
diff --git a/docs/drcorners.txt b/docs/drcorners.txt
@@ -1,34 +0,0 @@
-
-HELP PAGE FOR COMMAND drcorners
-
-SYNTAX
-drcorners [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Similar to drfinish, but only solves corners. CO must be solved. The scramble
-can be given as the last argument of the command, or it may be given as an $ID
-or @ID, or it can be typed out on the following line.
-
-OPTIONS
-from {ud|fb|rl} Allows to specify on which axis the CO is solved. It is
- usually not necessary, since nissy will find a CO on any
- axis.
-i Ignores E-layer centers. By default cornersare solved
- relatively to centers; this options allows for solutions
- which solve corners relatively to each other and to the
- U and D sides, but not to the E layer (or any equivalent
- if the CO is not on U/D).
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-n=N Specify a maximum number of solutions to be output. N must
- be a number.
- Default value: 1.
-
-EXAMPLES
-drcorners n=3 R' D R2 D' R' U2 R D R' U2 R' D' R
- Produces the following output:
-Found 3 results.
-@1: U' F2 U R2 U2 F2 U F2 U R2 (10)
-@2: U' F2 U R2 U2 B2 U R2 D R2 (10)
-@3: U' F2 U R2 U2 B2 U L2 U L2 (10)
-
diff --git a/docs/drfinish.txt b/docs/drfinish.txt
@@ -1,29 +0,0 @@
-
-HELP PAGE FOR COMMAND drfinish
-
-SYNTAX
-drfinish [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Solves the given scramble using the DR moveset. DR must be solved. The scramble
-can be given as the last argument of the command, or it may be given as an $ID
-or @ID, or it can be typed out on the following line.
-
-OPTIONS
-from {ud|fb|rl} Allows to specify on which axis the DR is solved. It is
- usually not necessary, since nissy will find a DR on any
- axis, but it can be useful if one want to e.g. solve an HTR
- state allowing quarter-turns from a specific DR.
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-n=N Specify a maximum number of solutions to be output. N must
- be a number.
- Default value: 1.
-
-EXAMPLES
-dr from ud R L' U2 R' L F2
- Solves the given scramble using the moveset <U,D,R2,L2,F2,B2>. In this case:
-@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)
-drfinish b=7 n=10 $1
- Finds (at most) 10 solutions of length at most 7 for the scramble $1.
-
diff --git a/docs/eo.txt b/docs/eo.txt
@@ -1,38 +0,0 @@
-
-HELP PAGE FOR COMMAND eo
-
-SYNTAX
-eo [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Solves EO for a given scramble. A scramble can be given as last argument of the
-command, or an ID of a saved scramble can be provided. If none of the two is
-given, a prompt will ask the user to input a new scramble.
-
-OPTIONS
-axis={fb,rl,ud} Specify the axis for the EO. One to three axes can be given,
- comma separated, no spaces.
- Default: EO on any of the three axis (omitting the option is
- the same as specifying axis=fb,rl,ud).
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-h Show hidden EOs.
- Default, if an EO ending in e.g. F is shown, the equivalent
- one ending in F' is hidden.
-niss Use NISS.
- Default: does not use NISS.
-n=N Specify a maximum number of EOs to be output. N must be a
- number.
- Default value: 1.
-
-EXAMPLES
-eo axis=fb $1
- Finds one optimal EO on fb for the first saved scramble.
-
-eo n=5 b=4 U R F
- Finds up to 5 EOs of length at most 4 for scramble U R F.
-
-eo n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F
- Finds up to 100 EOs of lenth at most 4, possibly using NISS, including
- "hidden" EOs, excluding the rl axis.
-
diff --git a/docs/exit.txt b/docs/exit.txt
@@ -1,9 +0,0 @@
-
-HELP PAGE FOR COMMAND exit
-
-SYNTAX
-exit
-
-DESCRIPTION
-Exits nissy.
-
diff --git a/docs/help.txt b/docs/help.txt
@@ -1,15 +0,0 @@
-
-HELP PAGE FOR COMMAND help
-
-SYNTAX
-help [nissy|COMMAND]
-
-DESCRIPTION
-'help nissy' prints a general user manual. 'help COMMAND' prints a detailed
-help page for the command COMMAND, if it exists. 'help' prints a list of all
-available commands a short description for each.
-
-EXAMPLES
-help help
- Prints this help page.
-
diff --git a/docs/htr.txt b/docs/htr.txt
@@ -1,30 +0,0 @@
-
-HELP PAGE FOR COMMAND htr
-
-SYNTAX
-htr [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Finds HTR for a given scramble. DR must be solved. A scramble can be given as
-last argument of the command, or an ID of a saved scramble can be provided. If
-none of the two is given, a prompt will ask the user to input a new scramble.
-
-OPTIONS
-from {ud|fb|rl} Allows to specify on which axis the DR is. This is usually
- not needed, since nissy will automatically find it out.
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-h Show hidden HTRs.
- Default, if an HTR ending in e.g. R is shown, the equivalent
- one ending in R' is hidden.
-niss Use NISS.
- Default: does not use NISS.
-n=N Specify a maximum number of HTRs to be output. N must be a
- number.
- Default value: 1.
-
-EXAMPLES
-eo n=10 b=7 niss $1
- Finds up to 100 HTRs of lenth at most 7, possibly using NISS, including
- "hidden" HTRs, for scramble $1. DR must be solved.
-
diff --git a/docs/htrfinish.txt b/docs/htrfinish.txt
@@ -1,23 +0,0 @@
-
-HELP PAGE FOR COMMAND htrfinish
-
-SYNTAX
-htrfinish [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Similar to drfinish, but uses the moveset <U2,D2,R2,L2,F2,B2>. HTR must be
-solved. The scramble can be given as the last argument of the command, or it
-may be given as an $ID or @ID, or it can be typed out on the following line.
-
-OPTIONS
-b=N Specify a bound for the number of moves. N must be a number.
- Default value: 20.
-n=N Specify a maximum number ofsolutions to be output. N must be
- a number.
- Default value: 1.
-
-EXAMPLES
-htrfinish R L' U2 R' L F2
- Produces the following solution:
-@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)
-
diff --git a/docs/invert.txt b/docs/invert.txt
@@ -1,14 +0,0 @@
-
-HELP PAGE FOR COMMAND invert
-
-SYNTAX
-invert [MOVES|$ID|@ID]
-
-DESCRIPTION
-Inverts a sequence of moves, which can be given also as $ID or @ID. The given
-sequence must not use NISS (if it does, use the command unniss first).
-
-EXAMPLES
-invert F R D'
- Prints D R' F'
-
diff --git a/docs/nissy.txt b/docs/nissy.txt
@@ -1,88 +0,0 @@
-
-*******************************************************************************
-********************* NISSY: a cube solver and FMC helper *********************
-*******************************************************************************
-
-If you just want to solve the cube, type 'solve' followed by the scramble. This
-will not always give you an optimal solution, unless it is 10 moves or less or
-you use the "o" option. Finding the optimal solution might take very long if it
-is 16 moves or more, especially for the first time.
-
-Now the fun stuff. With nissy you can save and manipulate move sequences, for
-example:
-
-nissy-# save R' U' F
-$1: R' U' F
-nissy-# add L2D' $1
-$1: R' U' F L2 D'
-
-You can then ask nissy to solve certain substepson a saved scramble:
-
-nissy-# eo axis=rl $1
-@1: U D F' R (4)
-
-And of course it uses also NISS, if you ask:
-
-nissy-# eo niss axis=rl $1
-@1: (R) (1)
-
-Notice that the sequences you save are marked with a $, while the "output"
-sequences are marked with @. The difference between these two type of sequences
-is that those marked with @ are temporary and get lost once you get new output.
-Most commands accept as input either a move sequence typed out, a $-sequence or
-a @-sequence. For example, you can however save a @-sequence and make it
-persistent:
-
-nissy-# save @1
-$2: (R)
-
-Nissy also understands NISS. Let's see a more complicated example where you
-save a scramble, ask for some EOs (using NISS) and then a DR on inverse:
-
-nissy-# save R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F
-nissy-# eo n=10 niss axis=fb,rl $3
-Found 10 results.
-@1: (U' L B D F) (5)
-@2: (U' L B' D F) (5)
-@3: (L B U D F) (5)
-@4: (L B' U D F) (5)
-@5: R U B U L (5)
-@6: R U' L (B L) (5)
-@7: R U' B U L (5)
-@8: R L (L B L) (5)
-@9: R (U2 D' F R) (5)
-@10: R (U2 F D' R) (5)
-nissy-# add @6 $3
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L)
-nissy-# unniss $3
-@1: L' B' R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L
-nissy-# invert @1
-@1: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L
-nissy-# save @1
-$5: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L
-nissy-# dr from rl $5
-@1: F2 U D2 F' B D B (7)
-nissy-#
-
-If you ask nissy to solve a substep (or the whole cube) using a sequence with
-NISS as scramble, it will first un-NISS it (but without saving the unNISSed
-scramble anywhere):
-
-print $3
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L)
-nissy-# solve $3
-@1: F U' R2 F2 U2 F U2 R2 L2 D R2 U B2 D' L2 D B2 D2 (18)
-
-Nissy knows how to solve certain common sub-steps for DR (or Thistlethwaite /
-Kociemba algorithms). For now it does know more common speedsolving methods.
-
-For a full list of commands type "help". For a more detailed help on a specific
-command, type "help (command)". The help pages can also be found in the docs
-folder.
-
-If you want to report a bug (I'm sure there are many!) or give a suggestion,
-you can send an email to sebastiano.tronto@gmail.com.
-
-Have fun!
-
diff --git a/docs/pic.txt b/docs/pic.txt
@@ -1,20 +0,0 @@
-
-HELP PAGE FOR COMMAND pic
-
-SYNTAX
-pic [MOVES|$ID|@ID]
-
-DESCRIPTION
-Prints the cube state after applying the given scramble.
-
-EXAMPLES
-pic R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F
- Gives the following output:
- UF UL UB UR DF DL DB DR FR FL BL BR
-EP: FR UL FL UR UF DB DR BL UB BR DL DF
-EO(F/B): x x x x x x x x
-
- UFR UFL UBL UBR DFR DFL DBL DBR
-CP: UBR UFR DFL DBL UFL UBL DBR DFR
-CO(U/D): ccw cw ccw cw
-
diff --git a/docs/print.txt b/docs/print.txt
@@ -1,19 +0,0 @@
-
-HELP PAGE FOR COMMAND print
-
-SYNTAX
-print [$ID|@ID]
-
-DESCRIPTION
-Prints memorized sequences. If no argument is given, it prints all memorized
-scrambles ($ only). If $ID or @ID is specified, it only prints the relative
-memorized sequence.
-
-EXAMPLES
-print
- Prints a list of all memorized scrambles (only $).
-print $2
- Prints the second memorized scramble.
-print @13
- Prints the 13th sequence that was part of the output of the last command.
-
diff --git a/docs/quit.txt b/docs/quit.txt
@@ -1,9 +0,0 @@
-
-HELP PAGE FOR COMMAND quit
-
-SYNTAX
-quit
-
-DESCRIPTION
-Exits nissy.
-
diff --git a/docs/replace.txt b/docs/replace.txt
@@ -1,22 +0,0 @@
-
-HELP PAGE FOR COMMAND replace
-
-SYNTAX
-eo [OPTIONS] [MOVES|$ID|@ID]
-
-DESCRIPTION
-Looks for non-optimal subsequences and replaces them to shorten the given
-sequence. By default it tries to shorten every subsequence of up to 10 moves,
-but this can be change with the "b" option.
-It outputs at most 10 equivalent optimal sequences for each replaceable part.
-
-OPTIONS
-b=N Finds non-optimal subsequences of up to N moves.
-
-EXAMPLES
-replace D2 F' D2 U2 F' L2 R2 U' D B2 D B2 U B2 F L2 R' F' D U'
- Produces the following output:
-Replace [ R2 U' D B2 D B2 ] (moves 7-12) with: [ D R2 D U' ] (-6+4)
-Replace [ R2 U' D B2 D B2 U ] (moves 7-13) with: [ D R2 D ] (-7+3)
-Replace [ U' D B2 D B2 U ] (moves 8-13) with: [ R2 D R2 D ] (-6+4)
-
diff --git a/docs/save.txt b/docs/save.txt
@@ -1,22 +0,0 @@
-
-HELP PAGE FOR COMMAND save
-
-SYNTAX
-save [MOVES|@ID|$ID]
-
-DESCRIPTION
-Memorizes the scramble specified by MOVES, given as input or temporarily saved
-as @ID, where ID is a number ('help nissy' for for more on IDs). If an $ID is
-given, it makes a copy of the scramble. An identifier of the form $ID, where ID
-is a number, is assigned to the memorized scramble.
-
-EXAMPLES
-save R U R' U'
- Saves the scramble R U R' U'.
-save F (B)
- Saves the scramble F (B) (NISS notation).
-save @3
- Saves the third output sequence of the last command.
-save $2
- Makes a copy of the second saved scramble.
-
diff --git a/docs/scramble.txt b/docs/scramble.txt
@@ -1,22 +0,0 @@
-
-HELP PAGE FOR COMMAND scramble
-
-SYNTAX
-scramble [OPTIONS]
-
-DESCRIPTION
-Produces a random-state scramble. There are options to get a corners-only,
-edges-only or dr-state scramble.
-
-OPTIONS
-c Scrambles corners only (edges are solved).
-e Scrambles edges only (corners are solved).
-dr DR-state scramble. The DR is always on the U/D axis.
-
-
-EXAMPLES
-scramble
- Gives a random-state scramble
-scramble dr
- Gives a random-DR-state scramble
-
diff --git a/docs/solve.txt b/docs/solve.txt
@@ -1,33 +0,0 @@
-
-HELP PAGE FOR COMMAND solve
-
-SYNTAX
-solve [MOVES|$ID|@ID]
-
-DESCRIPTION
-Solves the given scramble, which can be given as a sequence of moves or as $ID
-or @ID. If none is given, the user can type it on the next line.
-The algorithm first tries to find a short (<=10 moves) solution, and then
-switches to a 2-step algorithm (unless the option "o" is specified, in which
-case it keeps looking for an optimal solution).
-The first time it uses the 2-step algorithm it needs to load some tables, which
-can take a few seconds. It runs much faster after that. If the option "o" is
-specified, the first time it loads some large tables, which can take a minute
-or two.
-
-OPTIONS
-b=N Only looks for solutions up to N moves.
-n=N Tries to find multiple solutions, at most N. Multiple
- Solutions will only be found if they are <=10 moves.
-o Looks for optimal solution.
-
-
-EXAMPLES
-solve R' U' F
- Solves the scramble R' U' F.
-solve o b=14 $1
- Tries to solve the scramble $1 optimally, but stops if no solution of 14
- moves or shorter is found.
-solve n=16 R L' U2 R' L F2
- Finds the 16 shortest solutions for the scramble above.
-
diff --git a/docs/unniss.txt b/docs/unniss.txt
@@ -1,14 +0,0 @@
-
-HELP PAGE FOR COMMAND unniss
-
-SYNTAX
-unniss [MOVES|$ID|@ID]
-
-DESCRIPTION
-Removes NISS from a sequence of moves, which can be given also as $ID or @ID.
-A sequence of the form A (B) is translated to B' A.
-
-EXAMPLES
-invert F R (D' L2)
- Prints L2 D F R
-
diff --git a/makedoc.sh b/makedoc.sh
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-OUT=./src/helppages.h
-DOCDIR=./docs
-N=$(ls -1 $DOCDIR | wc -l)
-
-# This deletes the content of the file
-echo "/* To generate this help page, use the script makedoc.sh */" > $OUT
-
-echo "" >> $OUT
-echo "int Npages = $N;" >> $OUT
-echo "" >> $OUT
-echo "char *helppages[][10] = {" >> $OUT
-
-for f in $(ls $DOCDIR)
-do
- name=$(echo "$f" | sed 's/\..*//')
- echo "" >> $OUT
- echo "{ \"$name\"," >> $OUT
-
- echo "\"\\" >> $OUT
- sed 's/$/\\n\\/;s/\"/\\\"/g' $DOCDIR/$f >> $OUT
- echo "\"" >> $OUT
-
- echo "}," >> $OUT
-done
-
-echo "};" >> $OUT
-
diff --git a/nissy b/nissy
Binary files differ.
diff --git a/nissy-2.0beta1.tar.gz b/nissy-2.0beta1.tar.gz
Binary files differ.
diff --git a/nissy-win.exe b/nissy-win.exe
Binary files differ.
diff --git a/old/2020-unknown-date/cube_backup.c b/old/2020-unknown-date/cube_backup.c
@@ -0,0 +1,527 @@
+#include <stdio.h>
+#include <string.h>
+#include "cube.h"
+
+/* The next few functions are used to convert from the Cube structure
+ * representation to actual arrays of pieces. */
+void cube_to_ep_array(Cube cube, int ep[12]);
+void cube_to_eofb_array(Cube cube, int eo[12]);
+void cube_to_eorl_array(Cube cube, int eo[12]);
+void cube_to_eoud_array(Cube cube, int eo[12]);
+void cube_to_cp_array(Cube cube, int cp[8]);
+void cube_to_coud_array(Cube cube, int co[8]);
+void cube_to_cofb_array(Cube cube, int co[8]);
+void cube_to_corl_array(Cube cube, int co[8]);
+void cube_to_centerpos_array(Cube cube, int centerpos[6]);
+Cube ep_array_to_cube(int ep[12]);
+Cube eofb_array_to_cube(int eo[12]);
+Cube eorl_array_to_cube(int eo[12]);
+Cube eoud_array_to_cube(int eo[12]);
+Cube cp_array_to_cube(int cp[8]);
+Cube coud_array_to_cube(int co[8]);
+Cube cofb_array_to_cube(int co[8]);
+Cube corl_array_to_cube(int co[8]);
+Cube centerpos_array_to_cube(int centerpos[6]);
+Cube move_array(Cube cube, void (cube_to_arr)(Cube, int *),
+ Cube (*arr_to_cube)(int *),
+ int *perm, int *orient, int n, int m);
+
+
+/* Transition tables */
+int epose_ttable[NMOVES][factorial12/factorial8];
+int eposs_ttable[NMOVES][factorial12/factorial8];
+int eposm_ttable[NMOVES][factorial12/factorial8];
+int eofb_ttable[NMOVES][pow2to11];
+int eorl_ttable[NMOVES][pow2to11];
+int eoud_ttable[NMOVES][pow2to11];
+int cp_ttable[NMOVES][factorial8];
+int coud_ttable[NMOVES][pow3to7];
+int cofb_ttable[NMOVES][pow3to7];
+int corl_ttable[NMOVES][pow3to7];
+int centerpos_ttable[NMOVES][factorial6];
+
+char edge_string[12][5] = {
+ "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR"
+};
+char corner_string[8][5] = { "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR" };
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+char move_string[NMOVES][5] = {
+ "-",
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'",
+};
+
+int epe_solved[] = {FR, FL, BL, BR};
+int eps_solved[] = {UL, UR, DL, DR};
+int epm_solved[] = {UF, UB, DF, DB};
+
+/**************************/
+/* Internal use functions */
+/**************************/
+
+int cube_to_ep_array(Cube cube, int ep[12]) {
+ int epe[4], eps[4], epm[4];
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+
+ int epose[12], eposs[12], eposm[12];
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) {
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ }
+
+ for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) {
+ if (epose[i]) ep[i] = epe_solved[epe[ie++]];
+ if (eposs[i]) ep[i] = eps_solved[eps[is++]];
+ if (eposm[i]) ep[i] = epm_solved[epm[im++]];
+ }
+}
+
+void cube_to_eofb_array(Cube cube, int eo[12]) {
+ int_to_sum_zero_array(cube.eofb, 2, 12, eo);
+}
+
+void cube_to_eorl_array(Cube cube, int eo[12]) {
+ int_to_sum_zero_array(cube.eorl, 2, 12, eo);
+}
+
+void cube_to_eoud_array(Cube cube, int eo[12]) {
+ int_to_sum_zero_array(cube.eoud, 2, 12, eo);
+}
+
+void cube_to_cp_array(Cube cube, int cp[8]) {
+ index_to_perm(cube.cp, 8, cp);
+}
+
+void cube_to_coud_array(Cube cube, int co[8]) {
+ int_to_sum_zero_array(cube.coud, 3, 8, co);
+}
+
+void cube_to_cofb_array(Cube cube, int co[8]) {
+ int_to_sum_zero_array(cube.cofb, 3, 8, co);
+}
+
+void cube_to_corl_array(Cube cube, int co[8]) {
+ int_to_sum_zero_array(cube.corl, 3, 8, co);
+}
+
+void cube_to_centerpos_array(Cube cube, int centerpos[6]) {
+ index_to_perm(cube.centerpos, 6, centerpos);
+}
+
+Cube ep_array_to_cube(int ep[12]) {
+ int epe[4], eps[4], epm[4];
+ int epose[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposs[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposm[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) {
+ for (int j = 0; j < 4; j++) {
+ if (ep[i] == epe_solved[j]) { epe[ie++] = j; epose[i] = 1; }
+ if (ep[i] == eps_solved[j]) { eps[is++] = j; eposs[i] = 1; }
+ if (ep[i] == epm_solved[j]) { epm[im++] = j; eposm[i] = 1; }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ }
+ return { .epose = factorial(4) * subset_to_index(epose, 12, 4)
+ + perm_to_index(epe, 4),
+ .eposs = factorial(4) * subset_to_index(eposs, 12, 4)
+ + perm_to_index(eps, 4),
+ .eposm = factorial(4) * subset_to_index(eposm, 12, 4)
+ + perm_to_index(epm, 4) };
+}
+
+Cube eofb_array_to_cube(int eo[12]) {
+ return { .eofb = digit_array_to_int(eo, 11, 2) };
+}
+
+Cube eorl_array_to_cube(int eo[12]) {
+ return { .eorl = digit_array_to_int(eo, 11, 2) };
+}
+
+Cube eoud_array_to_cube(int eo[12]) {
+ return { .eoud = digit_array_to_int(eo, 11, 2) };
+}
+
+Cube cp_array_to_cube(int cp[8]) {
+ return { .cp = perm_to_index(cp, 8) };
+}
+
+Cube coud_array_to_cube(int co[8]) {
+ return { .coud = digit_array_to_int(co, 7, 3) };
+}
+
+Cube cofb_array_to_cube(int co[8]) {
+ return { .cofb = digit_array_to_int(co, 7, 3) };
+}
+
+Cube corl_array_to_cube(int co[8]) {
+ return { .corl = digit_array_to_int(co, 7, 3) };
+}
+
+Cube centerpos_array_to_cube(int centerpos[6]) {
+ return { .centerpos = perm_to_index(centerpos, 6) };
+}
+
+Cube move_array(Cube cube, void (*cube_to_arr)(Cube, int *),
+ void (*arr_to_cube)(int *),
+ int *perm, int *orient, int n, int m) {
+ int arr[n];
+ cube_to_arr(cube, arr);
+ apply_permutation(perm, arr, n);
+ sum_arrays_mod(arr, orient, n, m);
+ return arr_to_cube(arr);
+}
+
+/***********************/
+/* Interface functions */
+/***********************/
+
+Move inverse(Move m) {
+ if (m == NULLMOVE)
+ return m;
+ int mod = (m-1)%3;
+ Move base = m - mod;
+ return base + 2 - mod;
+}
+
+void copy_alg(NissMove *src, NissMove *dest) {
+ for (int i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+}
+
+bool commute(Move m1, Move m2) {
+ return equal(apply_move(m2, apply_move(m1, {0})),
+ apply_move(m1, apply_move(m2, {0})));
+}
+
+bool equal(Cube c1, Cube c2) {
+ return c1.eofb == c2.eofb && c1.epose == c2.epose &&
+ c1.eposs == c2.eposs && c1.eposm == c2.eposm &&
+ c1.coud == c2.coud && c1.cp == c2.cp &&
+ c1.centerpos == c2.centerpos;
+}
+
+bool solvable(Cube cube) {
+ /* Since we memorize orientation truncating the last digit, we only need to
+ * check that the permutations have the correct sign. */
+ /* TODO: add check that every integer is in range */
+ int ep[12], cp[12], c[6];
+ /* TODO: change to use cube-specific functions */
+ cube_to_ep_array(cube, ep);
+ cube_to_cp_array(cube, 8, cp);
+ cube_to_centerpos_array(cube, 6, c);
+ return (perm_sign(ep, 12) ^ perm_sign(c, 6)) == perm_sign(cp, 8);
+}
+
+bool is_solved(Cube cube) {
+ return (!cube.eofb && !cube.coud && !cube.cp &&
+ !cube.epose && !cube.eposs && !cube.eposm && !cube.centerpos);
+}
+
+char *to_string(Cube cube) {
+ int eo[12], co[8], ep[12], cp[8], cenpos[6];
+ cube_to_eofb_array(cube, eo);
+ cube_to_coud_array(cube, co);
+ cube_to_ep_array(cube, ep);
+ cube_to_cp_array(cube, cp);
+ cube_to_centerpos_array(cube, cenpos);
+
+ static char ret[1000];
+
+ for (int i = 0; i < 12; i++) {
+ strcat(ret, " ");
+ strcat(ret, edge_string[ep[i]]);
+ strcat(ret, " ");
+ }
+ strcat(ret, "\n");
+ for (int i = 0; i < 12; i++) {
+ strcat(ret, " ");
+ char num[2] = { [0] = eo[i] + '0', [1] = 0 };
+ strcat(ret, num);
+ strcat(ret, " ");
+ }
+ strcat(ret, "\n");
+ for (int i = 0; i < 8; i++) {
+ strcat(ret, corner_string[cp[i]]);
+ strcat(ret, " ");
+ }
+ strcat(ret, "\n");
+ for (int i = 0; i < 8; i++) {
+ strcat(ret, " ");
+ char num[2] = { [0] = co[i] + '0', [1] = 0 };
+ strcat(ret, num);
+ strcat(ret, " ");
+ }
+ strcat(ret, "\n");
+ for (int i = 0; i < 6; i++) {
+ strcat(ret, " ");
+ strcat(ret, center_string[cenpos[i]]);
+ strcat(ret, " ");
+ }
+ strcat(ret, "\n");
+
+ return ret;
+}
+
+void init_ttables() {
+ FILE *ttf;
+
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ for (int m = 0; m < NMOVES; m++) {
+ fread(epose_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fread(eposs_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fread(eposm_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fread(eofb_ttable[m], sizeof(int), pow2to11, ttf);
+ fread(eorl_ttable[m], sizeof(int), pow2to11, ttf);
+ fread(eoud_ttable[m], sizeof(int), pow2to11, ttf);
+ fread(cp_ttable[m], sizeof(int), factorial8, ttf);
+ fread(coud_ttable[m], sizeof(int), pow3to7, ttf);
+ fread(corl_ttable[m], sizeof(int), pow3to7, ttf);
+ fread(cofb_ttable[m], sizeof(int), pow3to7, ttf);
+ fread(centerpos_ttable[m], sizeof(int), factorial6, ttf);
+ }
+ fclose(ttf);
+ } else {
+ ttf = fopen("ttables", "wb");
+ int empty[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ /* For each type of pieces only the effects of U, x and y are described */
+ int edge_cycle[NMOVES][12] = {
+ [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [X] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [Y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL},
+ };
+ int eofb_flipped[NMOVES][12] = {
+ [X] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [Y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 },
+ };
+ int eorl_flipped[NMOVES][12] = {
+ [X] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [Y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 },
+ };
+ int eoud_flipped[NMOVES][12] = {
+ [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [X] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [Y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ };
+ int corner_cycle[NMOVES][8] = {
+ [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [X] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [Y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL},
+ };
+ int coud_flipped[NMOVES][8] = {
+ [X] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2},
+ };
+ int corl_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [Y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1},
+ };
+ int cofb_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [X] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2},
+ [Y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2},
+ };
+ int center_cycle[NMOVES][6] = {
+ [X] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [Y] = {U_center, D_center, B_center, F_center, R_center, L_center},
+ };
+
+ /* Each move is reduced to a combination of U, x and y using this table */
+ Move equiv_moves[NMOVES][13] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { X, X, U, X, X, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { X, X, U, U, X, X, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { X, X, U, U, U, X, X, 0, 0, 0, 0, 0, 0 },
+ [R] = { Y, X, U, X, X, X, Y, Y, Y, 0, 0, 0, 0 },
+ [R2] = { Y, X, U, U, X, X, X, Y, Y, Y, 0, 0, 0 },
+ [R3] = { Y, X, U, U, U, X, X, X, Y, Y, Y, 0, 0 },
+ [L] = { Y, Y, Y, X, U, X, X, X, Y, 0, 0, 0, 0 },
+ [L2] = { Y, Y, Y, X, U, U, X, X, X, Y, 0, 0, 0 },
+ [L3] = { Y, Y, Y, X, U, U, U, X, X, X, Y, 0, 0 },
+ [F] = { X, U, X, X, X, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { X, U, U, X, X, X, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { X, U, U, U, X, X, X, 0, 0, 0, 0, 0, 0 },
+ [B] = { X, X, X, U, X, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { X, X, X, U, U, X, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { X, X, X, U, U, U, X, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { X, X, U, X, X, Y, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { X, X, U, U, X, X, Y, Y, 0, 0, 0, 0, 0 },
+ [Uw3] = { X, X, U, U, U, X, X, Y, Y, Y, 0, 0, 0 },
+ [Dw] = { U, Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { Y, Y, Y, X, U, X, X, X, Y, X, 0, 0, 0 },
+ [Rw2] = { Y, Y, Y, X, U, U, X, X, X, Y, X, X, 0 },
+ [Rw3] = { Y, Y, Y, X, U, U, U, Y, X, X, X, Y, 0 },
+ [Lw] = { Y, X, U, X, X, X, Y, Y, Y, X, X, X, 0 },
+ [Lw2] = { Y, X, U, U, X, X, X, Y, Y, Y, X, X, 0 },
+ [Lw3] = { Y, X, U, U, U, X, X, X, Y, Y, Y, X, 0 },
+ [Fw] = { X, X, X, U, Y, Y, Y, X, 0, 0, 0, 0, 0 },
+ [Fw2] = { X, X, X, U, U, Y, Y, X, 0, 0, 0, 0, 0 },
+ [Fw3] = { X, X, X, U, U, U, Y, X, 0, 0, 0, 0, 0 },
+ [Bw] = { X, U, Y, Y, Y, X, X, X, 0, 0, 0, 0, 0 },
+ [Bw2] = { X, U, U, Y, Y, X, X, X, 0, 0, 0, 0, 0 },
+ [Bw3] = { X, U, U, U, Y, X, X, X, 0, 0, 0, 0, 0 },
+
+ [M] = { Y, X, U, X, X, U, U, U, Y, X, Y, Y, Y },
+ [M2] = { Y, X, U, U, X, X, U, U, X, X, X, Y, 0 },
+ [M3] = { Y, X, U, U, U, X, X, U, Y, X, X, X, Y },
+ [S] = { X, U, U, U, X, X, U, Y, Y, Y, X, 0, 0 },
+ [S2] = { X, U, U, X, X, U, U, Y, Y, X, 0, 0, 0 },
+ [S3] = { X, U, X, X, U, U, U, Y, X, 0, 0, 0, 0 },
+ [E] = { U, X, X, U, U, U, X, X, Y, Y, Y, 0, 0 },
+ [E2] = { U, U, X, X, U, U, X, X, Y, Y, 0, 0, 0 },
+ [E3] = { U, U, U, X, X, U, X, X, Y, 0, 0, 0, 0 },
+
+ [X] = { X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [X2] = { X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [X3] = { X, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Y] = { Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Y2] = { Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Y3] = { Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Z] = { Y, Y, Y, X, Y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Z2] = { Y, Y, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Z3] = { Y, X, Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ };
+
+ /* Generate all move cycles and flips */
+ for (int i = 0; i < NMOVES; i++) {
+ if (i == U || i == X || i == Y)
+ continue;
+
+ Cube cube = {0};
+
+ cube_to_ep_array(&cube, edge_cycle[i]);
+ cube_to_eofb_array(&cube, eofb_flipped[i]);
+ cube_to_eorl_array(&cube, eorl_flipped[i]);
+ cube_to_eoud_array(&cube, eoud_flipped[i]);
+ cube_to_cp_array(&cube, corner_cycle[i]);
+ cube_to_coud_array(&cube, coud_flipped[i]);
+ cube_to_cofb_array(&cube, cofb_flipped[i]);
+ cube_to_corl_array(&cube, corl_flipped[i]);
+ cube_to_centerpos_array(&cube, center_cycle[i]);
+
+ for (int j = 0; equiv_moves[i][j]; j++) {
+ int m = equiv_moves[i][j];
+
+ apply_permutation(edge_cycle[m], edge_cycle[i], 12);
+ apply_permutation(edge_cycle[m], eofb_flipped[i], 12);
+ apply_permutation(edge_cycle[m], eorl_flipped[i], 12);
+ apply_permutation(edge_cycle[m], eoud_flipped[i], 12);
+
+ sum_arrays_mod(eofb_flipped[i], eofb_flipped[m], 12, 2);
+ sum_arrays_mod(eorl_flipped[i], eorl_flipped[m], 12, 2);
+ sum_arrays_mod(eoud_flipped[i], eoud_flipped[m], 12, 2);
+
+ apply_permutation(corner_cycle[m], corner_cycle[i], 8);
+ apply_permutation(corner_cycle[m], coud_flipped[i], 8);
+ apply_permutation(corner_cycle[m], cofb_flipped[i], 8);
+ apply_permutation(corner_cycle[m], corl_flipped[i], 8);
+
+ sum_arrays_mod(coud_flipped[i], coud_flipped[m], 8, 3);
+ sum_arrays_mod(corl_flipped[i], corl_flipped[m], 8, 3);
+ sum_arrays_mod(cofb_flipped[i], cofb_flipped[m], 8, 3);
+
+ apply_permutation(center_cycle[m], center_cycle[i], 6);
+ }
+ }
+
+ /* Initialize transition tables */
+ for (int m = 0; m < NMOVES; m++) {
+ for (int i = 0; i < factorial12/factorial8; i++) {
+ Cube cube = { .epose = i };
+ move_array(&cube, cube_to_ep_array, ep_array_to_cube,
+ edge_cycle[m], empty, 12, 1);
+ epose_ttable[m][i] = cube.epose;
+ cube.eposs = i;
+ move_array(&cube, cube_to_ep_array, ep_array_to_cube,
+ edge_cycle[m], empty, 12, 1);
+ eposs_ttable[m][i] = cube.eposs;
+ cube.eposm = i;
+ move_array(&cube, cube_to_ep_array, ep_array_to_cube,
+ edge_cycle[m], empty, 12, 1);
+ eposm_ttable[m][i] = cube.eposm;
+ }
+ for (int i = 0; i < pow2to11; i++ ) {
+ Cube cube = { .eofb = i, .eorl = i, .eoud = i };
+ move_array(&cube, cube_to_eofb_array, eofb_array_to_cube,
+ edge_cycle[m], eofb_flipped[m], 12, 2);
+ move_array(&cube, cube_to_eorl_array, eorl_array_to_cube,
+ edge_cycle[m], eorl_flipped[m], 12, 2);
+ move_array(&cube, cube_to_eoud_array, eoud_array_to_cube,
+ edge_cycle[m], eoud_flipped[m], 12, 2);
+ eofb_ttable[m][i] = cube.eofb;
+ eorl_ttable[m][i] = cube.eorl;
+ eoud_ttable[m][i] = cube.eoud;
+ }
+ for (int i = 0; i < factorial8; i++) {
+ /* TODO: Maybe do this by hand to optimize? */
+ Cube cube = { .cp = i };
+ move_array(&cube, cube_to_cp_array, ep_array_to_cube,
+ corner_cycle[m], empty, 8, 1);
+ cp_ttable[m][i] = cube.cp;
+ }
+ for (int i = 0; i < pow3to7; i++) {
+ Cube cube = { .coud = i, .corl = i, .cofb = i };
+ move_array(&cube, cube_to_coud_array, coud_array_to_cube,
+ corner_cycle[m], coud_flipped[m], 8, 3);
+ move_array(&cube, cube_to_corl_array, corl_array_to_cube,
+ corner_cycle[m], corl_flipped[m], 8, 3);
+ move_array(&cube, cube_to_cofb_array, cofb_array_to_cube,
+ corner_cycle[m], cofb_flipped[m], 8, 3);
+ coud_ttable[m][i] = cube.coud;
+ corl_ttable[m][i] = cube.corl;
+ cofb_ttable[m][i] = cube.cofb;
+ }
+ for (int i = 0; i < factorial6; i++) {
+ Cube cube = { .centerpos = i };
+ move_array(&cube, cube_to_centerpos_array, centerpos_array_to_cube,
+ center_cycle[m], empty, 6, 1);
+ centerpos_ttable[m][i] = cube.centerpos;
+ }
+ fwrite(epose_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fwrite(eposs_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fwrite(eposm_ttable[m], sizeof(int), factorial12/factorial8, ttf);
+ fwrite(eofb_ttable[m], sizeof(int), pow2to11, ttf);
+ fwrite(eorl_ttable[m], sizeof(int), pow2to11, ttf);
+ fwrite(eoud_ttable[m], sizeof(int), pow2to11, ttf);
+ fwrite(cp_ttable[m], sizeof(int), factorial8, ttf);
+ fwrite(coud_ttable[m], sizeof(int), pow3to7, ttf);
+ fwrite(corl_ttable[m], sizeof(int), pow3to7, ttf);
+ fwrite(cofb_ttable[m], sizeof(int), pow3to7, ttf);
+ fwrite(centerpos_ttable[m], sizeof(int), factorial6, ttf);
+ }
+ fclose(ttf);
+ }
+}
+
+Cube apply_move(Move m, Cube cube) {
+ Cube moved = cube;
+
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.centerpos = centerpos_ttable[m][cube.centerpos];
+
+ return moved;
+}
diff --git a/old/2020-unknown-date/cubeutils.c b/old/2020-unknown-date/cubeutils.c
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include "utils.h"
+#include "pieces.h"
+#include "cubeutils.h"
+
+int epe_solved[] = {FR, FL, BL, BR};
+int eps_solved[] = {UL, UR, DL, DR};
+int epm_solved[] = {UF, UB, DF, DB};
+
+void cube_to_ep_array(Cube *cube, int ep[12]) {
+ int epe[4], eps[4], epm[4];
+ index_to_perm(cube->epose % factorial(4), 4, epe);
+ index_to_perm(cube->eposs % factorial(4), 4, eps);
+ index_to_perm(cube->eposm % factorial(4), 4, epm);
+
+ int epose[12], eposs[12], eposm[12];
+ index_to_subset(cube->epose / factorial(4), 12, 4, epose);
+ index_to_subset(cube->eposs / factorial(4), 12, 4, eposs);
+ index_to_subset(cube->eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) {
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ }
+
+ for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) {
+ if (epose[i]) ep[i] = epe_solved[epe[ie++]];
+ if (eposs[i]) ep[i] = eps_solved[eps[is++]];
+ if (eposm[i]) ep[i] = epm_solved[epm[im++]];
+ }
+}
+
+void ep_array_to_epos(int ep[12], Cube *cube) {
+ int epe[4], eps[4], epm[4];
+ int epose[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposs[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposm[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) {
+ for (int j = 0; j < 4; j++) {
+ if (ep[i] == epe_solved[j]) { epe[ie++] = j; epose[i] = 1; }
+ if (ep[i] == eps_solved[j]) { eps[is++] = j; eposs[i] = 1; }
+ if (ep[i] == epm_solved[j]) { epm[im++] = j; eposm[i] = 1; }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ }
+ cube->epose = factorial(4) * subset_to_index(epose, 12, 4)
+ + perm_to_index(epe, 4);
+ cube->eposs = factorial(4) * subset_to_index(eposs, 12, 4)
+ + perm_to_index(eps, 4);
+ cube->eposm = factorial(4) * subset_to_index(eposm, 12, 4)
+ + perm_to_index(epm, 4);
+}
+
+bool solvable(Cube *cube) {
+ /* Since we memorize orientation truncating the last digit, we only need to
+ * check that the permutations have the correct sign. */
+ /* TODO: add check that every integer is in range */
+ int ep[12], cp[12], c[6];
+ cube_to_ep_array(cube, ep);
+ index_to_perm(cube->cp, 8, cp);
+ index_to_perm(cube->centerpos, 6, c);
+ return (perm_sign(ep, 12) ^ perm_sign(c, 6)) == perm_sign(cp, 8);
+}
+
+Cube *solved_cube() {
+ static Cube cube = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ return &cube;
+}
+
+bool solved(Cube *cube) {
+ return (cube->eofb == 0 && cube->coud == 0 && cube->cp == 0 &&
+ cube->epose == 0 && cube->eposs == 0 && cube->eposm == 0 &&
+ cube->centerpos == 0);
+}
+
+char *to_string(Cube *cube) {
+ int eo[12], co[8], ep[12], cp[8], cenpos[6];
+ cube_to_eofb_array(cube->eofb, eo);
+ cube_to_coud_array(cube->coud, co);
+ cube_to_ep_array(cube, ep);
+ cube_to_cp_array(cube->cp, cp);
+ cube_to_centerpos_array(cube->centerpos, cenpos);
+
+ char *ret[1000];
+
+ for (int i = 0; i < 12; i++) sprintf(ret, " %s ", edge_string[ep[i]]);
+ sprintf(ret, "\n");
+ for (int i = 0; i < 12; i++) sprintf(ret, " %d ", eo[i]);
+ sprintf(ret, "\n");
+ for (int i = 0; i < 8; i++) sprintf(ret, "%s ", corner_string[cp[i]]);
+ sprintf(ret, "\n");
+ for (int i = 0; i < 8; i++) sprintf(ret, " %d ", co[i]);
+ sprintf(ret, "\n");
+ for (int i = 0; i < 6; i++) sprintf(ret, " %s ", center_string[cenpos[i]]);
+ sprintf(ret, "\n");
+
+ return ret;
+}
diff --git a/old/2020-unknown-date/cubeutils.h b/old/2020-unknown-date/cubeutils.h
@@ -0,0 +1,18 @@
+/* Cube-specific utility functions */
+
+#ifndef CUBEUTILS_H
+#define CUBEUTILS_H
+
+#include <stdbool.h>
+#include "pieces.h"
+
+void cube_to_ep_array(Cube *cube, int ep[12]);
+void ep_array_to_epos(int ep[12], Cube *cube);
+
+Cube *solved_cube();
+bool solvable(Cube *cube);
+bool solved(Cube *cube);
+
+void print(Cube *cube);
+
+#endif
diff --git a/old/2020-unknown-date/moves.c b/old/2020-unknown-date/moves.c
@@ -0,0 +1,57 @@
+#include <stdbool.h>
+#include "pieces.h"
+#include "moves.h"
+#include "utils.h"
+
+/* Transition tables */
+int eofb_ttable[pow2to11][NULLMOVE];
+int eorl_ttable[pow2to11][NULLMOVE];
+int eoud_ttable[pow2to11][NULLMOVE];
+int coud_ttable[pow3to7][NULLMOVE];
+int cofb_ttable[pow3to7][NULLMOVE];
+int corl_ttable[pow3to7][NULLMOVE];
+int epose_ttable[factorial12/factorial8][NULLMOVE];
+int eposs_ttable[factorial12/factorial8][NULLMOVE];
+int eposm_ttable[factorial12/factorial8][NULLMOVE];
+int cp_ttable[factorial8][NULLMOVE];
+int centerpos_ttable[factorial6][NULLMOVE];
+
+void apply_move_ep(Move m, int a[12]) {
+ int aux[12];
+ intarrcopy(a, aux, 12);
+ for (int i = 0; i < 12; i++)
+ a[i] = aux[edge_cycle[m][i]];
+}
+
+void apply_move_cp(Move m, int a[8]) {
+ int aux[8];
+ intarrcopy(a, aux, 8);
+ for (int i = 0; i < 8; i++)
+ a[i] = aux[corner_cycle[m][i]];
+}
+
+void apply_move_centerpos(Move m, int a[6]) {
+ int aux[6];
+ intarrcopy(a, aux, 6);
+ for (int i = 0; i < 6; i++)
+ a[i] = aux[center_cycle[m][i]];
+}
+
+void init_ttables() {
+ /* TODO */
+}
+
+void apply_move(Move m, Cube *cube) {
+ cube->eofb = eofb_ttable[cube->eofb][m];
+ cube->eorl = eorl_ttable[cube->eorl][m];
+ cube->eoud = eoud_ttable[cube->eoud][m];
+ cube->coud = coud_ttable[cube->coud][m];
+ cube->cofb = cofb_ttable[cube->cofb][m];
+ cube->corl = corl_ttable[cube->corl][m];
+ cube->epose = epose_ttable[cube->epose][m];
+ cube->eposs = eposs_ttable[cube->eposs][m];
+ cube->eposm = eposm_ttable[cube->eposm][m];
+ cube->cp = cp_ttable[cube->cp][m];
+ cube->centerpos = centerpos_ttable[cube->centerpos][m];
+}
+
diff --git a/old/2020-unknown-date/moves.h b/old/2020-unknown-date/moves.h
@@ -0,0 +1,9 @@
+/* Moves and transition tables */
+
+#ifndef MOVES_H
+#define MOVES_H
+
+void apply_move(Move m, Cube *cube);
+void init_ttables();
+
+#endif
diff --git a/old/2020-unknown-date/pieces.c b/old/2020-unknown-date/pieces.c
@@ -0,0 +1,208 @@
+#include "pieces.h"
+
+char edge_string[12][5] = {
+ "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR"
+};
+
+char corner_string[8][5] = {
+ "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR"
+};
+
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+
+char move_string[NULLMOVE+1][5] = {
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'",
+ "-"
+};
+
+int edge_cycle[NULLMOVE+1][12] = {
+ [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [U2] = {UB, UR, UF, UL, DF, DL, DB, DR, FR, FL, BL, BR},
+ [U3] = {UL, UB, UR, UF, DF, DL, DB, DR, FR, FL, BL, BR},
+ [D] = {UF, UL, UB, UR, DL, DB, DR, DF, FR, FL, BL, BR},
+ [D2] = {UF, UL, UB, UR, DB, DR, DF, DL, FR, FL, BL, BR},
+ [D3] = {UF, UL, UB, UR, DR, DF, DL, DB, FR, FL, BL, BR},
+ [R] = {UF, UL, UB, FR, DF, DL, DB, BR, DR, FL, BL, UR},
+ [R2] = {UF, UL, UB, DR, DF, DL, DB, UR, BR, FL, BL, FR},
+ [R3] = {UF, UL, UB, BR, DF, DL, DB, FR, UR, FL, BL, DR},
+ [L] = {UF, BL, UB, UR, DF, FL, DB, DR, FR, UL, DL, BR},
+ [L2] = {UF, DL, UB, UR, DF, UL, DB, DR, FR, BL, FL, BR},
+ [L3] = {UF, FL, UB, UR, DF, BL, DB, DR, FR, DL, UL, BR},
+ [F] = {FL, UL, UB, UR, FR, DL, DB, DR, UF, DF, BL, BR},
+ [F2] = {DF, UL, UB, UR, UF, DL, DB, DR, FL, FR, BL, BR},
+ [F3] = {FR, UL, UB, UR, FL, DL, DB, DR, DF, UF, BL, BR},
+ [B] = {UF, UL, BR, UR, DF, DL, BL, DR, FR, FL, UB, DB},
+ [B2] = {UF, UL, DB, UR, DF, DL, UB, DR, FR, FL, BR, BL},
+ [B3] = {UF, UL, BL, UR, DF, DL, BR, DR, FR, FL, DB, UB},
+
+ [Uw] = {UR, UF, UL, UB, DF, DL, DB, DR, BR, FR, FL, BL},
+ [Uw2] = {UB, UR, UF, UL, DF, DL, DB, DR, BL, BR, FR, FL},
+ [Uw3] = {UL, UB, UR, UF, DF, DL, DB, DR, FL, BL, BR, FR},
+ [Dw] = {UF, UL, UB, UR, DL, DB, DR, DF, FL, BL, BR, FR},
+ [Dw2] = {UF, UL, UB, UR, DB, DR, DF, DL, BL, BR, FR, FL},
+ [Dw3] = {UF, UL, UB, UR, DR, DF, DL, DB, BR, FR, FL, BL},
+ [Rw] = {DF, UL, UF, FR, DB, DL, UB, BR, DR, FL, BL, UR},
+ [Rw2] = {DB, UL, DF, DR, UB, DL, UF, UR, BR, FL, BL, FR},
+ [Rw3] = {UB, UL, DB, BR, UF, DL, DF, FR, UR, FL, BL, DR},
+ [Lw] = {UB, BL, DB, UR, UF, FL, DF, DR, FR, UL, DL, BR},
+ [Lw2] = {DB, DL, DF, UR, UB, UL, UF, DR, FR, BL, FL, BR},
+ [Lw3] = {DF, FL, UF, UR, DB, BL, UB, DR, FR, DL, UL, BR},
+ [Fw] = {FL, DL, UB, UL, FR, DR, DB, UR, UF, DF, BL, BR},
+ [Fw2] = {DF, DR, UB, DL, UF, UR, DB, UL, FL, FR, BL, BR},
+ [Fw3] = {FR, UR, UB, DR, FL, UL, DB, DL, DF, UF, BL, BR},
+ [Bw] = {UF, UR, BR, DR, DF, UL, BL, DL, FR, FL, UB, DB},
+ [Bw2] = {UF, DR, DB, DL, DF, UR, UB, UL, FR, FL, BR, BL},
+ [Bw3] = {UF, DL, BL, UL, DF, DR, BR, UR, FR, FL, DB, UB},
+
+ [M] = {UB, UL, DB, UR, UF, DL, DF, DR, FR, FL, BL, BR},
+ [M2] = {DB, UL, DF, UR, UB, DL, UF, DR, FR, FL, BL, BR},
+ [M3] = {DF, UL, UF, UR, DB, DL, UB, DR, FR, FL, BL, BR},
+ [S] = {UF, DL, UB, UL, DF, DR, DB, UR, FR, FL, BL, BR},
+ [S2] = {UF, DR, UB, DL, DF, UR, DB, UL, FR, FL, BL, BR},
+ [S3] = {UF, UR, UB, DR, DF, UL, DB, DL, FR, FL, BL, BR},
+ [E] = {UF, UL, UB, UR, DF, DL, DB, DR, FL, BL, BR, FR},
+ [E2] = {UF, UL, UB, UR, DF, DL, DB, DR, BL, BR, FR, FL},
+ [E3] = {UF, UL, UB, UR, DF, DL, DB, DR, BR, FR, FL, BL},
+
+ [X] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [X2] = {DB, DL, DF, DR, UB, UL, UF, UR, BR, BL, FL, FR},
+ [X3] = {UB, BL, DB, BR, UF, FL, DF, FR, UR, UL, DL, DR},
+ [Y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL},
+ [Y2] = {UB, UR, UF, UL, DB, DR, DF, DL, BL, BR, FR, FL},
+ [Y3] = {UL, UB, UR, UF, DL, DB, DR, DF, FL, BL, BR, FR},
+ [Z] = {FL, DL, BL, UL, FR, DR, BR, UR, UF, DF, DB, UB},
+ [Z2] = {DF, DR, DB, DL, UF, UR, UB, UL, FL, FR, BR, BL},
+ [Z3] = {FR, UR, BR, DR, FL, UL, BL, DL, DF, UF, UB, DB},
+
+ [NULLMOVE] = {UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR},
+};
+
+int corner_cycle[NULLMOVE+1][8] = {
+ [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [U2] = {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR},
+ [U3] = {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR},
+ [D] = {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR},
+ [D2] = {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL},
+ [D3] = {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL},
+ [R] = {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR},
+ [R2] = {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR},
+ [R3] = {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR},
+ [L] = {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR},
+ [L2] = {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR},
+ [L3] = {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR},
+ [F] = {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR},
+ [F2] = {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR},
+ [F3] = {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR},
+ [B] = {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL},
+ [B2] = {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL},
+ [B3] = {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR},
+
+ [Uw] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [Uw2] = {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR},
+ [Uw3] = {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR},
+ [Dw] = {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR},
+ [Dw2] = {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL},
+ [Dw3] = {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL},
+ [Rw] = {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR},
+ [Rw2] = {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR},
+ [Rw3] = {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR},
+ [Lw] = {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR},
+ [Lw2] = {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR},
+ [Lw3] = {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR},
+ [Fw] = {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR},
+ [Fw2] = {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR},
+ [Fw3] = {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR},
+ [Bw] = {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL},
+ [Bw2] = {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL},
+ [Bw3] = {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR},
+
+ [M] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [M2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [M3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [S] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [S2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [S3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [E] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [E2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+ [E3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+
+ [X] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [X2] = {DBR, DBL, DFL, DFR, UBR, UBL, UFL, UFR},
+ [X3] = {UBR, UBL, DBL, DBR, UFR, UFL, DFL, DFR},
+ [Y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL},
+ [Y2] = {UBL, UBR, UFR, UFL, DBL, DBR, DFR, DFL},
+ [Y3] = {UFL, UBL, UBR, UFR, DFL, DBL, DBR, DFR},
+ [Z] = {UFL, DFL, DBL, UBL, UFR, DFR, DBR, UBR},
+ [Z2] = {DFL, DFR, DBR, DBL, UFL, UFR, UBR, UBL},
+ [Z3] = {DFR, UFR, UBR, DBR, DFL, UFL, UBL, DBL},
+
+ [NULLMOVE] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR},
+};
+
+int center_cycle[NULLMOVE+1][6] = {
+ [U] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [U2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [U3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [D] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [D2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [D3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [R] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [R2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [R3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [L] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [L2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [L3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [F] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [F2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [F3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [B] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [B2] = {U_center, D_center, R_center, L_center, F_center, B_center},
+ [B3] = {U_center, D_center, R_center, L_center, F_center, B_center},
+
+ [Uw] = {U_center, D_center, B_center, F_center, R_center, L_center},
+ [Uw2] = {U_center, D_center, L_center, R_center, B_center, F_center},
+ [Uw3] = {U_center, D_center, F_center, B_center, L_center, R_center},
+ [Dw] = {U_center, D_center, F_center, B_center, L_center, R_center},
+ [Dw2] = {U_center, D_center, L_center, R_center, B_center, F_center},
+ [Dw3] = {U_center, D_center, B_center, F_center, R_center, L_center},
+ [Rw] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [Rw2] = {D_center, U_center, R_center, L_center, B_center, F_center},
+ [Rw3] = {B_center, F_center, R_center, L_center, U_center, D_center},
+ [Lw] = {B_center, F_center, R_center, L_center, U_center, D_center},
+ [Lw2] = {D_center, U_center, R_center, L_center, B_center, F_center},
+ [Lw3] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [Fw] = {L_center, R_center, U_center, D_center, F_center, B_center},
+ [Fw2] = {D_center, U_center, L_center, R_center, F_center, B_center},
+ [Fw3] = {R_center, L_center, D_center, U_center, F_center, B_center},
+ [Bw] = {R_center, L_center, D_center, U_center, F_center, B_center},
+ [Bw2] = {D_center, U_center, L_center, R_center, F_center, B_center},
+ [Bw3] = {L_center, R_center, U_center, D_center, F_center, B_center},
+
+ [X] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [X2] = {D_center, U_center, R_center, L_center, B_center, F_center},
+ [X3] = {B_center, F_center, R_center, L_center, U_center, D_center},
+ [Y] = {U_center, D_center, B_center, F_center, R_center, L_center},
+ [Y2] = {U_center, D_center, L_center, R_center, B_center, F_center},
+ [Y3] = {U_center, D_center, F_center, B_center, L_center, R_center},
+ [Z] = {L_center, R_center, U_center, D_center, F_center, B_center},
+ [Z2] = {D_center, U_center, L_center, R_center, F_center, B_center},
+ [Z3] = {R_center, L_center, D_center, U_center, F_center, B_center},
+
+ [M] = {B_center, F_center, R_center, L_center, U_center, D_center},
+ [M2] = {D_center, U_center, R_center, L_center, B_center, F_center},
+ [M3] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [E] = {U_center, D_center, F_center, B_center, L_center, R_center},
+ [E2] = {U_center, D_center, L_center, R_center, B_center, F_center},
+ [E3] = {U_center, D_center, B_center, F_center, R_center, L_center},
+ [S] = {L_center, R_center, U_center, D_center, F_center, B_center},
+ [S2] = {D_center, U_center, L_center, R_center, F_center, B_center},
+ [S3] = {R_center, L_center, D_center, U_center, F_center, B_center},
+
+ [NULLMOVE] = {U_center, D_center, R_center, L_center, F_center, B_center},
+};
+
diff --git a/old/2020-unknown-date/pieces.h b/old/2020-unknown-date/pieces.h
@@ -0,0 +1,59 @@
+/* Moves and other things */
+
+#ifndef PIECES_H
+#define PIECES_H
+
+typedef enum {
+ U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3,
+ Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3, S, S2, S3, E, E2, E3,
+ X, X2, X3, Y, Y2, Y3, Z, Z2, Z3,
+ NULLMOVE
+} Move;
+typedef enum {
+ U_center, D_center, R_center, L_center, F_center, B_center
+} Center;
+typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge;
+typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner;
+
+/* Arrays for strings of pieces and moves */
+extern char edge_string[12][5];
+extern char corner_string[8][5];
+extern char center_string[6][5];
+extern char move_string[NULLMOVE+1][5];
+
+/* Three big arrays that determine the movement of edges, corners and
+ * centers for some of the basic moves. Yes, one can be much more elegant and
+ * write much fewer lines. But then things get less elegant somewhere else. */
+extern int edge_cycle[NULLMOVE+1][12];
+extern int corner_cycle[NULLMOVE+1][8];
+extern int center_cycle[NULLMOVE+1][6];
+
+/* Representation of the cube */
+typedef struct {
+ /* Edge orientation */
+ int eofb;
+ int eorl;
+ int eoud;
+
+ /* Corner orientation */
+ int coud;
+ int cofb;
+ int corl;
+
+ /* Position of M/E/S slice edges */
+ /* TODO: keep in mind that the 4! positions with edges in correct slice
+ * must be the first 4! indeces */
+ int epose;
+ int eposs;
+ int eposm;
+
+ /* Permutation of corners */
+ int cp;
+
+ /* Position of centers */
+ int centerpos;
+} Cube;
+
+#endif
diff --git a/old/2021-02-06/cube.c b/old/2021-02-06/cube.c
@@ -0,0 +1,664 @@
+#include "cube.h"
+
+typedef struct {
+ int ep[12], eofb[12], eorl[12], eoud[12],
+ cp[8], coud[8], corl[8], cofb[8], cpos[6];
+} CubeArray;
+
+typedef struct {
+ bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos;
+} PieceFilter;
+
+PieceFilter fAll = {true,true,true,true,true,true,true,true,true,true,true};
+PieceFilter cpos_only = { .cpos = true };
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+Cube arrays_to_cube(CubeArray arr, PieceFilter f);
+void move_cubearray(Move m, CubeArray *arr, PieceFilter f);
+Cube move_via_array(Move m, Cube cube, PieceFilter f);
+void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front);
+bool read_ttables_file();
+bool write_ttables_file();
+
+/* Transition tables */
+uint16_t epose_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposs_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposm_ttable[NMOVES][factorial12/factorial8];
+uint16_t eofb_ttable[NMOVES][pow2to11];
+uint16_t eorl_ttable[NMOVES][pow2to11];
+uint16_t eoud_ttable[NMOVES][pow2to11];
+uint16_t cp_ttable[NMOVES][factorial8];
+uint16_t coud_ttable[NMOVES][pow3to7];
+uint16_t cofb_ttable[NMOVES][pow3to7];
+uint16_t corl_ttable[NMOVES][pow3to7];
+uint16_t cpos_ttable[NMOVES][factorial6];
+
+bool commute[NMOVES][NMOVES];
+bool possible_next[NMOVES][NMOVES][NMOVES];
+Move inverse[NMOVES];
+
+char edge_string[12][5] =
+ { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" };
+char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" };
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+char move_string[NMOVES][5] =
+ { "-",
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" };
+
+/* For each type of pieces only the effects of U, x and y are described */
+int edge_cycle[NMOVES][12] =
+ { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} };
+int eofb_flipped[NMOVES][12] =
+ { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eorl_flipped[NMOVES][12] =
+ { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eoud_flipped[NMOVES][12] =
+ { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
+int corner_cycle[NMOVES][8] =
+ { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} };
+int coud_flipped[NMOVES][8] =
+ { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} };
+int corl_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} };
+int cofb_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2},
+ [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} };
+int center_cycle[NMOVES][6] =
+ { [x] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [y] = {U_center, D_center, B_center, F_center, R_center, L_center} };
+
+/* Each move is reduced to a combination of U, x and y using this table */
+Move equiv_moves[NMOVES][14] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 },
+ [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 },
+ [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 },
+ [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 },
+ [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 },
+ [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 },
+ [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 },
+ [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 },
+ [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 },
+ [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 },
+ [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 },
+ [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 },
+ [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 },
+ [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 },
+ [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 },
+ [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+
+ [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 },
+ [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 },
+ [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 },
+ [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 },
+ [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 },
+ [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 },
+ [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 },
+ [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 },
+ [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 },
+
+ [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* Movesets */
+bool standard_moveset[NMOVES] = {
+ [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true,
+ [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true,
+ [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true,
+};
+
+int epe_solved[] = {FR, FL, BL, BR};
+int eps_solved[] = {UL, UR, DL, DR};
+int epm_solved[] = {UF, UB, DF, DB};
+
+Cube blank_cube() {
+ Cube c = {0};
+ return c;
+}
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) {
+ /* ep is the hardest */
+ if (f.epose || f.eposs || f.eposm)
+ for (int i = 0; i < 12; i++) arr->ep[i] = -1;
+ if (f.epose) {
+ int epe[4], epose[12];
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ for (int i = 0, ie = 0; i < 12; i++)
+ if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]];
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12];
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ for (int i = 0, is = 0; i < 12; i++)
+ if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]];
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12];
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ for (int i = 0, im = 0; i < 12; i++)
+ if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]];
+ }
+
+ /* All the others */
+ if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp) index_to_perm( cube.cp, 8, arr->cp);
+ if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos);
+}
+
+Cube arrays_to_cube(CubeArray arr, PieceFilter f) {
+ Cube ret = {0};
+
+ /* Again, ep is the hardest part */
+ if (f.epose) {
+ int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, ie = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epe_solved[j])
+ { epe[ie++] = j; epose[i] = 1; }
+ ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4);
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, is = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == eps_solved[j])
+ { eps[is++] = j; eposs[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4);
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, im = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epm_solved[j])
+ { epm[im++] = j; eposm[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4);
+ }
+ if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2);
+ if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2);
+ if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2);
+ if (f.cp) ret.cp = perm_to_index( arr.cp, 8 );
+ if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3);
+ if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3);
+ if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3);
+ if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 );
+
+ return ret;
+}
+
+void move_cubearray(Move m, CubeArray *arr, PieceFilter f) {
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(edge_cycle[m], arr->ep, 12);
+ if (f.eofb) {
+ apply_permutation(edge_cycle[m], arr->eofb, 12);
+ sum_arrays_mod(eofb_flipped[m], arr->eofb, 12, 2);
+ }
+ if (f.eofb) {
+ apply_permutation(edge_cycle[m], arr->eorl, 12);
+ sum_arrays_mod(eorl_flipped[m], arr->eorl, 12, 2);
+ }
+ if (f.eofb) {
+ apply_permutation(edge_cycle[m], arr->eoud, 12);
+ sum_arrays_mod(eoud_flipped[m], arr->eoud, 12, 2);
+ }
+ if (f.cp)
+ apply_permutation(corner_cycle[m], arr->cp, 8);
+ if (f.coud) {
+ apply_permutation(corner_cycle[m], arr->coud, 8);
+ sum_arrays_mod(coud_flipped[m], arr->coud, 8, 3);
+ }
+ if (f.corl) {
+ apply_permutation(corner_cycle[m], arr->corl, 8);
+ sum_arrays_mod(corl_flipped[m], arr->corl, 8, 3);
+ }
+ if (f.cofb) {
+ apply_permutation(corner_cycle[m], arr->cofb, 8);
+ sum_arrays_mod(cofb_flipped[m], arr->cofb, 8, 3);
+ }
+ if (f.cpos)
+ apply_permutation(center_cycle[m], arr->cpos, 6);
+}
+
+Cube move_via_array(Move m, Cube cube, PieceFilter f) {
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, f);
+ move_cubearray(m, &arr, f);
+ return arrays_to_cube(arr, f);
+}
+
+int copy_alg(NissMove *src, NissMove *dest) {
+ int i;
+ for (i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+ dest[i].m = NULLMOVE;
+ return i;
+}
+
+bool equal(Cube c1, Cube c2) {
+ return c1.eofb == c2.eofb && c1.epose == c2.epose &&
+ c1.eposs == c2.eposs && c1.eposm == c2.eposm &&
+ c1.coud == c2.coud && c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+bool solvable(Cube cube) {
+ /* Since we memorize orientation truncating the last digit, we only need to
+ * check that the permutations have the correct sign. */
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, fAll);
+ return (perm_sign(arr.ep,12) ^ perm_sign(arr.cpos,6)) == perm_sign(arr.cp,8);
+}
+
+bool is_solved(Cube cube, bool reorient) {
+ if (!reorient || !cube.cpos)
+ return !cube.eofb && !cube.coud && !cube.cp &&
+ !cube.epose && !cube.eposs && !cube.eposm;
+
+ bool ret = false;
+ for (int i = x; i <= z3; i++) {
+ ret = ret || is_solved(move_cube(i, cube), false);
+ for (int j = x; j <= z3; j++)
+ ret = ret || is_solved(move_cube(i, move_cube(j, cube)), false);
+ }
+ return ret;
+}
+
+void print_cube(Cube cube) {
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, fAll);
+
+ for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arr.ep[i]]);
+ printf("\n");
+ for (int i = 0; i < 12; i++) printf(" %c ", arr.eofb[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf("%s ", corner_string[arr.cp[i]]);
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf(" %c ", arr.coud[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 6; i++) printf(" %s ", center_string[arr.cpos[i]]);
+ printf("\n");
+}
+
+/* TODO: all strings start with space?? */
+void print_moves(NissMove *alg) {
+ bool niss = false;
+ for (int i = 0; alg[i].m != NULLMOVE; i++) {
+ char *fill = !niss && alg[i].inverse ? " (" :
+ (niss && !alg[i].inverse ? ") " : " ");
+ printf("%s%s", fill, move_string[alg[i].m]);
+ niss = alg[i].inverse;
+ }
+ printf("%s\n", niss ? ")" : "");
+}
+
+int read_moves(char *str, NissMove *alg, int n) {
+ bool niss = false;
+ int c = 0;
+
+ for (int i = 0; str[i] && c < n; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' || str[i] == ')') {
+ if ((niss && str[i] == '(') || (!niss && str[i] == ')'))
+ return -1;
+ niss = !niss;
+ continue;
+ }
+
+ alg[c].inverse = niss; alg[c].m = NULLMOVE;
+ for (Move j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ alg[c].m = j;
+ if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; }
+ if (str[i+1]=='2') { alg[c].m += 1; i++; }
+ else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; }
+ c++;
+ break;
+ }
+ }
+ }
+
+ alg[c].m = NULLMOVE;
+ return c;
+}
+
+/* Helper function for cleanup. alg must contain only basic moves, no 2 or '.
+ top and front describe an admissible orientation of the cube. */
+void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) {
+ int c = 0, i = 0;
+ NissMove aux[n+3];
+ aux[0].m = NULLMOVE;
+
+ while (i < n && alg[i].m != NULLMOVE) {
+ int j = i;
+ while (j < n && commute[alg[i].m][alg[j].m]) j++;
+ Move base = 6*((alg[i].m-1)/6);
+ int t1 = 0, t2 = 0;
+ for (int k = i; k < j; k++)
+ if (alg[k].m == base+1) t1 = (t1+1)%4;
+ else t2 = (t2+1)%4;
+ if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; }
+ if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; }
+ i = j;
+ }
+ aux[c].m = NULLMOVE;
+
+ CubeArray q;
+ cube_to_arrays(blank_cube(), &q, cpos_only);
+ /* First we try to rotate in one move, then we try an x or y rotation
+ followed by a z rotation */
+ for (int r = x; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front && q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c].m = r;
+
+ aux[++c].m = NULLMOVE;
+ copy_alg(aux, alg);
+ return;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = x; r <= y3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = z; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+
+ aux[c].m = NULLMOVE;
+ copy_alg(aux, alg);
+}
+
+void cleanup(NissMove *alg, int n) {
+ int count_n = 0, count_i = 0, *count;
+ NissMove aux_n[n+1], aux_i[n+1], *aux;
+ CubeArray cube_n, cube_i, *cube;
+ cube_to_arrays(blank_cube(), &cube_n, cpos_only);
+ cube_to_arrays(blank_cube(), &cube_i, cpos_only);
+
+ for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) {
+ if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; }
+ else { count = &count_n; aux = aux_n; cube = &cube_n; }
+
+ for (int j = 0; equiv_moves[alg[i].m][j]; j++) {
+ Move m = equiv_moves[alg[i].m][j];
+ aux[*count].inverse = alg[i].inverse;
+ move_cubearray(m, cube, cpos_only);
+ if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1;
+ }
+ }
+
+ aux_n[count_n].m = NULLMOVE;
+ aux_i[count_i].m = NULLMOVE;
+ sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]);
+ sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]);
+ copy_alg(aux_n, alg);
+ copy_alg(aux_i, alg+count_n);
+}
+
+Cube inverse_cube(Cube cube) {
+ CubeArray arr = {0}, inv = {0};
+ cube_to_arrays(cube, &arr, fAll);
+
+ for (int i = 0; i < 12; i++) {
+ inv.ep[arr.ep[i]] = i;
+ inv.eofb[arr.ep[i]] = arr.eofb[i];
+ inv.eorl[arr.ep[i]] = arr.eorl[i];
+ inv.eoud[arr.ep[i]] = arr.eoud[i];
+ }
+ for (int i = 0; i < 8; i++) {
+ inv.cp[arr.cp[i]] = i;
+ inv.coud[arr.cp[i]] = arr.coud[i];
+ inv.corl[arr.cp[i]] = arr.corl[i];
+ inv.cofb[arr.cp[i]] = arr.cofb[i];
+ }
+ for (int i = 0; i < 6; i++)
+ inv.cpos[arr.cpos[i]] = i;
+
+ return arrays_to_cube(inv, fAll);
+}
+
+bool read_ttables_file() {
+ FILE *ttf;
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ for (int m = 0; m < NMOVES; m++) {
+ fread(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(cp_ttable[m], sizeof(uint16_t), factorial8, ttf);
+ fread(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf);
+ }
+ fclose(ttf);
+ return true;
+ } else return false;
+}
+
+bool write_ttables_file() {
+ FILE *ttf;
+ if ((ttf = fopen("ttables", "wb")) != NULL) {
+ for (int m = 0; m < NMOVES; m++) {
+ fwrite(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(cp_ttable[m], sizeof(uint16_t), factorial8, ttf);
+ fwrite(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf);
+ }
+ fclose(ttf);
+ return true;
+ } else return false;
+}
+
+void init_ttables(bool read, bool write) {
+ /* Generate all move cycles and flips; I do this regardless */
+ for (int i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ CubeArray arr = {0};
+ cube_to_arrays(blank_cube(), &arr, fAll);
+ for (int j = 0; equiv_moves[i][j]; j++)
+ move_cubearray(equiv_moves[i][j], &arr, fAll);
+
+ intarrcopy(arr.ep, edge_cycle[i], 12);
+ intarrcopy(arr.eofb, eofb_flipped[i], 12);
+ intarrcopy(arr.eorl, eorl_flipped[i], 12);
+ intarrcopy(arr.eoud, eoud_flipped[i], 12);
+ intarrcopy(arr.cp, corner_cycle[i], 8);
+ intarrcopy(arr.coud, coud_flipped[i], 8);
+ intarrcopy(arr.corl, corl_flipped[i], 8);
+ intarrcopy(arr.cofb, cofb_flipped[i], 8);
+ intarrcopy(arr.cpos, center_cycle[i], 6);
+ }
+
+ if (read)
+ if (read_ttables_file())
+ return;
+
+ /* Initialize transition tables */
+ Cube c = {0};
+ PieceFilter fe = {.epose=true}, fs = {.eposs=true}, fm = {.eposm=true};
+ PieceFilter feo = { .eofb = true, .eorl = true, .eoud = true };
+ PieceFilter fcp = { .cp = true };
+ PieceFilter fco = { .cofb = true, .corl = true, .coud = true };
+ PieceFilter fcc = { .cpos = true };
+ for (int m = 0; m < NMOVES; m++) {
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ c.epose = i; epose_ttable[m][i] = move_via_array(m, c, fe).epose;
+ c.eposs = i; eposs_ttable[m][i] = move_via_array(m, c, fs).eposs;
+ c.eposm = i; eposm_ttable[m][i] = move_via_array(m, c, fm).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ c.eofb = i; eofb_ttable[m][i] = move_via_array(m, c, feo).eofb;
+ c.eorl = i; eorl_ttable[m][i] = move_via_array(m, c, feo).eorl;
+ c.eoud = i; eoud_ttable[m][i] = move_via_array(m, c, feo).eoud;
+ }
+ for (uint16_t i = 0; i < factorial8; i++) {
+ c.cp = i; cp_ttable[m][i] = move_via_array(m, c, fcp).cp;
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ c.coud = i; coud_ttable[m][i] = move_via_array(m, c, fco).coud;
+ c.corl = i; corl_ttable[m][i] = move_via_array(m, c, fco).corl;
+ c.cofb = i; cofb_ttable[m][i] = move_via_array(m, c, fco).cofb;
+ }
+ for (uint16_t i = 0; i < factorial6; i++) {
+ c.cpos = i; cpos_ttable[m][i] = move_via_array(m, c, fcc).cpos;
+ }
+ }
+
+ if (write) write_ttables_file();
+}
+
+Cube move_cube(Move m, Cube cube) {
+ Cube moved = cube;
+
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.cpos = cpos_ttable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube compose(Cube c2, Cube c1) {
+ /* This is basically the same as the move_cubearray function above */
+ CubeArray arr2 = {0}, arr1 = {0};
+ cube_to_arrays(c2, &arr2, fAll);
+ cube_to_arrays(c1, &arr1, fAll);
+
+ apply_permutation(arr2.ep, arr1.ep, 12);
+ apply_permutation(arr2.ep, arr1.eofb, 12);
+ apply_permutation(arr2.ep, arr1.eorl, 12);
+ apply_permutation(arr2.ep, arr1.eoud, 12);
+ sum_arrays_mod(arr2.eofb, arr1.eofb, 12, 2);
+ sum_arrays_mod(arr2.eorl, arr1.eorl, 12, 2);
+ sum_arrays_mod(arr2.eoud, arr1.eoud, 12, 2);
+ apply_permutation(arr2.cp, arr1.cp, 8);
+ apply_permutation(arr2.cp, arr1.coud, 8);
+ apply_permutation(arr2.cp, arr1.corl, 8);
+ apply_permutation(arr2.cp, arr1.cofb, 8);
+ sum_arrays_mod(arr2.coud, arr1.coud, 8, 3);
+ sum_arrays_mod(arr2.corl, arr1.corl, 8, 3);
+ sum_arrays_mod(arr2.cofb, arr1.cofb, 8, 3);
+ apply_permutation(arr2.cpos, arr1.cpos, 6);
+
+ return arrays_to_cube(arr1, fAll);
+}
+
+Cube apply_alg(NissMove *alg, Cube cube) {
+ Cube ret = {0};
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ ret = compose(cube, inverse_cube(ret));
+
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (!alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ return ret;
+}
+
+void init_aux_tables() {
+ /* Commute */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ commute[i][j] = equal(move_cube(i, move_cube(j, blank_cube())),
+ move_cube(j, move_cube(i, blank_cube())));
+
+ /* Possible next (if the sequence i j k is valid) */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ for (int k = 0; k < NMOVES; k++)
+ possible_next[i][j][k] =
+ (j == 0) ||
+ (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) &&
+ !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3)));
+
+ /* Inverse */
+ for (int i = 0; i < NMOVES; i++)
+ inverse[i] = i == 0 ? 0 : i + 2 - 2*((i-1)%3);
+}
+
diff --git a/old/2021-02-06/cube.h b/old/2021-02-06/cube.h
@@ -0,0 +1,60 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "utils.h"
+
+#define NMOVES (z3+1)
+
+/* Constants for moves and pieces */
+typedef enum {
+ NULLMOVE,
+ U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3,
+ Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3, S, S2, S3, E, E2, E3,
+ x, x2, x3, y, y2, y3, z, z2, z3,
+} Move;
+typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center;
+typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge;
+typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner;
+
+/* An alg is an array of "NissMoves", which can be on normal or on inverse. */
+typedef struct { bool inverse; Move m; } NissMove;
+
+/* Representation of the cube */
+typedef struct {
+ uint16_t eofb, eorl, eoud, coud, cofb, corl,
+ epose, eposs, eposm, cp, cpos;
+} Cube;
+
+extern bool commute[NMOVES][NMOVES];
+extern bool possible_next[NMOVES][NMOVES][NMOVES];
+extern Move inverse[NMOVES];
+/* Movesets */
+extern bool standard_moveset[NMOVES];
+
+int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */
+
+bool equal(Cube c1, Cube c2);
+bool is_solvable(Cube cube);
+/* reorient=true allows solved in wrong orientation */
+bool is_solved(Cube cube, bool reorient);
+void print_cube(Cube cube);
+void print_moves(NissMove *alg);
+int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */
+void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */
+Cube blank_cube();
+Cube inverse_cube(Cube cube);
+Cube move_cube(Move m, Cube cube);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg */
+Cube apply_alg(NissMove *alg, Cube cube);
+
+void init_ttables(bool read, bool write);
+void init_aux_tables();
+
+void init_dbg();
+
+#endif
diff --git a/old/2021-02-06/main.c b/old/2021-02-06/main.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include "cube.h"
+#include "solve.h"
+
+int main() {
+ init_ttables(true, true);
+ init_aux_tables();
+
+
+ char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2 y2";
+ NissMove alg[100];
+ read_moves(moves, alg, 100);
+ Cube cube = apply_alg(alg, blank_cube());
+
+ /*f_eofb(cube);*/
+
+
+/* NissMove sol[MAXS][MAXM];*/
+ SolveData d = { .optimal_only = true, .available = standard_moveset,
+ .max_moves = 10,
+ .cleanup = true,
+ .max_solutions = 10,
+ .f = f_eofb };
+ read_moves("y", d.pre_rotation, 2);
+ int n = solve(cube, &d);
+ printf("%d solutions found:\n", n);
+ for (int i = 0; i < n; i++)
+ print_moves(d.solutions[i]);
+
+ NissMove a[5], b[5];
+ read_moves("R", a, 5);
+ read_moves("U", b, 5);
+ Cube c1 = apply_alg(a,blank_cube()), c2 = apply_alg(b,blank_cube());
+ print_cube(compose(c2,c1));
+ print_cube(compose(c1,c2));
+ /*print_cube(compose(c2,blank_cube()));*/
+
+ NissMove nm[10];
+ read_moves("y(y)RU", nm, 10);
+
+ print_moves(nm);
+ cleanup(nm, 10);
+ print_moves(nm);
+
+ return 0;
+}
diff --git a/old/2021-02-06/solve.c b/old/2021-02-06/solve.c
@@ -0,0 +1,177 @@
+#include "solve.h"
+
+/* Data for creating a pruning table:
+ - compressed: if set to true, each entry occupies only 4 bits, but values
+ larger than 15 cannot be stored.
+ - available[] is the list of availabel moves, as above.
+ - *ptable is the actual table to fill.
+ - n is the number of states (size of ptable).
+ - index must "linearize" the cube, i.e. return its index in ptable.
+ - fname is the name of the file where to store the table */
+typedef struct {
+ bool compressed, *available;
+ int max_moves;
+ uint8_t *ptable;
+ uint64_t n;
+ uint64_t (*index)(Cube);
+ char *fname;
+} PruneData;
+
+/* TODO: comment this */
+typedef struct {
+ bool niss;
+ int m, d;
+ uint64_t *n;
+ Move last1, last2;
+} DfsData;
+
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd);
+void init_ptable(PruneData *pd, bool read, bool write);
+
+/* Search solutions of lenght exactly d */
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd) {
+ if (*dd.n >= sd->max_solutions ||
+ ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d))
+ return;
+
+ (sd->solutions[*dd.n][dd.m]).inverse = dd.niss;
+ (sd->solutions[*dd.n][dd.m]).m = NULLMOVE;
+
+ if (!sd->f(cube)) { /* Solved */
+ if (dd.m == dd.d) {
+ (*dd.n)++;
+ if (*dd.n < sd->max_solutions)
+ copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]);
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) {
+ Move move = sd->sorted_moves[i];
+ if (possible_next[dd.last2][dd.last1][move]) {
+ sd->solutions[*dd.n][dd.m].inverse = dd.niss;
+ sd->solutions[*dd.n][dd.m].m = move;
+ DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = move, .last2 = dd.last1 };
+ solve_dfs(move_cube(move, cube), sd, nn);
+ }
+ }
+
+ if (sd->can_niss && !dd.niss &&
+ (!dd.m || (dd.m && sd->f(move_cube(dd.last1, blank_cube()))))) {
+ DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n };
+ solve_dfs(inverse_cube(cube), sd, nn);
+ }
+}
+
+/* Iterative deepening depth-first search: for i running from the minimum
+ to the maximum number of moves allowed, looks for solutions of length i. */
+int solve(Cube cube, SolveData *sd) {
+ if (sd->precondition != NULL && !sd->precondition(cube))
+ return -1;
+
+ /* If not given, generate sorted list of moves */
+ if (sd->sorted_moves[0] == NULLMOVE) {
+ int a[NMOVES], b[NMOVES], ia = 0, ib = 0;
+ for (int i = 0; i < NMOVES; i++) {
+ if (sd->available[i]) {
+ if (sd->f(move_cube(i, blank_cube())))
+ a[ia++] = i;
+ else
+ b[ib++] = i;
+ }
+ }
+ intarrcopy(a, (int *)sd->sorted_moves, ia);
+ intarrcopy(b, (int *)sd->sorted_moves+ia, ib);
+ sd->sorted_moves[ia+ib] = NULLMOVE;
+ }
+
+ sd->max_solutions = min(sd->max_solutions, MAXS);
+ Cube rotated = apply_alg(sd->pre_rotation, blank_cube());
+ cube = apply_alg(inverse_cube(rotated), compose(cube, rotated));
+
+ uint64_t ret = 0;
+ for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) {
+ DfsData dd = { .d = i, .n = &ret };
+ solve_dfs(cube, sd, dd);
+ }
+
+ for (uint64_t i = 0; i < ret; i++) {
+ /* TODO: transform solutions with inverse of pre_rotation */
+ if (sd->cleanup)
+ cleanup(sd->solutions[i], sd->max_moves*3);
+ }
+
+ return ret;
+}
+
+void prune_dfs(Cube cube, PruneData *pd, DfsData dd) {
+ uint64_t ind = pd->index(cube);
+ if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m)
+ return;
+ if (dd.m == dd.d) {
+ if (ind && !pd->ptable[ind]) {
+ pd->ptable[ind] = dd.m;
+ (*dd.n)++;
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES; i++) {
+ if (dd.m<20)
+ if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) {
+ DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = i, .last2 = dd.last1 };
+ prune_dfs(move_cube(i, cube), pd, nn);
+ }
+ }
+}
+
+void init_ptable(PruneData *pd, bool read, bool write) {
+ if (read) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "rb")) != NULL) {
+ fread(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+
+ /* TODO: for now it behaves always as if copressed = false */
+ for (uint64_t i = 0; i < pd->n; i++)
+ pd->ptable[i] = 0;
+
+ uint64_t s = 1;
+ for (int i = 1; i < pd->max_moves && s < pd->n; i++) {
+ DfsData dd = { .d = i, .n = &s };
+ prune_dfs(blank_cube(), pd, dd);
+ }
+
+ if (write) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "wb")) != NULL) {
+ fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+}
+
+/* Solving steps (and indexing functions) */
+
+uint64_t index_eofb(Cube cube) { return cube.eofb; }
+uint16_t f_eofb(Cube cube) {
+ static bool initialized_ptable;
+ static uint8_t pt_eofb[pow2to11];
+ if (!initialized_ptable) {
+ PruneData pd = {
+ .compressed = false, .available = standard_moveset, .max_moves = 13,
+ .ptable = pt_eofb, .n = pow2to11, .index = index_eofb,
+ .fname = "ptable_eofb"
+ };
+ init_ptable(&pd, false, true);
+ initialized_ptable = true;
+ }
+ return cube.eofb ? pt_eofb[cube.eofb] : 0;
+}
+
diff --git a/old/2021-02-06/solve.h b/old/2021-02-06/solve.h
@@ -0,0 +1,55 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include <stdlib.h>
+#include "cube.h"
+
+/* Maximum number of moves per solution and of solutions */
+#define MAXM 30
+#define MAXS 999
+
+/* Data for solving a step:
+ - can_niss is true niss can be used, false otherwise.
+ - optimal_only if true, dynamically updates max_moves so non-optimal
+ solutions are discarded.
+ - cleanup determines whether the cleaunup() function should be used on
+ the found solutions before returning.
+ - available[m] is true if the move m can be used, false otherwise.
+ - min_moves and max_moves are the minimum and maximum number of moves that
+ can be used.
+ - max_solution is the maximum number of solutions that can be returned.
+ - precondition can be used to check wheter the step can actually be applied
+ to the cube. If it returns false, solve() stops immediately returning -1.
+ - f must return 0 if and only if the step is solve, otherwise it must return
+ a lower bound for the number of moves required (without niss).
+ - sorted_moves[] can be used to specify in which order moves are tried
+ by the solving algorithm (for example if one wants to always try F' before
+ F). If sorted_moves[0] == NULLMOVE, the list is generated automatically.
+ It is advised to list first all the moves that actually influence the
+ solved state of the step (this is the default choice). This is in order to
+ avoid cases like B2 F for EO and to NISS only when it makes sense.
+ - start_moves [Currently unused, REMOVE]
+ are the moves that will be used as first moves of all
+ solutions. For example giving R' U' F (F' U R) will generate FMC scrambles
+ and y (y) will solve the step on another axis.
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wrt a different orientation
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wth respect to a different orientation.
+ - solutions[][] is the array where to store the found solutions. */
+typedef struct {
+ bool can_niss, optimal_only, cleanup, *available;
+ int min_moves, max_moves;
+ uint64_t max_solutions;
+ bool (*precondition)(Cube);
+ uint16_t (*f)(Cube);
+ Move sorted_moves[NMOVES];
+ NissMove pre_rotation[3], solutions[MAXS][MAXM];
+} SolveData;
+
+int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */
+
+/* Steps */
+uint16_t f_eofb(Cube cube);
+
+#endif
diff --git a/old/2021-02-06/utils.c b/old/2021-02-06/utils.c
@@ -0,0 +1,197 @@
+#include "utils.h"
+
+void swap(int *a, int *b) {
+ int aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+void intarrcopy(int *src, int *dst, int n) {
+ for (int i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+int sum(int *a, int n) {
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ ret += a[i];
+ return ret;
+}
+
+bool is_perm(int *a, int n) {
+ int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0;
+ for (int i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+ for (int i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+ return true;
+}
+
+bool is_subset(int *a, int n, int k) {
+ int sum = 0;
+ for (int i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+ return sum == k;
+}
+
+int powint(int a, int b) {
+ return 0;
+ if (b == 0 || a == 1)
+ return 1;
+ if (a == 0)
+ return 0;
+ if (b < 0)
+ return 0; /* Immediate truncate (integer part is 0) */
+ if (b % 2) {
+ return a * powint(a, b-1);
+ } else {
+ int x = powint(a, b/2);
+ return x*x;
+ }
+}
+
+int factorial(int n) {
+ if (n < 0)
+ return 0;
+ int ret = 1;
+ for (int i = 1; i <= n; i++)
+ ret *= i;
+ return ret;
+}
+
+int binomial(int n, int k) {
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+void int_to_digit_array(int a, int b, int n, int *r) {
+ if (b <= 1)
+ for (int i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (int i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+int digit_array_to_int(int *a, int n, int b) {
+ int ret = 0, p = 1;
+ for (int i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+ return ret;
+}
+
+int perm_to_index(int *a, int n) {
+ if (!is_perm(a, n))
+ return factorial(n); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ for (int j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+ return ret;
+}
+
+void index_to_perm(int p, int n, int *r) {
+ if (p < 0 || p >= factorial(n)) /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */
+ for (int i = 0; i < n; i++) {
+ int c = 0, j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+}
+
+int perm_sign(int *a, int n) {
+ if (!is_perm(a,n))
+ return false;
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ for (int j = i+1; j < n; j++)
+ ret += (a[i]>a[j]) ? 1 : 0;
+ return ret % 2;
+}
+
+int subset_to_index(int *a, int n, int k) {
+ /* TODO: better checks */
+ if (!is_subset(a, n, k))
+ return binomial(n, k); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+ return ret;
+}
+
+void index_to_subset(int s, int n, int k, int *r) {
+ if (s < 0 || s >= binomial(n, k)) { /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (int j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+ if (k == 0) {
+ for (int j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+ /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ int v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void int_to_sum_zero_array(int x, int b, int n, int *a) {
+ if (b <= 1) {
+ for (int i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ int s = 0;
+ for (int i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+void apply_permutation(int *perm, int *set, int n) {
+ if (!is_perm(perm, n))
+ return;
+ int aux[n];
+ for (int i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+ intarrcopy(aux, set, n);
+}
+
+void sum_arrays_mod(int *a, int *b, int n, int m) {
+ for (int i = 0; i < n; i++)
+ b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m;
+}
diff --git a/old/2021-02-06/utils.h b/old/2021-02-06/utils.h
@@ -0,0 +1,70 @@
+/* General utility functions */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Some useful constants */
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+/* Generic utility functions */
+void swap(int *a, int *b);
+void intarrcopy(int *src, int *dst, int n);
+int sum(int *a, int n);
+bool is_perm(int *a, int n);
+bool is_perm(int *a, int n);
+
+
+/* Standard mathematical functions */
+int powint(int a, int b);
+int factorial(int n);
+int binomial(int n, int k);
+
+/* Converts the integer a to its representation in base b (first n digits
+ * only) and saves the result in r. */
+void int_to_digit_array(int a, int b, int n, int *r);
+int digit_array_to_int(int *a, int n, int b);
+
+/* Converts the first n-1 digits of a number to an array a of digits in base b;
+ * then adds one element to the array, so that the sum of the elements of a is
+ * zero modulo b.
+ * This is used for determing the edge orientation from an 11-bits integer or
+ * the corner orientation from a 7-trits integer. */
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+
+/* Converts a permutation on [0..(n-1)] into the integer i which is the index
+ * of the permutation in the sorted list of all n! such permutations. */
+int perm_to_index(int *a, int n);
+void index_to_perm(int p, int n, int *r);
+
+/* Determine the sign of a permutation */
+int perm_sign(int a[], int n);
+
+/* Converts a k-element subset of a set from an array of n elements, of which k
+ * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */
+int subset_to_index(int *a, int n, int k);
+void index_to_subset(int s, int n, int k, int *r);
+
+int ordered_subset_to_index(int *a, int n, int k);
+void index_to_ordered_subset(int s, int n, int k, int *r);
+
+void apply_permutation(int *perm, int *set, int n);
+
+/* b[i] = (a[i]+b[i])%m for i=1,...,n */
+void sum_arrays_mod(int *a, int *b, int n, int m);
+
+#endif
diff --git a/old/2021-02-18-piecefilter/compile.sh b/old/2021-02-18-piecefilter/compile.sh
@@ -0,0 +1 @@
+gcc -Wall -Wextra -O2 -std=c99 -o nissy ./src/*.c
diff --git a/old/2021-02-18-piecefilter/nissy b/old/2021-02-18-piecefilter/nissy
Binary files differ.
diff --git a/old/2021-02-18-piecefilter/src/cube.c b/old/2021-02-18-piecefilter/src/cube.c
@@ -0,0 +1,198 @@
+#include "cube.h"
+
+char edge_string[12][5] =
+ { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" };
+char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" };
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+
+int epe_solved[] = {FR, FL, BL, BR};
+int eps_solved[] = {UL, UR, DL, DR};
+int epm_solved[] = {UF, UB, DF, DB};
+
+PieceFilter fAll = {true,true,true,true,true,true,true,true,true,true,true};
+
+Cube blank_cube() {
+ Cube c = {0};
+ return c;
+}
+
+/* Return axis (ud=0, rl=1, fb=0) of center c */
+int center_axis(int c) {
+ if (c == U_center || c == D_center)
+ return 0;
+ if (c == R_center || c == L_center)
+ return 1;
+ return 2;
+}
+
+/* Return slice (e=0, s=1, m=2) to which e belongs */
+int edge_slice(int e) {
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+ return 2;
+}
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) {
+ /* ep is the hardest */
+ if (f.epose || f.eposs || f.eposm)
+ for (int i = 0; i < 12; i++) arr->ep[i] = -1;
+ if (f.epose) {
+ int epe[4], epose[12];
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ for (int i = 0, ie = 0; i < 12; i++)
+ if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]];
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12];
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ for (int i = 0, is = 0; i < 12; i++)
+ if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]];
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12];
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ for (int i = 0, im = 0; i < 12; i++)
+ if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]];
+ }
+
+ /* All the others */
+ if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp) index_to_perm( cube.cp, 8, arr->cp);
+ if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos);
+}
+
+Cube arrays_to_cube(CubeArray arr, PieceFilter f) {
+ Cube ret = {0};
+
+ /* Again, ep is the hardest part */
+ if (f.epose) {
+ int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, ie = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epe_solved[j])
+ { epe[ie++] = j; epose[i] = 1; }
+ ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4);
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, is = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == eps_solved[j])
+ { eps[is++] = j; eposs[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4);
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, im = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epm_solved[j])
+ { epm[im++] = j; eposm[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4);
+ }
+ if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2);
+ if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2);
+ if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2);
+ if (f.cp) ret.cp = perm_to_index( arr.cp, 8 );
+ if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3);
+ if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3);
+ if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3);
+ if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 );
+
+ return ret;
+}
+
+bool equal(Cube c1, Cube c2) {
+ return c1.eofb == c2.eofb && c1.epose == c2.epose &&
+ c1.eposs == c2.eposs && c1.eposm == c2.eposm &&
+ c1.coud == c2.coud && c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+bool solvable(Cube cube) {
+ /* Since we memorize orientation truncating the last digit, we only need to
+ * check that the permutations have the correct sign. */
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, fAll);
+ return (perm_sign(arr.ep,12) ^ perm_sign(arr.cpos,6)) == perm_sign(arr.cp,8);
+}
+
+bool is_solved(Cube cube) {
+ return !cube.eofb && !cube.coud && !cube.cp &&
+ !cube.epose && !cube.eposs && !cube.eposm && cube.cpos;
+}
+
+void print_cube(Cube cube) {
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, fAll);
+
+ for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arr.ep[i]]);
+ printf("\n");
+ for (int i = 0; i < 12; i++) printf(" %c ", arr.eofb[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf("%s ", corner_string[arr.cp[i]]);
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf(" %c ", arr.coud[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 6; i++) printf(" %s ", center_string[arr.cpos[i]]);
+ printf("\n");
+}
+
+Cube inverse_cube(Cube cube) {
+ CubeArray arr = {0}, inv = {0};
+ cube_to_arrays(cube, &arr, fAll);
+
+ for (int i = 0; i < 12; i++) {
+ inv.ep[arr.ep[i]] = i;
+ inv.eofb[arr.ep[i]] = arr.eofb[i];
+ inv.eorl[arr.ep[i]] = arr.eorl[i];
+ inv.eoud[arr.ep[i]] = arr.eoud[i];
+ }
+ for (int i = 0; i < 8; i++) {
+ inv.cp[arr.cp[i]] = i;
+ inv.coud[arr.cp[i]] = arr.coud[i];
+ inv.corl[arr.cp[i]] = arr.corl[i];
+ inv.cofb[arr.cp[i]] = arr.cofb[i];
+ }
+ for (int i = 0; i < 6; i++)
+ inv.cpos[arr.cpos[i]] = i;
+
+ return arrays_to_cube(inv, fAll);
+}
+
+Cube compose_via_arrays(CubeArray arr2, Cube c1, PieceFilter f) {
+ /* This is basically the same as the move_cubearray function above */
+ CubeArray arr1 = {0};
+ cube_to_arrays(c1, &arr1, fAll);
+
+ apply_permutation(arr2.ep, arr1.ep, 12);
+ apply_permutation(arr2.ep, arr1.eofb, 12);
+ apply_permutation(arr2.ep, arr1.eorl, 12);
+ apply_permutation(arr2.ep, arr1.eoud, 12);
+ sum_arrays_mod(arr2.eofb, arr1.eofb, 12, 2);
+ sum_arrays_mod(arr2.eorl, arr1.eorl, 12, 2);
+ sum_arrays_mod(arr2.eoud, arr1.eoud, 12, 2);
+ apply_permutation(arr2.cp, arr1.cp, 8);
+ apply_permutation(arr2.cp, arr1.coud, 8);
+ apply_permutation(arr2.cp, arr1.corl, 8);
+ apply_permutation(arr2.cp, arr1.cofb, 8);
+ sum_arrays_mod(arr2.coud, arr1.coud, 8, 3);
+ sum_arrays_mod(arr2.corl, arr1.corl, 8, 3);
+ sum_arrays_mod(arr2.cofb, arr1.cofb, 8, 3);
+ apply_permutation(arr2.cpos, arr1.cpos, 6);
+
+ return arrays_to_cube(arr1, fAll);
+}
diff --git a/old/2021-02-18-piecefilter/src/cube.h b/old/2021-02-18-piecefilter/src/cube.h
@@ -0,0 +1,45 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "utils.h"
+
+typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center;
+typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge;
+typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner;
+
+typedef struct {
+ bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos;
+} PieceFilter;
+
+typedef struct {
+ uint16_t eofb, eorl, eoud, coud, cofb, corl,
+ epose, eposs, eposm, cp, cpos;
+} Cube;
+
+typedef struct {
+ int ep[12], eofb[12], eorl[12], eoud[12],
+ cp[8], coud[8], corl[8], cofb[8], cpos[6];
+} CubeArray;
+
+
+extern PieceFilter fAll;
+
+Cube blank_cube();
+/* Return axis (ud=0, rl=1, fb=0) of center c */
+int center_axis(int c);
+/* Return slice (e=0, s=1, m=2) to which e belongs */
+int edge_slice(int e);
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+Cube arrays_to_cube(CubeArray arr, PieceFilter f);
+bool equal(Cube c1, Cube c2);
+bool is_solvable(Cube cube);
+bool is_solved(Cube cube);
+void print_cube(Cube cube);
+Cube inverse_cube(Cube cube);
+/* Use c2 as an alg on c1 */
+Cube compose_via_arrays(CubeArray c2, Cube c1, PieceFilter f);
+
+#endif
diff --git a/old/2021-02-18-piecefilter/src/main.c b/old/2021-02-18-piecefilter/src/main.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include "cube.h"
+#include "moves.h"
+#include "solve.h"
+
+int main() {
+ init_ttables(true, true);
+ init_aux_tables();
+
+
+ char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2 y2";
+ NissMove alg[100];
+ read_moves(moves, alg, 100);
+ Cube cube = apply_alg(alg, blank_cube());
+
+ /*f_eofb(cube);*/
+
+
+/* NissMove sol[MAXS][MAXM];*/
+ SolveData d = { .optimal_only = true, .available = standard_moveset,
+ .max_moves = 10,
+ .cleanup = true,
+ .max_solutions = 10,
+ .f = f_eofb };
+ read_moves("y", d.pre_rotation, 2);
+ int n = solve(cube, &d);
+ printf("%d solutions found:\n", n);
+ for (int i = 0; i < n; i++)
+ print_moves(d.solutions[i]);
+
+ NissMove a[5], b[5];
+ read_moves("R", a, 5);
+ read_moves("U", b, 5);
+ Cube c1 = apply_alg(a,blank_cube()), c2 = apply_alg(b,blank_cube());
+ print_cube(compose(c2,c1));
+ print_cube(compose(c1,c2));
+ /*print_cube(compose(c2,blank_cube()));*/
+
+ NissMove nm[10];
+ read_moves("y(y)RU", nm, 10);
+
+ print_moves(nm);
+ cleanup(nm, 10);
+ print_moves(nm);
+
+ return 0;
+}
diff --git a/old/2021-02-18-piecefilter/src/moves.c b/old/2021-02-18-piecefilter/src/moves.c
@@ -0,0 +1,482 @@
+#include "moves.h"
+
+void move_cubearray(Move m, CubeArray *arr, PieceFilter f);
+Cube move_via_array(Move m, Cube cube, PieceFilter f);
+void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front);
+bool read_ttables_file();
+bool write_ttables_file();
+
+/* Transition tables */
+uint16_t epose_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposs_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposm_ttable[NMOVES][factorial12/factorial8];
+uint16_t eofb_ttable[NMOVES][pow2to11];
+uint16_t eorl_ttable[NMOVES][pow2to11];
+uint16_t eoud_ttable[NMOVES][pow2to11];
+uint16_t cp_ttable[NMOVES][factorial8];
+uint16_t coud_ttable[NMOVES][pow3to7];
+uint16_t cofb_ttable[NMOVES][pow3to7];
+uint16_t corl_ttable[NMOVES][pow3to7];
+uint16_t cpos_ttable[NMOVES][factorial6];
+
+bool commute[NMOVES][NMOVES];
+bool possible_next[NMOVES][NMOVES][NMOVES];
+Move inverse[NMOVES];
+
+char move_string[NMOVES][5] =
+ { "-",
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" };
+
+/* For each type of pieces only the effects of U, x and y are described */
+int edge_cycle[NMOVES][12] =
+ { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} };
+int eofb_flipped[NMOVES][12] =
+ { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eorl_flipped[NMOVES][12] =
+ { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eoud_flipped[NMOVES][12] =
+ { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
+int corner_cycle[NMOVES][8] =
+ { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} };
+int coud_flipped[NMOVES][8] =
+ { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} };
+int corl_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} };
+int cofb_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2},
+ [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} };
+int center_cycle[NMOVES][6] =
+ { [x] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [y] = {U_center, D_center, B_center, F_center, R_center, L_center} };
+
+/* Each move is reduced to a combination of U, x and y using this table */
+Move equiv_moves[NMOVES][14] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 },
+ [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 },
+ [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 },
+ [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 },
+ [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 },
+ [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 },
+ [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 },
+ [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 },
+ [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 },
+ [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 },
+ [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 },
+ [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 },
+ [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 },
+ [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 },
+ [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 },
+ [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+
+ [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 },
+ [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 },
+ [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 },
+ [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 },
+ [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 },
+ [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 },
+ [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 },
+ [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 },
+ [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 },
+
+ [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* Movesets */
+bool standard_moveset[NMOVES] = {
+ [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true,
+ [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true,
+ [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true,
+};
+
+bool is_solved_up_to_reorient(Cube cube) {
+ if (is_solved(cube))
+ return true;
+
+ /*TODO: improve this crap */
+
+ bool ret = false;
+ for (int i = x; i <= z3; i++) {
+ ret = ret || is_solved(move_cube(i, cube));
+ for (int j = x; j <= z3; j++)
+ ret = ret || is_solved(move_cube(i, move_cube(j, cube)));
+ }
+ return ret;
+}
+
+void move_cubearray(Move m, CubeArray *arr, PieceFilter f) {
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(edge_cycle[m], arr->ep, 12);
+ if (f.eofb) {
+ apply_permutation(edge_cycle[m], arr->eofb, 12);
+ sum_arrays_mod(eofb_flipped[m], arr->eofb, 12, 2);
+ }
+ if (f.eorl) {
+ apply_permutation(edge_cycle[m], arr->eorl, 12);
+ sum_arrays_mod(eorl_flipped[m], arr->eorl, 12, 2);
+ }
+ if (f.eoud) {
+ apply_permutation(edge_cycle[m], arr->eoud, 12);
+ sum_arrays_mod(eoud_flipped[m], arr->eoud, 12, 2);
+ }
+ if (f.cp)
+ apply_permutation(corner_cycle[m], arr->cp, 8);
+ if (f.coud) {
+ apply_permutation(corner_cycle[m], arr->coud, 8);
+ sum_arrays_mod(coud_flipped[m], arr->coud, 8, 3);
+ }
+ if (f.corl) {
+ apply_permutation(corner_cycle[m], arr->corl, 8);
+ sum_arrays_mod(corl_flipped[m], arr->corl, 8, 3);
+ }
+ if (f.cofb) {
+ apply_permutation(corner_cycle[m], arr->cofb, 8);
+ sum_arrays_mod(cofb_flipped[m], arr->cofb, 8, 3);
+ }
+ if (f.cpos)
+ apply_permutation(center_cycle[m], arr->cpos, 6);
+}
+
+Cube move_via_array(Move m, Cube cube, PieceFilter f) {
+ CubeArray arr = {0};
+ cube_to_arrays(cube, &arr, f);
+ move_cubearray(m, &arr, f);
+ return arrays_to_cube(arr, f);
+}
+
+int copy_alg(NissMove *src, NissMove *dest) {
+ int i;
+ for (i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+ dest[i].m = NULLMOVE;
+ return i;
+}
+
+/* TODO: all strings start with space?? */
+void print_moves(NissMove *alg) {
+ bool niss = false;
+ for (int i = 0; alg[i].m != NULLMOVE; i++) {
+ char *fill = !niss && alg[i].inverse ? " (" :
+ (niss && !alg[i].inverse ? ") " : " ");
+ printf("%s%s", fill, move_string[alg[i].m]);
+ niss = alg[i].inverse;
+ }
+ printf("%s\n", niss ? ")" : "");
+}
+
+int read_moves(char *str, NissMove *alg, int n) {
+ bool niss = false;
+ int c = 0;
+
+ for (int i = 0; str[i] && c < n; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' || str[i] == ')') {
+ if ((niss && str[i] == '(') || (!niss && str[i] == ')'))
+ return -1;
+ niss = !niss;
+ continue;
+ }
+
+ alg[c].inverse = niss; alg[c].m = NULLMOVE;
+ for (Move j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ alg[c].m = j;
+ if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; }
+ if (str[i+1]=='2') { alg[c].m += 1; i++; }
+ else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; }
+ c++;
+ break;
+ }
+ }
+ }
+
+ alg[c].m = NULLMOVE;
+ return c;
+}
+
+/* Helper function for cleanup. alg must contain only basic moves, no 2 or '.
+ top and front describe an admissible orientation of the cube. */
+void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) {
+ int c = 0, i = 0;
+ PieceFilter cpos_only = { .cpos = true };
+ NissMove aux[n+3];
+ aux[0].m = NULLMOVE;
+
+ while (i < n && alg[i].m != NULLMOVE) {
+ int j = i;
+ while (j < n && commute[alg[i].m][alg[j].m]) j++;
+ Move base = 6*((alg[i].m-1)/6);
+ int t1 = 0, t2 = 0;
+ for (int k = i; k < j; k++)
+ if (alg[k].m == base+1) t1 = (t1+1)%4;
+ else t2 = (t2+1)%4;
+ if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; }
+ if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; }
+ i = j;
+ }
+ aux[c].m = NULLMOVE;
+
+ CubeArray q;
+ cube_to_arrays(blank_cube(), &q, cpos_only);
+ /* First we try to rotate in one move, then we try an x or y rotation
+ followed by a z rotation */
+ for (int r = x; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front && q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c].m = r;
+
+ aux[++c].m = NULLMOVE;
+ copy_alg(aux, alg);
+ return;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = x; r <= y3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = z; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+
+ aux[c].m = NULLMOVE;
+ copy_alg(aux, alg);
+}
+
+/* TODO: does not work with niss + rotations */
+void cleanup(NissMove *alg, int n) {
+ int count_n = 0, count_i = 0, *count;
+ PieceFilter cpos_only = { .cpos = true };
+ NissMove aux_n[n+1], aux_i[n+1], *aux;
+ CubeArray cube_n, cube_i, *cube;
+ cube_to_arrays(blank_cube(), &cube_n, cpos_only);
+ cube_to_arrays(blank_cube(), &cube_i, cpos_only);
+
+ for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) {
+ if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; }
+ else { count = &count_n; aux = aux_n; cube = &cube_n; }
+
+ for (int j = 0; equiv_moves[alg[i].m][j]; j++) {
+ Move m = equiv_moves[alg[i].m][j];
+ aux[*count].inverse = alg[i].inverse;
+ move_cubearray(m, cube, cpos_only);
+ if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1;
+ }
+ }
+
+ aux_n[count_n].m = NULLMOVE;
+ aux_i[count_i].m = NULLMOVE;
+ sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]);
+ sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]);
+ copy_alg(aux_n, alg);
+ copy_alg(aux_i, alg+count_n);
+}
+
+bool read_ttables_file() {
+ FILE *ttf;
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ for (int m = 0; m < NMOVES; m++) {
+ fread(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fread(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fread(cp_ttable[m], sizeof(uint16_t), factorial8, ttf);
+ fread(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fread(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf);
+ }
+ fclose(ttf);
+ return true;
+ } else return false;
+}
+
+bool write_ttables_file() {
+ FILE *ttf;
+ if ((ttf = fopen("ttables", "wb")) != NULL) {
+ for (int m = 0; m < NMOVES; m++) {
+ fwrite(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf);
+ fwrite(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf);
+ fwrite(cp_ttable[m], sizeof(uint16_t), factorial8, ttf);
+ fwrite(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf);
+ fwrite(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf);
+ }
+ fclose(ttf);
+ return true;
+ } else return false;
+}
+
+void init_ttables(bool read, bool write) {
+ /* Generate all move cycles and flips; I do this regardless */
+ for (int i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ CubeArray arr = {0};
+ cube_to_arrays(blank_cube(), &arr, fAll);
+ for (int j = 0; equiv_moves[i][j]; j++)
+ move_cubearray(equiv_moves[i][j], &arr, fAll);
+
+ intarrcopy(arr.ep, edge_cycle[i], 12);
+ intarrcopy(arr.eofb, eofb_flipped[i], 12);
+ intarrcopy(arr.eorl, eorl_flipped[i], 12);
+ intarrcopy(arr.eoud, eoud_flipped[i], 12);
+ intarrcopy(arr.cp, corner_cycle[i], 8);
+ intarrcopy(arr.coud, coud_flipped[i], 8);
+ intarrcopy(arr.corl, corl_flipped[i], 8);
+ intarrcopy(arr.cofb, cofb_flipped[i], 8);
+ intarrcopy(arr.cpos, center_cycle[i], 6);
+ }
+
+ if (read)
+ if (read_ttables_file())
+ return;
+
+ /* Initialize transition tables */
+ Cube c = {0};
+ PieceFilter fe = {.epose=true}, fs = {.eposs=true}, fm = {.eposm=true};
+ PieceFilter feo = { .eofb = true, .eorl = true, .eoud = true };
+ PieceFilter fcp = { .cp = true };
+ PieceFilter fco = { .cofb = true, .corl = true, .coud = true };
+ PieceFilter fcc = { .cpos = true };
+ for (int m = 0; m < NMOVES; m++) {
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ c.epose = i; epose_ttable[m][i] = move_via_array(m, c, fe).epose;
+ c.eposs = i; eposs_ttable[m][i] = move_via_array(m, c, fs).eposs;
+ c.eposm = i; eposm_ttable[m][i] = move_via_array(m, c, fm).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ c.eofb = i; eofb_ttable[m][i] = move_via_array(m, c, feo).eofb;
+ c.eorl = i; eorl_ttable[m][i] = move_via_array(m, c, feo).eorl;
+ c.eoud = i; eoud_ttable[m][i] = move_via_array(m, c, feo).eoud;
+ }
+ for (uint16_t i = 0; i < factorial8; i++) {
+ c.cp = i; cp_ttable[m][i] = move_via_array(m, c, fcp).cp;
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ c.coud = i; coud_ttable[m][i] = move_via_array(m, c, fco).coud;
+ c.corl = i; corl_ttable[m][i] = move_via_array(m, c, fco).corl;
+ c.cofb = i; cofb_ttable[m][i] = move_via_array(m, c, fco).cofb;
+ }
+ for (uint16_t i = 0; i < factorial6; i++) {
+ c.cpos = i; cpos_ttable[m][i] = move_via_array(m, c, fcc).cpos;
+ }
+ }
+
+ if (write) write_ttables_file();
+}
+
+Cube move_cube(Move m, Cube cube) {
+ Cube moved = cube;
+
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.cpos = cpos_ttable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube apply_alg(NissMove *alg, Cube cube) {
+ Cube ret = {0};
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ ret = compose(cube, inverse_cube(ret));
+
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (!alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ return ret;
+}
+
+void init_aux_tables() {
+ /* Commute */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ commute[i][j] = equal(move_cube(i, move_cube(j, blank_cube())),
+ move_cube(j, move_cube(i, blank_cube())));
+
+ /* Possible next (if the sequence i j k is valid) */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ for (int k = 0; k < NMOVES; k++)
+ possible_next[i][j][k] =
+ (j == 0) ||
+ (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) &&
+ !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3)));
+
+ /* Inverse */
+ for (int i = 0; i < NMOVES; i++)
+ inverse[i] = i == 0 ? 0 : i + 2 - 2*((i-1)%3);
+}
+
diff --git a/old/2021-02-18-piecefilter/src/moves.h b/old/2021-02-18-piecefilter/src/moves.h
@@ -0,0 +1,46 @@
+#ifndef MOVES_H
+#define MOVES_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "utils.h"
+
+#define NMOVES (z3+1)
+
+typedef enum {
+ NULLMOVE,
+ U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3,
+ Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3, S, S2, S3, E, E2, E3,
+ x, x2, x3, y, y2, y3, z, z2, z3,
+} Move;
+
+/* An alg is an array of "NissMoves", which can be on normal or on inverse. */
+typedef struct { bool inverse; Move m; } NissMove;
+
+/* Movesets */
+extern bool standard_moveset[NMOVES];
+
+extern bool commute[NMOVES][NMOVES];
+extern bool possible_next[NMOVES][NMOVES][NMOVES];
+extern Move inverse[NMOVES];
+
+bool is_solve_up_to_reorient(Cube cube);
+int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */
+void print_moves(NissMove *alg);
+int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */
+void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */
+Cube move_cube(Move m, Cube cube);
+/* I might want to replace this with two versions, one that uses PieceFilter */
+Cube apply_alg(NissMove *alg, Cube cube);
+
+/* Merge the following two?
+ always in this order */
+void init_ttables(bool read, bool write);
+void init_aux_tables();
+
+
+#endif
diff --git a/old/2021-02-18-piecefilter/src/solve.c b/old/2021-02-18-piecefilter/src/solve.c
@@ -0,0 +1,179 @@
+#include "solve.h"
+
+/* Data for creating a pruning table:
+ - compressed: if set to true, each entry occupies only 4 bits, but values
+ larger than 15 cannot be stored.
+ - available[] is the list of availabel moves, as above.
+ - *ptable is the actual table to fill.
+ - n is the number of states (size of ptable).
+ - index must "linearize" the cube, i.e. return its index in ptable.
+ - fname is the name of the file where to store the table */
+typedef struct {
+ bool compressed, *available;
+ int max_moves;
+ uint8_t *ptable;
+ uint64_t n;
+ uint64_t (*index)(Cube);
+ char *fname;
+} PruneData;
+
+/* TODO: comment this */
+typedef struct {
+ bool niss;
+ int m, d;
+ uint64_t *n;
+ Move last1, last2;
+} DfsData;
+
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd);
+void init_ptable(PruneData *pd, bool read, bool write);
+
+/* Search solutions of lenght exactly d */
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd) {
+ if (*dd.n >= sd->max_solutions ||
+ ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d))
+ return;
+
+ (sd->solutions[*dd.n][dd.m]).inverse = dd.niss;
+ (sd->solutions[*dd.n][dd.m]).m = NULLMOVE;
+
+ if (!sd->f(cube)) { /* Solved */
+ if (dd.m == dd.d) {
+ (*dd.n)++;
+ if (*dd.n < sd->max_solutions)
+ copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]);
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) {
+ Move move = sd->sorted_moves[i];
+ if (possible_next[dd.last2][dd.last1][move]) {
+ sd->solutions[*dd.n][dd.m].inverse = dd.niss;
+ sd->solutions[*dd.n][dd.m].m = move;
+ DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = move, .last2 = dd.last1 };
+ solve_dfs(move_cube(move, cube), sd, nn);
+ }
+ }
+
+ if (sd->can_niss && !dd.niss &&
+ (!dd.m || (dd.m && sd->f(move_cube(dd.last1, blank_cube()))))) {
+ DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n };
+ solve_dfs(inverse_cube(cube), sd, nn);
+ }
+}
+
+/* Iterative deepening depth-first search: for i running from the minimum
+ to the maximum number of moves allowed, looks for solutions of length i. */
+int solve(Cube cube, SolveData *sd) {
+ if (sd->precondition != NULL && !sd->precondition(cube))
+ return -1;
+
+ /* If not given, generate sorted list of moves */
+ if (sd->sorted_moves[0] == NULLMOVE) {
+ int a[NMOVES], b[NMOVES], ia = 0, ib = 0;
+ for (int i = 0; i < NMOVES; i++) {
+ if (sd->available[i]) {
+ if (sd->f(move_cube(i, blank_cube())))
+ a[ia++] = i;
+ else
+ b[ib++] = i;
+ }
+ }
+ intarrcopy(a, (int *)sd->sorted_moves, ia);
+ intarrcopy(b, (int *)sd->sorted_moves+ia, ib);
+ sd->sorted_moves[ia+ib] = NULLMOVE;
+ }
+
+ sd->max_solutions = min(sd->max_solutions, MAXS);
+ /*TODO
+ Cube rotated = apply_alg(sd->pre_rotation, blank_cube());
+ cube = apply_alg(inverse_cube(rotated), compose(cube, rotated));
+ */
+
+ uint64_t ret = 0;
+ for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) {
+ DfsData dd = { .d = i, .n = &ret };
+ solve_dfs(cube, sd, dd);
+ }
+
+ for (uint64_t i = 0; i < ret; i++) {
+ /* TODO: transform solutions with inverse of pre_rotation */
+ if (sd->cleanup)
+ cleanup(sd->solutions[i], sd->max_moves*3);
+ }
+
+ return ret;
+}
+
+void prune_dfs(Cube cube, PruneData *pd, DfsData dd) {
+ uint64_t ind = pd->index(cube);
+ if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m)
+ return;
+ if (dd.m == dd.d) {
+ if (ind && !pd->ptable[ind]) {
+ pd->ptable[ind] = dd.m;
+ (*dd.n)++;
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES; i++) {
+ if (dd.m<20)
+ if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) {
+ DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = i, .last2 = dd.last1 };
+ prune_dfs(move_cube(i, cube), pd, nn);
+ }
+ }
+}
+
+void init_ptable(PruneData *pd, bool read, bool write) {
+ if (read) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "rb")) != NULL) {
+ fread(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+
+ /* TODO: for now it behaves always as if copressed = false */
+ for (uint64_t i = 0; i < pd->n; i++)
+ pd->ptable[i] = 0;
+
+ uint64_t s = 1;
+ for (int i = 1; i < pd->max_moves && s < pd->n; i++) {
+ DfsData dd = { .d = i, .n = &s };
+ prune_dfs(blank_cube(), pd, dd);
+ }
+
+ if (write) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "wb")) != NULL) {
+ fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+}
+
+/* Solving steps (and indexing functions) */
+
+uint64_t index_eofb(Cube cube) { return cube.eofb; }
+uint16_t f_eofb(Cube cube) {
+ static bool initialized_ptable;
+ static uint8_t pt_eofb[pow2to11];
+ if (!initialized_ptable) {
+ PruneData pd = {
+ .compressed = false, .available = standard_moveset, .max_moves = 13,
+ .ptable = pt_eofb, .n = pow2to11, .index = index_eofb,
+ .fname = "ptable_eofb"
+ };
+ init_ptable(&pd, false, true);
+ initialized_ptable = true;
+ }
+ return cube.eofb ? pt_eofb[cube.eofb] : 0;
+}
+
diff --git a/old/2021-02-18-piecefilter/src/solve.h b/old/2021-02-18-piecefilter/src/solve.h
@@ -0,0 +1,56 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include <stdlib.h>
+#include "cube.h"
+#include "moves.h"
+
+/* Maximum number of moves per solution and of solutions */
+#define MAXM 30
+#define MAXS 999
+
+/* Data for solving a step:
+ - can_niss is true niss can be used, false otherwise.
+ - optimal_only if true, dynamically updates max_moves so non-optimal
+ solutions are discarded.
+ - cleanup determines whether the cleaunup() function should be used on
+ the found solutions before returning.
+ - available[m] is true if the move m can be used, false otherwise.
+ - min_moves and max_moves are the minimum and maximum number of moves that
+ can be used.
+ - max_solution is the maximum number of solutions that can be returned.
+ - precondition can be used to check wheter the step can actually be applied
+ to the cube. If it returns false, solve() stops immediately returning -1.
+ - f must return 0 if and only if the step is solve, otherwise it must return
+ a lower bound for the number of moves required (without niss).
+ - sorted_moves[] can be used to specify in which order moves are tried
+ by the solving algorithm (for example if one wants to always try F' before
+ F). If sorted_moves[0] == NULLMOVE, the list is generated automatically.
+ It is advised to list first all the moves that actually influence the
+ solved state of the step (this is the default choice). This is in order to
+ avoid cases like B2 F for EO and to NISS only when it makes sense.
+ - start_moves [Currently unused, REMOVE]
+ are the moves that will be used as first moves of all
+ solutions. For example giving R' U' F (F' U R) will generate FMC scrambles
+ and y (y) will solve the step on another axis.
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wrt a different orientation
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wth respect to a different orientation.
+ - solutions[][] is the array where to store the found solutions. */
+typedef struct {
+ bool can_niss, optimal_only, cleanup, *available;
+ int min_moves, max_moves;
+ uint64_t max_solutions;
+ bool (*precondition)(Cube);
+ uint16_t (*f)(Cube);
+ Move sorted_moves[NMOVES];
+ NissMove pre_rotation[3], solutions[MAXS][MAXM];
+} SolveData;
+
+int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */
+
+/* Steps */
+uint16_t f_eofb(Cube cube);
+
+#endif
diff --git a/old/2021-02-18-piecefilter/src/transformations.c b/old/2021-02-18-piecefilter/src/transformations.c
@@ -0,0 +1,103 @@
+#include "transformations.h"
+/*
+Cube apply_rotation_alg(Rotation r, Cube c);
+void apply_rotation_cube(NissMove *alg);
+Cube apply_mirror_cube(Cube c);
+void apply_mirror_alg(NissMove *alg);
+*/
+
+void compute_sources();
+Cube rotate_via_compose(Rotation r, Cube c);
+
+/* Values mod 3 to determine from which side to take the state to convert */
+int epose_source[NROTATIONS]; /* 0 = epose, 1 = eposs, 2 = eposm */
+int eposs_source[NROTATIONS];
+int eposm_source[NROTATIONS];
+int eofb_source[NROTATIONS]; /* 0 = eofb, 1 = eorl, 2 = eoud */
+int eorl_source[NROTATIONS];
+int eoud_source[NROTATIONS];
+int coud_source[NROTATIONS]; /* 0 = coud, 1 = cofb, 2 = corl */
+int cofb_source[NROTATIONS];
+int corl_source[NROTATIONS];
+
+/* Transition tables for rotations */
+uint16_t epose_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eposs_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eposm_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eofb_rtable[NROTATIONS][pow2to11];
+uint16_t eorl_rtable[NROTATIONS][pow2to11];
+uint16_t eoud_rtable[NROTATIONS][pow2to11];
+uint16_t cp_rtable[NROTATIONS][factorial8];
+uint16_t coud_rtable[NROTATIONS][pow3to7];
+uint16_t cofb_rtable[NROTATIONS][pow3to7];
+uint16_t corl_rtable[NROTATIONS][pow3to7];
+
+/* Transition tables for mirror */
+uint16_t epose_mtable[factorial12/factorial8];
+uint16_t eposs_mtable[factorial12/factorial8];
+uint16_t eposm_mtable[factorial12/factorial8];
+uint16_t eofb_mtable[pow2to11];
+uint16_t eorl_mtable[pow2to11];
+uint16_t eoud_mtable[pow2to11];
+uint16_t cp_mtable[factorial8];
+uint16_t coud_mtable[pow3to7];
+uint16_t cofb_mtable[pow3to7];
+uint16_t corl_mtable[pow3to7];
+
+/* Same for moves */
+uint16_t move_rtable[NROTATIONS][NMOVES];
+uint16_t move_mtable[NMOVES];
+
+/* Applying a rotation to the cube is equivalent to applying m (m) */
+Move equiv_moves[NROTATIONS][3] = {
+ [uf] = {0,0,0}, [ur] = {y,0,0}, [ub] = {y2,0,0}, [ul] = {y3,0,0},
+ [df] = {z2,0,0}, [dr] = {y,z2,0}, [db] = {y2,z2,0}, [dl] = {y3,z2,0},
+ [rf] = {z3,0,0}, [rd] = {z3,y,0}, [rb] = {z3,y2,0}, [ru] = {z3,y3,0},
+ [lf] = {z,0,0}, [ld] = {z,y3,0}, [lb] = {z,y2,0}, [lu] = {z,y,0},
+ [fu] = {x,y2,0}, [fr] = {x,y,0}, [fd] = {x,0,0}, [fl] = {x,y3,0},
+ [bu] = {x3,0,0}, [br] = {x3,y,0}, [bd] = {x3,y2,0}, [bl] = {x3,y3,0},
+};
+
+int mirror_ep[12] = {UF, UR, UB, UL, DF, DR, DB, DL, FL, FR, BR, BL};
+int mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL};
+int mirror_cpos[6] = {U_center,D_center,L_center,R_center,F_center,B_center};
+
+void compute_sources() {
+ /* epos{e,s,m} */
+ CubeArray arr;
+ for (int i = 0; i < NROTATIONS; i++) {
+ cube_to_arrays(move_cube(equiv_moves[i][1],
+ move_cube(equiv_moves[i][0], blank_cube())), &arr, fAll);
+ epose_source[i] = edge_slice(arr.ep[FR]);
+ eposs_source[i] = edge_slice(arr.ep[UR]);
+ eposm_source[i] = edge_slice(arr.ep[UF]);
+ eofb_source[i] = 2 - center_axis(arr.cpos[F_center]);
+ eorl_source[i] = 2 - center_axis(arr.cpos[R_center]);
+ eoud_source[i] = 2 - center_axis(arr.cpos[U_center]);
+ coud_source[i] = (2 * center_axis(arr.cpos[U_center])) % 3;
+ cofb_source[i] = (2 * center_axis(arr.cpos[F_center])) % 3;
+ corl_source[i] = (2 * center_axis(arr.cpos[R_center])) % 3;
+ }
+}
+
+/* Use Piecefilter? */
+Cube rotate_via_compose(Rotation r, Cube c) {
+/* TODO: if this is slow, change compose() in cube.{c,h} to use PieceFilter */
+ int j = 0;
+ NissMove nm[10] = {0};
+ for (int i = 1; i >= 0; i--) {
+ if (equiv_moves[r][i] != NULLMOVE) {
+ nm[j].inverse = true;
+ nm[j++].m = inverse[equiv_moves[r][i]];
+ }
+ }
+ for (int i = 0; equiv_moves[r][i] != NULLMOVE; i++) {
+ nm[j].inverse = false;
+ nm[j++].m = equiv_moves[r][i];
+ }
+
+ return apply_alg(nm, c);
+}
+
+/* Use PieceFilter? Not really necessary, but could be nice */
+Cube mirror_via_cubearray(Cube c) {
diff --git a/old/2021-02-18-piecefilter/src/transformations.h b/old/2021-02-18-piecefilter/src/transformations.h
@@ -0,0 +1,37 @@
+#ifndef TRANSFORMATIONS_H
+#define TRANSFORMATIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "moves.h"
+#include "utils.h"
+
+#define NROTATIONS (bl+1)
+
+/* Letters indicate top and front centers
+ * Lowercase letter to distinguish from pieces */
+typedef enum {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+} Rotation;
+
+/* Mirror is always on fb axis and applied after rotation */
+typedef struct {
+ Rotation rotation;
+ bool mirror;
+} Transformation;
+
+void print_transformation(Transformation t);
+
+Cube transform_cube(Transformation t, Cube cube);
+void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */
+
+void init_transformations(bool read, bool write);
+
+#endif
diff --git a/old/2021-02-18-piecefilter/src/utils.c b/old/2021-02-18-piecefilter/src/utils.c
@@ -0,0 +1,197 @@
+#include "utils.h"
+
+void swap(int *a, int *b) {
+ int aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+void intarrcopy(int *src, int *dst, int n) {
+ for (int i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+int sum(int *a, int n) {
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ ret += a[i];
+ return ret;
+}
+
+bool is_perm(int *a, int n) {
+ int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0;
+ for (int i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+ for (int i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+ return true;
+}
+
+bool is_subset(int *a, int n, int k) {
+ int sum = 0;
+ for (int i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+ return sum == k;
+}
+
+int powint(int a, int b) {
+ return 0;
+ if (b == 0 || a == 1)
+ return 1;
+ if (a == 0)
+ return 0;
+ if (b < 0)
+ return 0; /* Immediate truncate (integer part is 0) */
+ if (b % 2) {
+ return a * powint(a, b-1);
+ } else {
+ int x = powint(a, b/2);
+ return x*x;
+ }
+}
+
+int factorial(int n) {
+ if (n < 0)
+ return 0;
+ int ret = 1;
+ for (int i = 1; i <= n; i++)
+ ret *= i;
+ return ret;
+}
+
+int binomial(int n, int k) {
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+void int_to_digit_array(int a, int b, int n, int *r) {
+ if (b <= 1)
+ for (int i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (int i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+int digit_array_to_int(int *a, int n, int b) {
+ int ret = 0, p = 1;
+ for (int i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+ return ret;
+}
+
+int perm_to_index(int *a, int n) {
+ if (!is_perm(a, n))
+ return factorial(n); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ for (int j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+ return ret;
+}
+
+void index_to_perm(int p, int n, int *r) {
+ if (p < 0 || p >= factorial(n)) /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */
+ for (int i = 0; i < n; i++) {
+ int c = 0, j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+}
+
+int perm_sign(int *a, int n) {
+ if (!is_perm(a,n))
+ return false;
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ for (int j = i+1; j < n; j++)
+ ret += (a[i]>a[j]) ? 1 : 0;
+ return ret % 2;
+}
+
+int subset_to_index(int *a, int n, int k) {
+ /* TODO: better checks */
+ if (!is_subset(a, n, k))
+ return binomial(n, k); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+ return ret;
+}
+
+void index_to_subset(int s, int n, int k, int *r) {
+ if (s < 0 || s >= binomial(n, k)) { /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (int j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+ if (k == 0) {
+ for (int j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+ /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ int v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void int_to_sum_zero_array(int x, int b, int n, int *a) {
+ if (b <= 1) {
+ for (int i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ int s = 0;
+ for (int i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+void apply_permutation(int *perm, int *set, int n) {
+ if (!is_perm(perm, n))
+ return;
+ int aux[n];
+ for (int i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+ intarrcopy(aux, set, n);
+}
+
+void sum_arrays_mod(int *a, int *b, int n, int m) {
+ for (int i = 0; i < n; i++)
+ b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m;
+}
diff --git a/old/2021-02-18-piecefilter/src/utils.h b/old/2021-02-18-piecefilter/src/utils.h
@@ -0,0 +1,70 @@
+/* General utility functions */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Some useful constants */
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+/* Generic utility functions */
+void swap(int *a, int *b);
+void intarrcopy(int *src, int *dst, int n);
+int sum(int *a, int n);
+bool is_perm(int *a, int n);
+bool is_perm(int *a, int n);
+
+
+/* Standard mathematical functions */
+int powint(int a, int b);
+int factorial(int n);
+int binomial(int n, int k);
+
+/* Converts the integer a to its representation in base b (first n digits
+ * only) and saves the result in r. */
+void int_to_digit_array(int a, int b, int n, int *r);
+int digit_array_to_int(int *a, int n, int b);
+
+/* Converts the first n-1 digits of a number to an array a of digits in base b;
+ * then adds one element to the array, so that the sum of the elements of a is
+ * zero modulo b.
+ * This is used for determing the edge orientation from an 11-bits integer or
+ * the corner orientation from a 7-trits integer. */
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+
+/* Converts a permutation on [0..(n-1)] into the integer i which is the index
+ * of the permutation in the sorted list of all n! such permutations. */
+int perm_to_index(int *a, int n);
+void index_to_perm(int p, int n, int *r);
+
+/* Determine the sign of a permutation */
+int perm_sign(int a[], int n);
+
+/* Converts a k-element subset of a set from an array of n elements, of which k
+ * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */
+int subset_to_index(int *a, int n, int k);
+void index_to_subset(int s, int n, int k, int *r);
+
+int ordered_subset_to_index(int *a, int n, int k);
+void index_to_ordered_subset(int s, int n, int k, int *r);
+
+void apply_permutation(int *perm, int *set, int n);
+
+/* b[i] = (a[i]+b[i])%m for i=1,...,n */
+void sum_arrays_mod(int *a, int *b, int n, int m);
+
+#endif
diff --git a/old/2021-02-28-transformcube-works/src/cube.c b/old/2021-02-28-transformcube-works/src/cube.c
@@ -0,0 +1,271 @@
+#include "cube.h"
+
+typedef struct {
+ int ep[12],eofb[12],eorl[12],eoud[12],cp[8],coud[8],corl[8],cofb[8],cpos[6];
+} CubeArrayAllocated;
+
+void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all);
+
+char edge_string[12][5] =
+ { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" };
+char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" };
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+
+int epe_solved[4] = {FR, FL, BL, BR};
+int eps_solved[4] = {UL, UR, DL, DR};
+int epm_solved[4] = {UF, UB, DF, DB};
+
+PieceFilter pf_all = {true,true,true,true,true,true,true,true,true,true,true},
+ pf_cpos = { .cpos = true }, pf_cp = { .cp = true },
+ pf_ep = { .epose = true, .eposs = true, .eposm = true },
+ pf_e = {.epose=true}, pf_s={.eposs=true}, pf_m={.eposm=true},
+ pf_eo = { .eofb = true, .eorl = true, .eoud = true },
+ pf_co = { .coud = true, .cofb = true, .corl = true };
+
+void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all) {
+ arr->ep = all->ep;
+ arr->eofb = all->eofb;
+ arr->eorl = all->eorl;
+ arr->eoud = all->eoud;
+ arr->cp = all->cp;
+ arr->coud = all->coud;
+ arr->corl = all->corl;
+ arr->cofb = all->cofb;
+ arr->cpos = all->cpos;
+}
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) {
+ /* ep is the hardest */
+ if (f.epose || f.eposs || f.eposm)
+ for (int i = 0; i < 12; i++) arr->ep[i] = -1;
+ if (f.epose) {
+ int epe[4], epose[12];
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ for (int i = 0, ie = 0; i < 12; i++)
+ if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]];
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12];
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ for (int i = 0, is = 0; i < 12; i++)
+ if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]];
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12];
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ for (int i = 0, im = 0; i < 12; i++)
+ if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]];
+ }
+
+ /* All the others */
+ if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp) index_to_perm( cube.cp, 8, arr->cp);
+ if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos);
+}
+
+Cube arrays_to_cube(CubeArray arr, PieceFilter f) {
+ Cube ret = {0};
+
+ /* Again, ep is the hardest part */
+ if (f.epose) {
+ int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, ie = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epe_solved[j])
+ { epe[ie++] = j; epose[i] = 1; }
+ ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4);
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, is = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == eps_solved[j])
+ { eps[is++] = j; eposs[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4);
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, im = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epm_solved[j])
+ { epm[im++] = j; eposm[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4);
+ }
+ if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2);
+ if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2);
+ if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2);
+ if (f.cp) ret.cp = perm_to_index( arr.cp, 8 );
+ if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3);
+ if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3);
+ if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3);
+ if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 );
+
+ return ret;
+}
+
+Center center_at(Cube cube, Center c) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_cpos);
+ return arr.cpos[c];
+}
+
+Edge edge_at(Cube cube, Edge e) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_ep);
+ return arr.ep[e];
+}
+
+Corner corner_at(Cube cube, Corner c) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_cp);
+ return arr.cp[c];
+}
+
+bool equal(Cube c1, Cube c2) {
+ return c1.eofb == c2.eofb && c1.epose == c2.epose &&
+ c1.eposs == c2.eposs && c1.eposm == c2.eposm &&
+ c1.coud == c2.coud && c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+bool is_solvable(Cube cube) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+ cube_to_arrays(cube, &arrx, pf_all);
+
+ /* Since we memorize orientation truncating the last digit, we only need to
+ * check that the permutations have the correct sign. */
+ /* TODO: I should also check that the different eos and cos are compatible */
+ return (perm_sign(arrx.ep,12)^perm_sign(arrx.cpos,6))==perm_sign(arrx.cp,8);
+}
+
+bool is_solved(Cube cube) {
+ /* TODO: might return true if cube is not solvable but looks solved form one
+ of the incompatible interpretations (e.g. eofb and ep solved, but
+ eorl not solve) */
+ return !cube.eofb && !cube.coud && !cube.cp &&
+ !cube.epose && !cube.eposs && !cube.eposm && cube.cpos;
+}
+
+void print_cube(Cube cube) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+
+ cube_to_arrays(cube, &arrx, pf_all);
+
+/*
+ for (int i = 0; i < 12; i++) printf("%d ", arrx.ep[i]);
+ printf("\n");*/
+
+ for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arrx.ep[i]]);
+ printf("\n");
+ for (int i = 0; i < 12; i++) printf(" %c ", arrx.eofb[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf("%s ", corner_string[arrx.cp[i]]);
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf(" %c ", arrx.coud[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 6; i++) printf(" %s ", center_string[arrx.cpos[i]]);
+ printf("\n");
+}
+
+Cube admissible_ep(Cube cube, PieceFilter f) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+ cube_to_arrays(cube, &arrx, f);
+
+ bool used[12] = {0};
+ for (int i = 0; i < 12; i++)
+ if (arrx.ep[i] != -1)
+ used[arrx.ep[i]] = true;
+ for (int i = 0, j = 0; i < 12; i++) {
+ while (j < 11 && used[j]) j++;
+ if (arrx.ep[i] == -1)
+ arrx.ep[i] = j++;
+ }
+
+ return arrays_to_cube(arrx, pf_ep);
+}
+
+Cube inverse_cube(Cube cube) {
+ static CubeArrayAllocated all = {0}, invall = {0};
+ CubeArray arrx = {0}, invx = {0};
+ allocate_cubearray(&arrx, &all);
+ allocate_cubearray(&invx, &invall);
+
+ cube_to_arrays(cube, &arrx, pf_all);
+
+ for (int i = 0; i < 12; i++) {
+ invx.ep[arrx.ep[i]] = i;
+ invx.eofb[arrx.ep[i]] = arrx.eofb[i];
+ invx.eorl[arrx.ep[i]] = arrx.eorl[i];
+ invx.eoud[arrx.ep[i]] = arrx.eoud[i];
+ }
+ for (int i = 0; i < 8; i++) {
+ invx.cp[arrx.cp[i]] = i;
+ invx.coud[arrx.cp[i]] = (3 - arrx.coud[i])%3;
+ invx.corl[arrx.cp[i]] = (3 - arrx.corl[i])%3;
+ invx.cofb[arrx.cp[i]] = (3 - arrx.cofb[i])%3;
+ }
+ for (int i = 0; i < 6; i++)
+ invx.cpos[arrx.cpos[i]] = i;
+
+ return arrays_to_cube(invx, pf_all);
+}
+
+Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter f) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+
+ cube_to_arrays(c, &arrx, f);
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation( arr.ep, arrx.ep, 12 );
+ if (f.eofb) { apply_permutation( arr.ep, arrx.eofb, 12 );
+ sum_arrays_mod( arr.eofb, arrx.eofb, 12, 2 ); }
+ if (f.eorl) { apply_permutation( arr.ep, arrx.eorl, 12 );
+ sum_arrays_mod( arr.eorl, arrx.eorl, 12, 2 ); }
+ if (f.eoud) { apply_permutation( arr.ep, arrx.eoud, 12 );
+ sum_arrays_mod( arr.eoud, arrx.eoud, 12, 2 ); }
+ if (f.cp) apply_permutation( arr.cp, arrx.cp, 8 );
+ if (f.coud) { apply_permutation( arr.cp, arrx.coud, 8 );
+ sum_arrays_mod( arr.coud, arrx.coud, 8, 3 ); }
+ if (f.corl) { apply_permutation( arr.cp, arrx.corl, 8 );
+ sum_arrays_mod( arr.corl, arrx.corl, 8, 3 ); }
+ if (f.cofb) { apply_permutation( arr.cp, arrx.cofb, 8 );
+ sum_arrays_mod( arr.cofb, arrx.cofb, 8, 3 ); }
+ if (f.cpos) apply_permutation( arr.cpos, arrx.cpos, 6 );
+
+ return arrays_to_cube(arrx, f);
+}
+
+Cube compose(Cube c2, Cube c1) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+
+ cube_to_arrays(c2, &arrx, pf_all);
+ return move_via_arrays(arrx, c1, pf_all);
+}
diff --git a/old/2021-02-28-transformcube-works/src/cube.h b/old/2021-02-28-transformcube-works/src/cube.h
@@ -0,0 +1,55 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "utils.h"
+
+typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center;
+typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge;
+typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner;
+
+typedef struct {
+ uint16_t eofb, eorl, eoud, coud, cofb, corl,
+ epose, eposs, eposm, cp, cpos;
+} Cube;
+
+typedef struct {
+ bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos;
+} PieceFilter;
+
+typedef struct {
+ int *ep, *eofb, *eorl, *eoud, *cp, *coud, *corl, *cofb, *cpos;
+} CubeArray;
+
+extern PieceFilter pf_all, pf_cpos, pf_ep, pf_cp,
+ pf_e, pf_s, pf_m, pf_eo, pf_co;
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+Cube arrays_to_cube(CubeArray arr, PieceFilter f);
+
+Center center_at(Cube cube, Center c);
+Edge edge_at(Cube cube, Edge e);
+Corner corner_at(Cube cube, Corner c);
+/* Aggiungi funzioni per "queries" sul cubo: se pezzo è orientato rispetto ad
+ un certo asse, se il pezzo è risolto... */
+/* Would be nice: a funciton block_solved(Cube c, Block b), where Block is
+ something like struct {bool centers[6], edges[12], corners[8]}
+ (The advantage over checking pieces one by one is that I can convert
+ to cubearray only once and for all) */
+/* Altro TODO, ma forse non ne vale la pena: pre-calcolare tutti i possibili
+ valori per questi, e salvare i risultati in array (facile per cp e cpos,
+ mentre per ep bisogna anche cercare quale tra epose, eposs e eposm contiene
+ il valore giusto) */
+
+bool equal(Cube c1, Cube c2);
+bool is_solvable(Cube cube);
+bool is_solved(Cube cube);
+void print_cube(Cube cube);
+Cube admissible_ep(Cube cube, PieceFilter f); /* Returns admissible ep */
+Cube inverse_cube(Cube cube);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf);
+
+#endif
diff --git a/old/2021-02-28-transformcube-works/src/main.c b/old/2021-02-28-transformcube-works/src/main.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include "cube.h"
+#include "moves.h"
+#include "solve.h"
+#include "transformations.h"
+
+int main() {
+ init_ttables(true, true);
+ init_aux_tables();
+ init_transformations(true, true);
+
+
+ /*char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2";*/
+
+ /*char moves[100] = "M'U2MU2";*/
+ char moves[100] = "R' D2 F2 U2 R F2 R D2 L' R2 D2 F D' L' U' B R' D' U R' B";
+ NissMove alg[100];
+ read_moves(moves, alg, 100);
+ Cube cube = apply_alg(alg, (Cube){0});
+ print_cube(cube);
+ cube = transform_cube(rd, cube);
+ print_cube(cube);
+
+ /*f_eofb(cube);*/
+
+
+ /*
+ SolveData d = { .optimal_only = true, .available = standard_moveset,
+ .max_moves = 10,
+ .cleanup = true,
+ .max_solutions = 10,
+ .f = f_eofb };
+ read_moves("y", d.pre_rotation, 2);
+ int n = solve(cube, &d);
+ printf("%d solutions found:\n", n);
+ for (int i = 0; i < n; i++)
+ print_moves(d.solutions[i]);
+ */
+
+/*
+ NissMove a[5], b[5];
+ read_moves("R", a, 5);
+ read_moves("U", b, 5);
+ Cube c1 = apply_alg(a,(Cube){0}), c2 = apply_alg(b,(Cube){0});
+ print_cube(compose(c2,c1));
+ print_cube(compose(c1,c2));
+
+ NissMove nm[10];
+ read_moves("y(y)RU", nm, 10);
+
+ print_moves(nm);
+ cleanup(nm, 10);
+ print_moves(nm);
+ */
+
+ return 0;
+}
diff --git a/old/2021-02-28-transformcube-works/src/moves.c b/old/2021-02-28-transformcube-works/src/moves.c
@@ -0,0 +1,489 @@
+#include "moves.h"
+
+Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+/* void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); */
+bool read_ttables_file();
+bool write_ttables_file();
+
+/* Transition tables */
+uint16_t epose_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposs_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposm_ttable[NMOVES][factorial12/factorial8];
+uint16_t eofb_ttable[NMOVES][pow2to11];
+uint16_t eorl_ttable[NMOVES][pow2to11];
+uint16_t eoud_ttable[NMOVES][pow2to11];
+uint16_t cp_ttable[NMOVES][factorial8];
+uint16_t coud_ttable[NMOVES][pow3to7];
+uint16_t cofb_ttable[NMOVES][pow3to7];
+uint16_t corl_ttable[NMOVES][pow3to7];
+uint16_t cpos_ttable[NMOVES][factorial6];
+
+bool commute[NMOVES][NMOVES];
+bool possible_next[NMOVES][NMOVES][NMOVES];
+Move inverse[NMOVES];
+NissMove rotation_algs[24][3] = {
+ { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } },
+};
+
+char move_string[NMOVES][5] =
+ { "-",
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" };
+
+/* For each type of pieces only the effects of U, x and y are described */
+int edge_cycle[NMOVES][12] =
+ { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} };
+int eofb_flipped[NMOVES][12] =
+ { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eorl_flipped[NMOVES][12] =
+ { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eoud_flipped[NMOVES][12] =
+ { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
+int corner_cycle[NMOVES][8] =
+ { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} };
+int coud_flipped[NMOVES][8] =
+ { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} };
+int corl_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} };
+int cofb_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=1,[UFL]=2,[DBL]=2,[DFL]=1},
+ [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} };
+int center_cycle[NMOVES][6] =
+ { [x] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [y] = {U_center, D_center, B_center, F_center, R_center, L_center} };
+
+/* Each move is reduced to a combination of U, x and y using this table */
+Move equiv_moves[NMOVES][14] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 },
+ [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 },
+ [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 },
+ [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 },
+ [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 },
+ [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 },
+ [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 },
+ [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 },
+ [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 },
+ [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 },
+ [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 },
+ [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 },
+ [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 },
+ [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 },
+ [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 },
+ [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+
+ [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 },
+ [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 },
+ [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 },
+ [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 },
+ [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 },
+ [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 },
+ [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 },
+ [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 },
+ [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 },
+
+ [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* Movesets */
+bool standard_moveset[NMOVES] = {
+ [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true,
+ [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true,
+ [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true,
+};
+
+bool is_solved_up_to_reorient(Cube cube) {
+ for (int i = 0; i < 25; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube)))
+ return true;
+ return false;
+}
+
+Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f) {
+ return move_via_arrays((CubeArray)
+ { edge_cycle[m], eofb_flipped[m], eorl_flipped[m], eoud_flipped[m],
+ corner_cycle[m], coud_flipped[m], corl_flipped[m], cofb_flipped[m],
+ center_cycle[m] }, cube, f);
+}
+
+int len(NissMove *alg) {
+ int i;
+ for (i = 0; alg[i].m != NULLMOVE; i++);
+ return i;
+}
+
+int copy_alg(NissMove *src, NissMove *dest) {
+ int i;
+ for (i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+ dest[i].m = NULLMOVE;
+ return i;
+}
+
+int invert_alg(NissMove *src, NissMove *dest) {
+ int n = len(src);
+ for (int i = 0; i < n; i++)
+ dest[n-i-1] = (NissMove){.m=inverse[src[i].m], .inverse=src[i].inverse};
+ dest[n].m = NULLMOVE;
+ return n;
+}
+
+int concat(NissMove *src1, NissMove *src2, NissMove *dest) {
+ int n1 = len(src1), n2 = len(src2);
+ copy_alg(src1, dest);
+ copy_alg(src2, dest+n1);
+ return n1+n2;
+}
+
+/* TODO: all strings start with space?? */
+void print_moves(NissMove *alg) {
+ bool niss = false;
+ for (int i = 0; alg[i].m != NULLMOVE; i++) {
+ char *fill = !niss && alg[i].inverse ? " (" :
+ (niss && !alg[i].inverse ? ") " : " ");
+ printf("%s%s", fill, move_string[alg[i].m]);
+ niss = alg[i].inverse;
+ }
+ printf("%s\n", niss ? ")" : "");
+}
+
+int read_moves(char *str, NissMove *alg, int n) {
+ bool niss = false;
+ int c = 0;
+
+ for (int i = 0; str[i] && c < n; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' || str[i] == ')') {
+ if ((niss && str[i] == '(') || (!niss && str[i] == ')'))
+ return -1;
+ niss = !niss;
+ continue;
+ }
+
+ alg[c].inverse = niss; alg[c].m = NULLMOVE;
+ for (Move j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ alg[c].m = j;
+ if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; }
+ if (str[i+1]=='2') { alg[c].m += 1; i++; }
+ else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; }
+ c++;
+ break;
+ }
+ }
+ }
+
+ alg[c].m = NULLMOVE;
+ return c;
+}
+
+/* Helper function for cleanup. alg must contain only basic moves, no 2 or '.
+ top and front describe an admissible orientation of the cube. *
+void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) {
+ int c = 0, i = 0;
+ PieceFilter cpos_only = { .cpos = true };
+ NissMove aux[n+3];
+ aux[0].m = NULLMOVE;
+
+ while (i < n && alg[i].m != NULLMOVE) {
+ int j = i;
+ while (j < n && commute[alg[i].m][alg[j].m]) j++;
+ Move base = 6*((alg[i].m-1)/6);
+ int t1 = 0, t2 = 0;
+ for (int k = i; k < j; k++)
+ if (alg[k].m == base+1) t1 = (t1+1)%4;
+ else t2 = (t2+1)%4;
+ if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; }
+ if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; }
+ i = j;
+ }
+ aux[c].m = NULLMOVE;
+
+ CubeArray q;
+ cube_to_arrays((Cube){0}, &q, cpos_only);
+ * First we try to rotate in one move, then we try an x or y rotation
+ followed by a z rotation
+ TODO: change once I implement the "is_rotaton(Move) function" *
+ for (int r = x; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front && q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c].m = r;
+
+ aux[++c].m = NULLMOVE;
+ copy_alg(aux, alg);
+ return;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = x; r <= y3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[F_center] == front) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+ for (int r = z; r <= z3; r++) {
+ move_cubearray(r, &q, cpos_only);
+ if (q.cpos[U_center] == top) {
+ aux[c].inverse = inv; aux[c++].m = r;
+ break;
+ }
+ move_cubearray(inverse[r], &q, cpos_only);
+ }
+
+ aux[c].m = NULLMOVE;
+ copy_alg(aux, alg);
+}
+
+* TODO: does not work with niss + rotations *
+void cleanup(NissMove *alg, int n) {
+ int count_n = 0, count_i = 0, *count;
+ PieceFilter cpos_only = { .cpos = true };
+ NissMove aux_n[n+1], aux_i[n+1], *aux;
+ CubeArray cube_n, cube_i, *cube;
+ cube_to_arrays((Cube){0}, &cube_n, cpos_only);
+ cube_to_arrays((Cube){0}, &cube_i, cpos_only);
+
+ for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) {
+ if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; }
+ else { count = &count_n; aux = aux_n; cube = &cube_n; }
+
+ for (int j = 0; equiv_moves[alg[i].m][j]; j++) {
+ Move m = equiv_moves[alg[i].m][j];
+ aux[*count].inverse = alg[i].inverse;
+ move_cubearray(m, cube, cpos_only);
+ if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1;
+ }
+ }
+
+ aux_n[count_n].m = NULLMOVE;
+ aux_i[count_i].m = NULLMOVE;
+ sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]);
+ sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]);
+ copy_alg(aux_n, alg);
+ copy_alg(aux_i, alg+count_n);
+}
+*/
+
+bool read_ttables_file() {
+ FILE *ttf;
+ long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6 };
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fread(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fread(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fread(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fread(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fread(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5];
+ r = r && fread(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fread(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fread(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fread(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9];
+ r = r && fread(cpos_ttable[m], sizeof(uint16_t), me[10], ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+bool write_ttables_file() {
+ FILE *ttf;
+ long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6 };
+ if ((ttf = fopen("ttables", "wb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fwrite(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fwrite(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fwrite(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fwrite(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fwrite(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5];
+ r = r && fwrite(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fwrite(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fwrite(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fwrite(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9];
+ r = r && fwrite(cpos_ttable[m], sizeof(uint16_t), me[10],ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+void init_ttables(bool read, bool write) {
+ /* Generate all move cycles and flips; I do this regardless */
+ for (int i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ Cube c = {0};
+ for (int j = 0; equiv_moves[i][j]; j++)
+ c = apply_move_cubearray(equiv_moves[i][j], c, pf_all);
+
+ CubeArray arrs = {
+ edge_cycle[i], eofb_flipped[i], eorl_flipped[i], eoud_flipped[i],
+ corner_cycle[i], coud_flipped[i], corl_flipped[i], cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read)
+ if (read_ttables_file())
+ return;
+
+ /* Initialize transition tables */
+ for (int m = 0; m < NMOVES; m++) {
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ epose_ttable[m][i] = apply_move_cubearray(m,(Cube){.epose=i},pf_e).epose;
+ eposs_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposs=i},pf_s).eposs;
+ eposm_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposm=i},pf_m).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ eofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.eofb=i},pf_eo).eofb;
+ eorl_ttable[m][i] = apply_move_cubearray(m,(Cube){.eorl=i},pf_eo).eorl;
+ eoud_ttable[m][i] = apply_move_cubearray(m,(Cube){.eoud=i},pf_eo).eoud;
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ coud_ttable[m][i] = apply_move_cubearray(m,(Cube){.coud=i},pf_co).coud;
+ corl_ttable[m][i] = apply_move_cubearray(m,(Cube){.corl=i},pf_co).corl;
+ cofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.cofb=i},pf_co).cofb;
+ }
+ for (uint16_t i = 0; i < factorial8; i++)
+ cp_ttable[m][i] = apply_move_cubearray(m,(Cube){.cp=i},pf_cp).cp;
+ for (uint16_t i = 0; i < factorial6; i++)
+ cpos_ttable[m][i] = apply_move_cubearray(m,(Cube){.cpos=i},pf_cpos).cpos;
+ }
+
+ if (write)
+ if (!write_ttables_file())
+ printf("Error in writing ttables: file not writable\n");
+}
+
+Cube move_cube(Move m, Cube cube) {
+ Cube moved = {0};
+
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.cpos = cpos_ttable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube apply_alg(NissMove *alg, Cube cube) {
+ Cube ret = {0};
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+
+ ret = compose(cube, inverse_cube(ret));
+
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (!alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ return ret;
+}
+
+void init_aux_tables() {
+ /* Commute */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ commute[i][j] = equal(move_cube(i, move_cube(j, (Cube){0})),
+ move_cube(j, move_cube(i, (Cube){0})));
+
+ /* Possible next (if the sequence i j k is valid) */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ for (int k = 0; k < NMOVES; k++)
+ possible_next[i][j][k] =
+ (j == 0) ||
+ (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) &&
+ !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3)));
+
+ /* Inverse */
+ for (int i = 0; i < NMOVES; i++)
+ inverse[i] = i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3);
+
+}
+
diff --git a/old/2021-02-28-transformcube-works/src/moves.h b/old/2021-02-28-transformcube-works/src/moves.h
@@ -0,0 +1,50 @@
+#ifndef MOVES_H
+#define MOVES_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "utils.h"
+
+#define NMOVES (z3+1)
+
+typedef enum {
+ NULLMOVE,
+ U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3,
+ Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3, S, S2, S3, E, E2, E3,
+ x, x2, x3, y, y2, y3, z, z2, z3,
+} Move;
+
+/* An alg is an array of "NissMoves", which can be on normal or on inverse. */
+typedef struct { bool inverse; Move m; } NissMove;
+
+/* Movesets */
+extern bool standard_moveset[NMOVES];
+
+extern bool commute[NMOVES][NMOVES];
+extern bool possible_next[NMOVES][NMOVES][NMOVES];
+extern Move inverse[NMOVES];
+extern NissMove rotation_algs[24][3]; /* Same order as transformations */
+
+int len(NissMove *alg);
+int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */
+int invert_alg(NissMove *src, NissMove *dest);
+int concat(NissMove *src1, NissMove *src2, NissMove *dest);
+void print_moves(NissMove *alg);
+int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */
+void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */
+
+bool is_solved_up_to_reorient(Cube cube);
+Cube move_cube(Move m, Cube cube);
+Cube apply_alg(NissMove *alg, Cube cube);
+
+/* Merge the following two?
+ always in this order */
+void init_ttables(bool read, bool write);
+void init_aux_tables();
+
+
+#endif
diff --git a/old/2021-02-28-transformcube-works/src/solve.c b/old/2021-02-28-transformcube-works/src/solve.c
@@ -0,0 +1,180 @@
+#include "solve.h"
+
+/* Data for creating a pruning table:
+ - compressed: if set to true, each entry occupies only 4 bits, but values
+ larger than 15 cannot be stored.
+ - available[] is the list of availabel moves, as above.
+ - *ptable is the actual table to fill.
+ - n is the number of states (size of ptable).
+ - index must "linearize" the cube, i.e. return its index in ptable.
+ - fname is the name of the file where to store the table */
+typedef struct {
+ bool compressed, *available;
+ int max_moves;
+ uint8_t *ptable;
+ uint64_t n;
+ uint64_t (*index)(Cube);
+ char *fname;
+} PruneData;
+
+/* TODO: comment this */
+typedef struct {
+ bool niss;
+ int m, d;
+ uint64_t *n;
+ Move last1, last2;
+} DfsData;
+
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd);
+void init_ptable(PruneData *pd, bool read, bool write);
+
+/* Search solutions of lenght exactly d */
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd) {
+ if (*dd.n >= sd->max_solutions ||
+ ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d))
+ return;
+
+ (sd->solutions[*dd.n][dd.m]).inverse = dd.niss;
+ (sd->solutions[*dd.n][dd.m]).m = NULLMOVE;
+
+ if (!sd->f(cube)) { /* Solved */
+ if (dd.m == dd.d) {
+ (*dd.n)++;
+ if (*dd.n < sd->max_solutions)
+ copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]);
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) {
+ Move move = sd->sorted_moves[i];
+ if (possible_next[dd.last2][dd.last1][move]) {
+ sd->solutions[*dd.n][dd.m].inverse = dd.niss;
+ sd->solutions[*dd.n][dd.m].m = move;
+ DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = move, .last2 = dd.last1 };
+ solve_dfs(move_cube(move, cube), sd, nn);
+ }
+ }
+
+ if (sd->can_niss && !dd.niss &&
+ (!dd.m || (dd.m && sd->f(move_cube(dd.last1, (Cube){0}))))) {
+ DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n };
+ solve_dfs(inverse_cube(cube), sd, nn);
+ }
+}
+
+/* Iterative deepening depth-first search: for i running from the minimum
+ to the maximum number of moves allowed, looks for solutions of length i. */
+int solve(Cube cube, SolveData *sd) {
+ if (sd->precondition != NULL && !sd->precondition(cube))
+ return -1;
+
+ /* If not given, generate sorted list of moves */
+ if (sd->sorted_moves[0] == NULLMOVE) {
+ int a[NMOVES], b[NMOVES], ia = 0, ib = 0;
+ for (int i = 0; i < NMOVES; i++) {
+ if (sd->available[i]) {
+ if (sd->f(move_cube(i, (Cube){0})))
+ a[ia++] = i;
+ else
+ b[ib++] = i;
+ }
+ }
+ intarrcopy(a, (int *)sd->sorted_moves, ia);
+ intarrcopy(b, (int *)sd->sorted_moves+ia, ib);
+ sd->sorted_moves[ia+ib] = NULLMOVE;
+ }
+
+ sd->max_solutions = min(sd->max_solutions, MAXS);
+ /*TODO
+ Cube rotated = apply_alg(sd->pre_rotation, (Cube){0});
+ cube = apply_alg(inverse_cube(rotated), compose(cube, rotated));
+ */
+
+ uint64_t ret = 0;
+ for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) {
+ DfsData dd = { .d = i, .n = &ret };
+ solve_dfs(cube, sd, dd);
+ }
+
+ /* TODO: transform solutions with inverse of pre_rotation */
+ /*
+ for (uint64_t i = 0; i < ret; i++) {
+ if (sd->cleanup)
+ cleanup(sd->solutions[i], sd->max_moves*3);
+ }*/
+
+ return ret;
+}
+
+void prune_dfs(Cube cube, PruneData *pd, DfsData dd) {
+ uint64_t ind = pd->index(cube);
+ if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m)
+ return;
+ if (dd.m == dd.d) {
+ if (ind && !pd->ptable[ind]) {
+ pd->ptable[ind] = dd.m;
+ (*dd.n)++;
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES; i++) {
+ if (dd.m<20)
+ if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) {
+ DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = i, .last2 = dd.last1 };
+ prune_dfs(move_cube(i, cube), pd, nn);
+ }
+ }
+}
+
+void init_ptable(PruneData *pd, bool read, bool write) {
+ if (read) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "rb")) != NULL) {
+ uint64_t r = fread(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ if (r == pd->n) return;
+ }
+ }
+
+ /* TODO: for now it behaves always as if copressed = false */
+ for (uint64_t i = 0; i < pd->n; i++)
+ pd->ptable[i] = 0;
+
+ uint64_t s = 1;
+ for (int i = 1; i < pd->max_moves && s < pd->n; i++) {
+ DfsData dd = { .d = i, .n = &s };
+ prune_dfs((Cube){0}, pd, dd);
+ }
+
+ if (write) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "wb")) != NULL) {
+ fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+}
+
+/* Solving steps (and indexing functions) */
+
+uint64_t index_eofb(Cube cube) { return cube.eofb; }
+uint16_t f_eofb(Cube cube) {
+ static bool initialized_ptable;
+ static uint8_t pt_eofb[pow2to11];
+ if (!initialized_ptable) {
+ PruneData pd = {
+ .compressed = false, .available = standard_moveset, .max_moves = 13,
+ .ptable = pt_eofb, .n = pow2to11, .index = index_eofb,
+ .fname = "ptable_eofb"
+ };
+ init_ptable(&pd, false, true);
+ initialized_ptable = true;
+ }
+ return cube.eofb ? pt_eofb[cube.eofb] : 0;
+}
+
diff --git a/old/2021-02-28-transformcube-works/src/solve.h b/old/2021-02-28-transformcube-works/src/solve.h
@@ -0,0 +1,56 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include <stdlib.h>
+#include "cube.h"
+#include "moves.h"
+
+/* Maximum number of moves per solution and of solutions */
+#define MAXM 30
+#define MAXS 999
+
+/* Data for solving a step:
+ - can_niss is true niss can be used, false otherwise.
+ - optimal_only if true, dynamically updates max_moves so non-optimal
+ solutions are discarded.
+ - cleanup determines whether the cleaunup() function should be used on
+ the found solutions before returning.
+ - available[m] is true if the move m can be used, false otherwise.
+ - min_moves and max_moves are the minimum and maximum number of moves that
+ can be used.
+ - max_solution is the maximum number of solutions that can be returned.
+ - precondition can be used to check wheter the step can actually be applied
+ to the cube. If it returns false, solve() stops immediately returning -1.
+ - f must return 0 if and only if the step is solve, otherwise it must return
+ a lower bound for the number of moves required (without niss).
+ - sorted_moves[] can be used to specify in which order moves are tried
+ by the solving algorithm (for example if one wants to always try F' before
+ F). If sorted_moves[0] == NULLMOVE, the list is generated automatically.
+ It is advised to list first all the moves that actually influence the
+ solved state of the step (this is the default choice). This is in order to
+ avoid cases like B2 F for EO and to NISS only when it makes sense.
+ - start_moves [Currently unused, REMOVE]
+ are the moves that will be used as first moves of all
+ solutions. For example giving R' U' F (F' U R) will generate FMC scrambles
+ and y (y) will solve the step on another axis.
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wrt a different orientation
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wth respect to a different orientation.
+ - solutions[][] is the array where to store the found solutions. */
+typedef struct {
+ bool can_niss, optimal_only, cleanup, *available;
+ int min_moves, max_moves;
+ uint64_t max_solutions;
+ bool (*precondition)(Cube);
+ uint16_t (*f)(Cube);
+ Move sorted_moves[NMOVES];
+ NissMove pre_rotation[3], solutions[MAXS][MAXM];
+} SolveData;
+
+int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */
+
+/* Steps */
+uint16_t f_eofb(Cube cube);
+
+#endif
diff --git a/old/2021-02-28-transformcube-works/src/transformations.c b/old/2021-02-28-transformcube-works/src/transformations.c
@@ -0,0 +1,224 @@
+#include "transformations.h"
+
+int edge_slice(int e); /* Return slice (e=0, s=1, m=2) to which e belongs */
+Cube rotate_via_compose(Transformation r, Cube c);
+bool read_rtables_file();
+bool write_rtables_file();
+
+/* Values mod 3 to determine from which side to take the state to convert */
+int epose_source[NROTATIONS]; /* 0 = epose, 1 = eposs, 2 = eposm */
+int eposs_source[NROTATIONS];
+int eposm_source[NROTATIONS];
+int eofb_source[NROTATIONS]; /* 0 = eoud, 1 = eorl, 2 = eofb */
+int eorl_source[NROTATIONS];
+int eoud_source[NROTATIONS];
+int coud_source[NROTATIONS]; /* 0 = coud, 1 = corl, 2 = cofb */
+int cofb_source[NROTATIONS];
+int corl_source[NROTATIONS];
+
+/* Transition tables for rotations (n+1 is mirror) */
+uint16_t epose_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eposs_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eposm_rtable[NROTATIONS][factorial12/factorial8];
+uint16_t eo_rtable[NROTATIONS][pow2to11];
+/*uint16_t eofb_rtable[NROTATIONS][pow2to11];
+uint16_t eorl_rtable[NROTATIONS][pow2to11];
+uint16_t eoud_rtable[NROTATIONS][pow2to11];*/
+uint16_t cp_rtable[NROTATIONS][factorial8];
+uint16_t co_rtable[NROTATIONS][pow3to7];
+/*uint16_t coud_rtable[NROTATIONS][pow3to7];
+uint16_t cofb_rtable[NROTATIONS][pow3to7];
+uint16_t corl_rtable[NROTATIONS][pow3to7];*/
+uint16_t cpos_rtable[NROTATIONS][factorial6];
+
+/* Same for moves */
+uint16_t move_rtable[NROTATIONS][NMOVES];
+
+NissMove rotation_niss[NROTATIONS][6];
+
+int edge_slice(int e) {
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+ return 2;
+}
+
+Cube rotate_via_compose(Transformation r, Cube c) {
+ if (r != mirror) {
+ return apply_alg(rotation_niss[r], c);
+ } else {
+ static int zero12[12] = {0,0,0,0,0,0,0,0,0,0,0,0},
+ zero8[12] = {0,0,0,0,0,0,0,0},
+ mirror_ep[12] = {UF,UR,UB,UL,DF,DR,DB,DL,FL,FR,BR,BL},
+ mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL},
+ mirror_cpos[6] =
+ {U_center,D_center,L_center,R_center,F_center,B_center};
+ return move_via_arrays((CubeArray){
+ .ep = mirror_ep, .eofb = zero12, .eorl = zero12, .eoud = zero12,
+ .cp = mirror_cp, .coud = zero8, .corl = zero8, .cofb = zero8,
+ .cpos = mirror_cpos}, c, pf_all);
+ }
+}
+
+bool read_rtables_file() {
+ FILE *ttf;
+ long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES };
+ if ((ttf = fopen("rtables", "rb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NROTATIONS; m++) {
+ r = r && fread(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fread(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fread(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fread(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ /*r = r && fread(eofb_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fread(eorl_rtable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fread(eoud_rtable[m], sizeof(uint16_t), me[5], ttf) == me[5];*/
+ r = r && fread(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fread(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ /*r = r && fread(coud_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fread(corl_rtable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fread(cofb_rtable[m], sizeof(uint16_t), me[9], ttf) == me[9];*/
+ r = r && fread(cpos_rtable[m], sizeof(uint16_t), me[10], ttf) == me[10];
+ r = r && fread(move_rtable[m], sizeof(uint16_t), me[11], ttf) == me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+bool write_rtables_file() {
+ FILE *ttf;
+ long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES };
+ if ((ttf = fopen("rtables", "wb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NROTATIONS; m++) {
+ r = r && fwrite(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fwrite(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fwrite(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fwrite(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ /*r = r && fwrite(eofb_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fwrite(eorl_rtable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fwrite(eoud_rtable[m], sizeof(uint16_t), me[5], ttf) == me[5];*/
+ r = r && fwrite(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fwrite(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ /*r = r && fwrite(coud_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fwrite(corl_rtable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fwrite(cofb_rtable[m], sizeof(uint16_t), me[9], ttf) == me[9];*/
+ r = r && fwrite(cpos_rtable[m], sizeof(uint16_t), me[10],ttf) == me[10];
+ r = r && fwrite(move_rtable[m], sizeof(uint16_t), me[11],ttf) == me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+void init_transformations(bool read, bool write) {
+ /* Compute sources */
+ for (int i = 0; i < NROTATIONS; i++) {
+ Cube cube = {0};
+ if (i != mirror)
+ cube = apply_alg(rotation_algs[i], (Cube){0});
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ /*TODO: maybe move down*/
+ /* Compute rotation_niss array, necessary for rotate_via_compose */
+ for (int r = 0; r != mirror; r++) {
+ concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]);
+ for (int i = len(rotation_algs[r]); rotation_niss[r][i].m != NULLMOVE; i++)
+ rotation_niss[r][i].inverse = true;
+ }
+
+ /* If I can read tables from file, I stop here */
+ if (read)
+ if (read_rtables_file())
+ return;
+
+ /* Initialize tables */
+ for (int m = 0; m < NROTATIONS; m++) {
+ int eparr[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, cparr[8] = {0,0,0,0,0,0,0,0};
+ CubeArray epcp = { .ep = eparr, .cp = cparr };
+ cube_to_arrays(apply_alg(rotation_algs[m], (Cube){0}), &epcp,
+ (PieceFilter){.epose=true,.eposs=true,.eposm=true,.cp=true});
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ Cube c[3] = { admissible_ep((Cube){ .epose = i}, pf_e),
+ admissible_ep((Cube){ .eposs = i}, pf_s),
+ admissible_ep((Cube){ .eposm = i}, pf_m) };
+ epose_rtable[m][i] = rotate_via_compose(m, c[epose_source[m]]).epose;
+ eposs_rtable[m][i] = rotate_via_compose(m, c[eposs_source[m]]).eposs;
+ eposm_rtable[m][i] = rotate_via_compose(m, c[eposm_source[m]]).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ int eoarr[12];
+ int_to_sum_zero_array(i, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_rtable[m][i] = digit_array_to_int(eoarr, 11, 2);
+ /*Cube c[3] = {(Cube){.eoud=i}, (Cube){.eorl=i}, (Cube){.eofb=i}};
+ eofb_rtable[m][i] = apply_alg(rotation_algs[m], (Cube){.eofb=i}).eofb;
+ eorl_rtable[m][i] = rotate_via_compose(m, c[eorl_source[m]]).eorl;
+ eoud_rtable[m][i] = rotate_via_compose(m, c[eoud_source[m]]).eoud;*/
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ int coarr[12];
+ int_to_sum_zero_array(i, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_rtable[m][i] = digit_array_to_int(coarr, 8, 3);
+ /*Cube c[3] = {(Cube){.coud=i}, (Cube){.corl=i}, (Cube){.cofb=i}};
+ coud_rtable[m][i] = rotate_via_compose(m, c[coud_source[m]]).coud;
+ corl_rtable[m][i] = rotate_via_compose(m, c[corl_source[m]]).corl;
+ cofb_rtable[m][i] = rotate_via_compose(m, c[cofb_source[m]]).cofb;*/
+ }
+ for (uint16_t i = 0; i < factorial8; i++)
+ cp_rtable[m][i] = rotate_via_compose(m, (Cube){.cp=i}).cp;
+ for (uint16_t i = 0; i < factorial6; i++)
+ cpos_rtable[m][i] = rotate_via_compose(m, (Cube){.cpos=i}).cpos;
+ }
+
+ if (write)
+ if (!write_rtables_file())
+ printf("Error in writing rtables: file not writable\n");
+}
+
+Cube transform_cube(Transformation t, Cube cube) {
+ Cube transformed = {0};
+
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm },
+ aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb },
+ aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]];
+ transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]];
+ transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]];
+ transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]];
+ transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]];
+ transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]];
+ transformed.coud = co_rtable[t][aux_co[coud_source[t]]];
+ transformed.corl = co_rtable[t][aux_co[corl_source[t]]];
+ transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]];
+ transformed.cp = cp_rtable[t][cube.cp];
+ transformed.cpos = cpos_rtable[t][cube.cpos];
+
+/*
+ printf("%d\n", coud_source[t]);
+ int ccc[8];
+ int_to_sum_zero_array(cube.cofb, 3, 8, ccc);
+ for (int i = 0; i < 8; i++)
+ printf("%d ", ccc[i]);
+ printf("\n");
+ */
+
+ return transformed;
+}
diff --git a/old/2021-02-28-transformcube-works/src/transformations.h b/old/2021-02-28-transformcube-works/src/transformations.h
@@ -0,0 +1,34 @@
+#ifndef TRANSFORMATIONS_H
+#define TRANSFORMATIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "moves.h"
+#include "utils.h"
+
+#define NROTATIONS (mirror+1)
+
+/* Letters indicate top and front centers
+ * Mirror is wrt rl
+ * Lowercase letter to distinguish from pieces */
+
+typedef enum {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror,
+} Transformation;
+
+void print_transformation(Transformation t);
+
+Cube transform_cube(Transformation t, Cube cube);
+void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */
+
+void init_transformations(bool read, bool write);
+
+#endif
diff --git a/old/2021-02-28-transformcube-works/src/utils.c b/old/2021-02-28-transformcube-works/src/utils.c
@@ -0,0 +1,197 @@
+#include "utils.h"
+
+void swap(int *a, int *b) {
+ int aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+void intarrcopy(int *src, int *dst, int n) {
+ for (int i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+int sum(int *a, int n) {
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ ret += a[i];
+ return ret;
+}
+
+bool is_perm(int *a, int n) {
+ int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0;
+ for (int i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+ for (int i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+ return true;
+}
+
+bool is_subset(int *a, int n, int k) {
+ int sum = 0;
+ for (int i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+ return sum == k;
+}
+
+int powint(int a, int b) {
+ return 0;
+ if (b == 0 || a == 1)
+ return 1;
+ if (a == 0)
+ return 0;
+ if (b < 0)
+ return 0; /* Immediate truncate (integer part is 0) */
+ if (b % 2) {
+ return a * powint(a, b-1);
+ } else {
+ int x = powint(a, b/2);
+ return x*x;
+ }
+}
+
+int factorial(int n) {
+ if (n < 0)
+ return 0;
+ int ret = 1;
+ for (int i = 1; i <= n; i++)
+ ret *= i;
+ return ret;
+}
+
+int binomial(int n, int k) {
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+void int_to_digit_array(int a, int b, int n, int *r) {
+ if (b <= 1)
+ for (int i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (int i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+int digit_array_to_int(int *a, int n, int b) {
+ int ret = 0, p = 1;
+ for (int i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+ return ret;
+}
+
+int perm_to_index(int *a, int n) {
+ if (!is_perm(a, n))
+ return factorial(n); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ for (int j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+ return ret;
+}
+
+void index_to_perm(int p, int n, int *r) {
+ if (p < 0 || p >= factorial(n)) /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */
+ for (int i = 0; i < n; i++) {
+ int c = 0, j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+}
+
+int perm_sign(int *a, int n) {
+ if (!is_perm(a,n))
+ return false;
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ for (int j = i+1; j < n; j++)
+ ret += (a[i]>a[j]) ? 1 : 0;
+ return ret % 2;
+}
+
+int subset_to_index(int *a, int n, int k) {
+ /* TODO: better checks */
+ if (!is_subset(a, n, k))
+ return binomial(n, k); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+ return ret;
+}
+
+void index_to_subset(int s, int n, int k, int *r) {
+ if (s < 0 || s >= binomial(n, k)) { /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (int j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+ if (k == 0) {
+ for (int j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+ /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ int v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void int_to_sum_zero_array(int x, int b, int n, int *a) {
+ if (b <= 1) {
+ for (int i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ int s = 0;
+ for (int i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+void apply_permutation(int *perm, int *set, int n) {
+ if (!is_perm(perm, n))
+ return;
+ int aux[n];
+ for (int i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+ intarrcopy(aux, set, n);
+}
+
+void sum_arrays_mod(int *a, int *b, int n, int m) {
+ for (int i = 0; i < n; i++)
+ b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m;
+}
diff --git a/old/2021-02-28-transformcube-works/src/utils.h b/old/2021-02-28-transformcube-works/src/utils.h
@@ -0,0 +1,70 @@
+/* General utility functions */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Some useful constants */
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+/* Generic utility functions */
+void swap(int *a, int *b);
+void intarrcopy(int *src, int *dst, int n);
+int sum(int *a, int n);
+bool is_perm(int *a, int n);
+bool is_perm(int *a, int n);
+
+
+/* Standard mathematical functions */
+int powint(int a, int b);
+int factorial(int n);
+int binomial(int n, int k);
+
+/* Converts the integer a to its representation in base b (first n digits
+ * only) and saves the result in r. */
+void int_to_digit_array(int a, int b, int n, int *r);
+int digit_array_to_int(int *a, int n, int b);
+
+/* Converts the first n-1 digits of a number to an array a of digits in base b;
+ * then adds one element to the array, so that the sum of the elements of a is
+ * zero modulo b.
+ * This is used for determing the edge orientation from an 11-bits integer or
+ * the corner orientation from a 7-trits integer. */
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+
+/* Converts a permutation on [0..(n-1)] into the integer i which is the index
+ * of the permutation in the sorted list of all n! such permutations. */
+int perm_to_index(int *a, int n);
+void index_to_perm(int p, int n, int *r);
+
+/* Determine the sign of a permutation */
+int perm_sign(int a[], int n);
+
+/* Converts a k-element subset of a set from an array of n elements, of which k
+ * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */
+int subset_to_index(int *a, int n, int k);
+void index_to_subset(int s, int n, int k, int *r);
+
+int ordered_subset_to_index(int *a, int n, int k);
+void index_to_ordered_subset(int s, int n, int k, int *r);
+
+void apply_permutation(int *perm, int *set, int n);
+
+/* b[i] = (a[i]+b[i])%m for i=1,...,n */
+void sum_arrays_mod(int *a, int *b, int n, int m);
+
+#endif
diff --git a/old/2021-05-26-before-restyle/cube.c b/old/2021-05-26-before-restyle/cube.c
@@ -0,0 +1,293 @@
+#include "cube.h"
+
+typedef struct {
+ int ep[12],eofb[12],eorl[12],eoud[12],cp[8],coud[8],corl[8],cofb[8],cpos[6];
+} CubeArrayAllocated;
+
+void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all);
+
+char edge_string[12][5] =
+ { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" };
+char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" };
+char center_string[6][5] = { "U", "D", "R", "L", "F", "B" };
+
+int epe_solved[4] = {FR, FL, BL, BR};
+int eps_solved[4] = {UL, UR, DL, DR};
+int epm_solved[4] = {UF, UB, DF, DB};
+
+PieceFilter pf_all = {true,true,true,true,true,true,true,true,true,true,true},
+ pf_cpos = { .cpos = true }, pf_cp = { .cp = true },
+ pf_ep = { .epose = true, .eposs = true, .eposm = true },
+ pf_e = {.epose=true}, pf_s={.eposs=true}, pf_m={.eposm=true},
+ pf_eo = { .eofb = true, .eorl = true, .eoud = true },
+ pf_co = { .coud = true, .cofb = true, .corl = true };
+
+void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all) {
+ arr->ep = all->ep;
+ arr->eofb = all->eofb;
+ arr->eorl = all->eorl;
+ arr->eoud = all->eoud;
+ arr->cp = all->cp;
+ arr->coud = all->coud;
+ arr->corl = all->corl;
+ arr->cofb = all->cofb;
+ arr->cpos = all->cpos;
+}
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) {
+ /* ep is the hardest */
+ if (f.epose || f.eposs || f.eposm)
+ for (int i = 0; i < 12; i++) arr->ep[i] = -1;
+ if (f.epose) {
+ int epe[4], epose[12];
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ for (int i = 0, ie = 0; i < 12; i++)
+ if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]];
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12];
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ for (int i = 0, is = 0; i < 12; i++)
+ if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]];
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12];
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ for (int i = 0, im = 0; i < 12; i++)
+ if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]];
+ }
+
+ /* All the others */
+ if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp) index_to_perm( cube.cp, 8, arr->cp);
+ if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos);
+}
+
+Cube arrays_to_cube(CubeArray arr, PieceFilter f) {
+ Cube ret = {0};
+
+ /* Again, ep is the hardest part */
+ if (f.epose) {
+ int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, ie = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epe_solved[j])
+ { epe[ie++] = j; epose[i] = 1; }
+ ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4);
+ }
+ if (f.eposs) {
+ int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, is = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == eps_solved[j])
+ { eps[is++] = j; eposs[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4);
+ }
+ if (f.eposm) {
+ int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ for (int i = 0, im = 0; i < 12; i++)
+ for (int j = 0; j < 4; j++)
+ if (arr.ep[i] == epm_solved[j])
+ { epm[im++] = j; eposm[i] = 1; }
+ for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4);
+ }
+ if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2);
+ if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2);
+ if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2);
+ if (f.cp) ret.cp = perm_to_index( arr.cp, 8 );
+ if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3);
+ if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3);
+ if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3);
+ if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 );
+
+ return ret;
+}
+
+int piece_orientation(Cube cube, int piece, char *orientation) {
+ int arr[12], n, b;
+ uint16_t x;
+ if (!strcmp(orientation, "eofb")) { x = cube.eofb; n = 12; b = 2; } else
+ if (!strcmp(orientation, "eorl")) { x = cube.eorl; n = 12; b = 2; } else
+ if (!strcmp(orientation, "eoud")) { x = cube.eoud; n = 12; b = 2; } else
+ if (!strcmp(orientation, "coud")) { x = cube.coud; n = 8; b = 3; } else
+ if (!strcmp(orientation, "corl")) { x = cube.corl; n = 8; b = 3; } else
+ if (!strcmp(orientation, "cofb")) { x = cube.cofb; n = 8; b = 3; }
+ else return -1;
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+ return -1;
+}
+
+Center center_at(Cube cube, Center c) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_cpos);
+ return arr.cpos[c];
+}
+
+Edge edge_at(Cube cube, Edge e) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_ep);
+ return arr.ep[e];
+}
+
+Corner corner_at(Cube cube, Corner c) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_cp);
+ return arr.cp[c];
+}
+
+bool block_solved(Cube cube, Block block) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arr = {0};
+ allocate_cubearray(&arr, &all);
+ cube_to_arrays(cube, &arr, pf_all);
+
+ bool ret = true;
+
+ for (int i = 0; i < 12; i++)
+ ret = ret && !(block.edge[i] && (arr.ep[i] != i || arr.eofb[i]));
+ for (int i = 0; i < 8; i++)
+ ret = ret && !(block.corner[i] && (arr.cp[i] != i || arr.coud[i]));
+ for (int i = 0; i < 6; i++)
+ ret = ret && !(block.center[i] && arr.cpos[i] != i);
+
+ return ret;
+}
+
+bool equal(Cube c1, Cube c2) {
+ return c1.eofb == c2.eofb && c1.epose == c2.epose &&
+ c1.eposs == c2.eposs && c1.eposm == c2.eposm &&
+ c1.coud == c2.coud && c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+bool is_solved(Cube cube) {
+ /* TODO: might return true if cube is not solvable but looks solved form one
+ of the incompatible interpretations (e.g. eofb and ep solved, but
+ eorl not solve) */
+ return !cube.eofb && !cube.coud && !cube.cp &&
+ !cube.epose && !cube.eposs && !cube.eposm && !cube.cpos;
+}
+
+void print_cube(Cube cube) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+ cube_to_arrays(cube, &arrx, pf_all);
+
+ for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arrx.ep[i]]);
+ printf("\n");
+ for (int i = 0; i < 12; i++) printf(" %c ", arrx.eofb[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf("%s ", corner_string[arrx.cp[i]]);
+ printf("\n");
+ for (int i = 0; i < 8; i++) printf(" %c ", arrx.coud[i] + '0');
+ printf("\n");
+ for (int i = 0; i < 6; i++) printf(" %s ", center_string[arrx.cpos[i]]);
+ printf("\n");
+}
+
+Cube admissible_ep(Cube cube, PieceFilter f) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+ cube_to_arrays(cube, &arrx, f);
+
+ bool used[12] = {0};
+ for (int i = 0; i < 12; i++)
+ if (arrx.ep[i] != -1)
+ used[arrx.ep[i]] = true;
+ for (int i = 0, j = 0; i < 12; i++) {
+ while (j < 11 && used[j]) j++;
+ if (arrx.ep[i] == -1)
+ arrx.ep[i] = j++;
+ }
+
+ return arrays_to_cube(arrx, pf_ep);
+}
+
+Cube inverse_cube(Cube cube) {
+ static CubeArrayAllocated all = {0}, invall = {0};
+ CubeArray arrx = {0}, invx = {0};
+ allocate_cubearray(&arrx, &all);
+ allocate_cubearray(&invx, &invall);
+
+ cube_to_arrays(cube, &arrx, pf_all);
+
+ for (int i = 0; i < 12; i++) {
+ invx.ep[arrx.ep[i]] = i;
+ invx.eofb[arrx.ep[i]] = arrx.eofb[i];
+ invx.eorl[arrx.ep[i]] = arrx.eorl[i];
+ invx.eoud[arrx.ep[i]] = arrx.eoud[i];
+ }
+ for (int i = 0; i < 8; i++) {
+ invx.cp[arrx.cp[i]] = i;
+ invx.coud[arrx.cp[i]] = (3 - arrx.coud[i])%3;
+ invx.corl[arrx.cp[i]] = (3 - arrx.corl[i])%3;
+ invx.cofb[arrx.cp[i]] = (3 - arrx.cofb[i])%3;
+ }
+ for (int i = 0; i < 6; i++)
+ invx.cpos[arrx.cpos[i]] = i;
+
+ return arrays_to_cube(invx, pf_all);
+}
+
+Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter f) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+
+ cube_to_arrays(c, &arrx, f);
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation( arr.ep, arrx.ep, 12 );
+ if (f.eofb) { apply_permutation( arr.ep, arrx.eofb, 12 );
+ sum_arrays_mod( arr.eofb, arrx.eofb, 12, 2 ); }
+ if (f.eorl) { apply_permutation( arr.ep, arrx.eorl, 12 );
+ sum_arrays_mod( arr.eorl, arrx.eorl, 12, 2 ); }
+ if (f.eoud) { apply_permutation( arr.ep, arrx.eoud, 12 );
+ sum_arrays_mod( arr.eoud, arrx.eoud, 12, 2 ); }
+ if (f.cp) apply_permutation( arr.cp, arrx.cp, 8 );
+ if (f.coud) { apply_permutation( arr.cp, arrx.coud, 8 );
+ sum_arrays_mod( arr.coud, arrx.coud, 8, 3 ); }
+ if (f.corl) { apply_permutation( arr.cp, arrx.corl, 8 );
+ sum_arrays_mod( arr.corl, arrx.corl, 8, 3 ); }
+ if (f.cofb) { apply_permutation( arr.cp, arrx.cofb, 8 );
+ sum_arrays_mod( arr.cofb, arrx.cofb, 8, 3 ); }
+ if (f.cpos) apply_permutation( arr.cpos, arrx.cpos, 6 );
+
+ return arrays_to_cube(arrx, f);
+}
+
+Cube compose_filtered(Cube c2, Cube c1, PieceFilter f) {
+ static CubeArrayAllocated all = {0};
+ CubeArray arrx = {0};
+ allocate_cubearray(&arrx, &all);
+
+ cube_to_arrays(c2, &arrx, f);
+ return move_via_arrays(arrx, c1, f);
+}
+
+Cube compose(Cube c2, Cube c1) {
+ return compose_filtered(c2, c1, pf_all);
+}
diff --git a/old/2021-05-26-before-restyle/cube.h b/old/2021-05-26-before-restyle/cube.h
@@ -0,0 +1,65 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "utils.h"
+
+typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center;
+typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge;
+typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner;
+
+typedef struct {
+ uint16_t eofb, eorl, eoud, coud, cofb, corl,
+ epose, eposs, eposm, cp, cpos;
+} Cube;
+
+typedef struct {
+ bool edge[12], corner[8], center[6];
+} Block;
+
+typedef struct {
+ bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos;
+} PieceFilter;
+
+typedef struct {
+ int *ep, *eofb, *eorl, *eoud, *cp, *coud, *corl, *cofb, *cpos;
+} CubeArray;
+
+extern PieceFilter pf_all, pf_cpos, pf_ep, pf_cp,
+ pf_e, pf_s, pf_m, pf_eo, pf_co;
+
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+Cube arrays_to_cube(CubeArray arr, PieceFilter f);
+
+/* piece can be edge or corner and orientation is any of the following:
+ "eofb", "eorl", "eoud", "coud", "corl", "cofb"
+ Return either 0 (oriented) or 1 for edges and 0, 1 or 2 for corners */
+int piece_orientation(Cube cube, int piece, char *orientation);
+Center center_at(Cube cube, Center c);
+Edge edge_at(Cube cube, Edge e);
+Corner corner_at(Cube cube, Corner c);
+bool block_solved(Cube cube, Block);
+/* Aggiungi funzioni per "queries" sul cubo: se pezzo è orientato rispetto ad
+ un certo asse, se il pezzo è risolto... */
+/* Would be nice: a funciton block_solved(Cube c, Block b), where Block is
+ something like struct {bool centers[6], edges[12], corners[8]}
+ (The advantage over checking pieces one by one is that I can convert
+ to cubearray only once and for all) */
+/* Altro TODO, ma forse non ne vale la pena: pre-calcolare tutti i possibili
+ valori per questi, e salvare i risultati in array (facile per cp e cpos,
+ mentre per ep bisogna anche cercare quale tra epose, eposs e eposm contiene
+ il valore giusto) */
+
+bool equal(Cube c1, Cube c2);
+bool is_solved(Cube cube);
+void print_cube(Cube cube);
+Cube admissible_ep(Cube cube, PieceFilter f); /* Returns admissible ep */
+Cube inverse_cube(Cube cube);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf);
+
+#endif
diff --git a/old/2021-05-26-before-restyle/main.c b/old/2021-05-26-before-restyle/main.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include "cube.h"
+#include "moves.h"
+#include "solve.h"
+#include "transformations.h"
+
+int main() {
+ init_ttables(true, true);
+ init_aux_tables();
+ init_transformations(true, true);
+
+
+ char moves[100] = "R' D2 F2 U2 R F2 R D2 L' R2 D2 F D' L' U' B R' D' U R' B";
+ NissMove alg[100];
+ read_moves(moves, alg, 100);
+ Cube cube = apply_alg(alg, (Cube){0});
+ print_cube(transform_cube(rd, cube));
+
+ transform_alg(rd, alg);
+ print_alg(alg);
+
+
+ return 0;
+}
diff --git a/old/2021-05-26-before-restyle/moves.c b/old/2021-05-26-before-restyle/moves.c
@@ -0,0 +1,412 @@
+#include "moves.h"
+
+Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+/* void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); */
+bool read_ttables_file();
+bool write_ttables_file();
+
+/* Transition tables */
+uint16_t epose_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposs_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposm_ttable[NMOVES][factorial12/factorial8];
+uint16_t eofb_ttable[NMOVES][pow2to11];
+uint16_t eorl_ttable[NMOVES][pow2to11];
+uint16_t eoud_ttable[NMOVES][pow2to11];
+uint16_t cp_ttable[NMOVES][factorial8];
+uint16_t coud_ttable[NMOVES][pow3to7];
+uint16_t cofb_ttable[NMOVES][pow3to7];
+uint16_t corl_ttable[NMOVES][pow3to7];
+uint16_t cpos_ttable[NMOVES][factorial6];
+
+bool commute[NMOVES][NMOVES];
+bool possible_next[NMOVES][NMOVES][NMOVES];
+Move inverse[NMOVES];
+NissMove rotation_algs[24][3] = {
+ { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } },
+};
+
+char move_string[NMOVES][5] =
+ { "-",
+ "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
+ "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'",
+ "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'",
+ "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" };
+
+/* For each type of pieces only the effects of U, x and y are described */
+int edge_cycle[NMOVES][12] =
+ { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR},
+ [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR},
+ [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} };
+int eofb_flipped[NMOVES][12] =
+ { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eorl_flipped[NMOVES][12] =
+ { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } };
+int eoud_flipped[NMOVES][12] =
+ { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
+int corner_cycle[NMOVES][8] =
+ { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} };
+int coud_flipped[NMOVES][8] =
+ { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} };
+int corl_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} };
+int cofb_flipped[NMOVES][8] =
+ { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=1,[UFL]=2,[DBL]=2,[DFL]=1},
+ [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} };
+int center_cycle[NMOVES][6] =
+ { [x] = {F_center, B_center, R_center, L_center, D_center, U_center},
+ [y] = {U_center, D_center, B_center, F_center, R_center, L_center} };
+
+/* Each move is reduced to a combination of U, x and y using this table */
+Move equiv_moves[NMOVES][14] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 },
+ [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 },
+ [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 },
+ [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 },
+ [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 },
+ [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 },
+ [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 },
+ [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 },
+ [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 },
+ [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 },
+ [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 },
+ [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 },
+ [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 },
+ [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 },
+ [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 },
+ [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+
+ [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 },
+ [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 },
+ [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 },
+ [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 },
+ [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 },
+ [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 },
+ [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 },
+ [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 },
+ [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 },
+
+ [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* Movesets */
+bool standard_moveset[NMOVES] = {
+ [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true,
+ [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true,
+ [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true,
+};
+
+bool is_solved_up_to_reorient(Cube cube) {
+ for (int i = 0; i < 25; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube)))
+ return true;
+ return false;
+}
+
+Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f) {
+ return move_via_arrays((CubeArray)
+ { edge_cycle[m], eofb_flipped[m], eorl_flipped[m], eoud_flipped[m],
+ corner_cycle[m], coud_flipped[m], corl_flipped[m], cofb_flipped[m],
+ center_cycle[m] }, cube, f);
+}
+
+int len(NissMove *alg) {
+ int i;
+ for (i = 0; alg[i].m != NULLMOVE; i++);
+ return i;
+}
+
+int copy_alg(NissMove *src, NissMove *dest) {
+ int i;
+ for (i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+ dest[i].m = NULLMOVE;
+ return i;
+}
+
+int invert_alg(NissMove *src, NissMove *dest) {
+ int n = len(src);
+ for (int i = 0; i < n; i++)
+ dest[n-i-1] = (NissMove){.m=inverse[src[i].m], .inverse=src[i].inverse};
+ dest[n].m = NULLMOVE;
+ return n;
+}
+
+int concat(NissMove *src1, NissMove *src2, NissMove *dest) {
+ int n1 = len(src1), n2 = len(src2);
+ copy_alg(src1, dest);
+ copy_alg(src2, dest+n1);
+ return n1+n2;
+}
+
+/* TODO: all strings start with space?? */
+void print_alg(NissMove *alg) {
+ bool niss = false;
+ for (int i = 0; alg[i].m != NULLMOVE; i++) {
+ char *fill = !niss && alg[i].inverse ? " (" :
+ (niss && !alg[i].inverse ? ") " : " ");
+ printf("%s%s", fill, move_string[alg[i].m]);
+ niss = alg[i].inverse;
+ }
+ printf("%s\n", niss ? ")" : "");
+}
+
+int read_moves(char *str, NissMove *alg, int n) {
+ bool niss = false;
+ int c = 0;
+
+ for (int i = 0; str[i] && c < n; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' || str[i] == ')') {
+ if ((niss && str[i] == '(') || (!niss && str[i] == ')'))
+ return -1;
+ niss = !niss;
+ continue;
+ }
+
+ alg[c].inverse = niss; alg[c].m = NULLMOVE;
+ for (Move j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ alg[c].m = j;
+ if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; }
+ if (str[i+1]=='2') { alg[c].m += 1; i++; }
+ else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; }
+ c++;
+ break;
+ }
+ }
+ }
+
+ alg[c].m = NULLMOVE;
+ return c;
+}
+
+/* TODO: rewrite cleanup(). Idea:
+ 1. split alg in normal + inverse part, rewriting each with equiv_moves
+ 2. shift rotations to the end while transforming all moves in between
+ ARGH! this uses transformation! One more reason to merge everything in
+ one file.
+ 3. cancellations
+*/
+
+bool read_ttables_file() {
+ FILE *ttf;
+ long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6 };
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fread(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fread(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fread(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fread(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fread(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5];
+ r = r && fread(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fread(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fread(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fread(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9];
+ r = r && fread(cpos_ttable[m], sizeof(uint16_t), me[10], ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+bool write_ttables_file() {
+ FILE *ttf;
+ long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6 };
+ if ((ttf = fopen("ttables", "wb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fwrite(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fwrite(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fwrite(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fwrite(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4];
+ r = r && fwrite(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5];
+ r = r && fwrite(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fwrite(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fwrite(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8];
+ r = r && fwrite(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9];
+ r = r && fwrite(cpos_ttable[m], sizeof(uint16_t), me[10],ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+void init_ttables(bool read, bool write) {
+ /* Generate all move cycles and flips; I do this regardless */
+ for (int i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ Cube c = {0};
+ for (int j = 0; equiv_moves[i][j]; j++)
+ c = apply_move_cubearray(equiv_moves[i][j], c, pf_all);
+
+ CubeArray arrs = {
+ edge_cycle[i], eofb_flipped[i], eorl_flipped[i], eoud_flipped[i],
+ corner_cycle[i], coud_flipped[i], corl_flipped[i], cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read)
+ if (read_ttables_file())
+ return;
+
+ /* Initialize transition tables */
+ for (int m = 0; m < NMOVES; m++) {
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ epose_ttable[m][i] = apply_move_cubearray(m,(Cube){.epose=i},pf_e).epose;
+ eposs_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposs=i},pf_s).eposs;
+ eposm_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposm=i},pf_m).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ eofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.eofb=i},pf_eo).eofb;
+ eorl_ttable[m][i] = apply_move_cubearray(m,(Cube){.eorl=i},pf_eo).eorl;
+ eoud_ttable[m][i] = apply_move_cubearray(m,(Cube){.eoud=i},pf_eo).eoud;
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ coud_ttable[m][i] = apply_move_cubearray(m,(Cube){.coud=i},pf_co).coud;
+ corl_ttable[m][i] = apply_move_cubearray(m,(Cube){.corl=i},pf_co).corl;
+ cofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.cofb=i},pf_co).cofb;
+ }
+ for (uint16_t i = 0; i < factorial8; i++)
+ cp_ttable[m][i] = apply_move_cubearray(m,(Cube){.cp=i},pf_cp).cp;
+ for (uint16_t i = 0; i < factorial6; i++)
+ cpos_ttable[m][i] = apply_move_cubearray(m,(Cube){.cpos=i},pf_cpos).cpos;
+ }
+
+ if (write)
+ if (!write_ttables_file())
+ printf("Error in writing ttables: file not writable\n");
+}
+
+Cube move_cube(Move m, Cube cube) {
+ Cube moved = {0};
+
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.cpos = cpos_ttable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube apply_alg_filtered(NissMove *alg, Cube cube, PieceFilter f) {
+ Cube ret = {0};
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+
+ ret = compose_filtered(cube, inverse_cube(ret), f);
+
+ for (int i = 0; alg[i].m != NULLMOVE; i++)
+ if (!alg[i].inverse)
+ ret = move_cube(alg[i].m, ret);
+ return ret;
+}
+
+Cube apply_alg(NissMove *alg, Cube cube) {
+ return apply_alg_filtered(alg, cube, pf_all);
+}
+
+void init_aux_tables() {
+ /* Commute */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ commute[i][j] = equal(move_cube(i, move_cube(j, (Cube){0})),
+ move_cube(j, move_cube(i, (Cube){0})));
+
+ /* Possible next (if the sequence i j k is valid) */
+ for (int i = 0; i < NMOVES; i++)
+ for (int j = 0; j < NMOVES; j++)
+ for (int k = 0; k < NMOVES; k++)
+ possible_next[i][j][k] =
+ (j == 0) ||
+ (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) &&
+ !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3)));
+
+ /* Inverse */
+ for (int i = 0; i < NMOVES; i++)
+ inverse[i] = i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3);
+
+}
+
diff --git a/old/2021-05-26-before-restyle/moves.h b/old/2021-05-26-before-restyle/moves.h
@@ -0,0 +1,51 @@
+#ifndef MOVES_H
+#define MOVES_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "utils.h"
+
+#define NMOVES (z3+1)
+
+typedef enum {
+ NULLMOVE,
+ U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3,
+ Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3, S, S2, S3, E, E2, E3,
+ x, x2, x3, y, y2, y3, z, z2, z3,
+} Move;
+
+/* An alg is an array of "NissMoves", which can be on normal or on inverse. */
+typedef struct { bool inverse; Move m; } NissMove;
+
+/* Movesets */
+extern bool standard_moveset[NMOVES];
+
+extern bool commute[NMOVES][NMOVES];
+extern bool possible_next[NMOVES][NMOVES][NMOVES];
+extern Move inverse[NMOVES];
+extern NissMove rotation_algs[24][3]; /* Same order as transformations */
+
+int len(NissMove *alg);
+int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */
+int invert_alg(NissMove *src, NissMove *dest);
+int concat(NissMove *src1, NissMove *src2, NissMove *dest);
+void print_alg(NissMove *alg);
+int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */
+void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */
+
+bool is_solved_up_to_reorient(Cube cube);
+Cube move_cube(Move m, Cube cube);
+Cube apply_alg(NissMove *alg, Cube cube);
+Cube apply_alg_filtered(NissMove *alg, Cube cube, PieceFilter f);
+
+/* Merge the following two?
+ always in this order */
+void init_ttables(bool read, bool write);
+void init_aux_tables();
+
+
+#endif
diff --git a/old/2021-05-26-before-restyle/solve.c b/old/2021-05-26-before-restyle/solve.c
@@ -0,0 +1,180 @@
+#include "solve.h"
+
+/* Data for creating a pruning table:
+ - compressed: if set to true, each entry occupies only 4 bits, but values
+ larger than 15 cannot be stored.
+ - available[] is the list of availabel moves, as above.
+ - *ptable is the actual table to fill.
+ - n is the number of states (size of ptable).
+ - index must "linearize" the cube, i.e. return its index in ptable.
+ - fname is the name of the file where to store the table */
+typedef struct {
+ bool compressed, *available;
+ int max_moves;
+ uint8_t *ptable;
+ uint64_t n;
+ uint64_t (*index)(Cube);
+ char *fname;
+} PruneData;
+
+/* TODO: comment this */
+typedef struct {
+ bool niss;
+ int m, d;
+ uint64_t *n;
+ Move last1, last2;
+} DfsData;
+
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd);
+void init_ptable(PruneData *pd, bool read, bool write);
+
+/* Search solutions of lenght exactly d */
+void solve_dfs(Cube cube, SolveData *sd, DfsData dd) {
+ if (*dd.n >= sd->max_solutions ||
+ ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d))
+ return;
+
+ (sd->solutions[*dd.n][dd.m]).inverse = dd.niss;
+ (sd->solutions[*dd.n][dd.m]).m = NULLMOVE;
+
+ if (!sd->f(cube)) { /* Solved */
+ if (dd.m == dd.d) {
+ (*dd.n)++;
+ if (*dd.n < sd->max_solutions)
+ copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]);
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) {
+ Move move = sd->sorted_moves[i];
+ if (possible_next[dd.last2][dd.last1][move]) {
+ sd->solutions[*dd.n][dd.m].inverse = dd.niss;
+ sd->solutions[*dd.n][dd.m].m = move;
+ DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = move, .last2 = dd.last1 };
+ solve_dfs(move_cube(move, cube), sd, nn);
+ }
+ }
+
+ if (sd->can_niss && !dd.niss &&
+ (!dd.m || (dd.m && sd->f(move_cube(dd.last1, (Cube){0}))))) {
+ DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n };
+ solve_dfs(inverse_cube(cube), sd, nn);
+ }
+}
+
+/* Iterative deepening depth-first search: for i running from the minimum
+ to the maximum number of moves allowed, looks for solutions of length i. */
+int solve(Cube cube, SolveData *sd) {
+ if (sd->precondition != NULL && !sd->precondition(cube))
+ return -1;
+
+ /* If not given, generate sorted list of moves */
+ if (sd->sorted_moves[0] == NULLMOVE) {
+ int a[NMOVES], b[NMOVES], ia = 0, ib = 0;
+ for (int i = 0; i < NMOVES; i++) {
+ if (sd->available[i]) {
+ if (sd->f(move_cube(i, (Cube){0})))
+ a[ia++] = i;
+ else
+ b[ib++] = i;
+ }
+ }
+ intarrcopy(a, (int *)sd->sorted_moves, ia);
+ intarrcopy(b, (int *)sd->sorted_moves+ia, ib);
+ sd->sorted_moves[ia+ib] = NULLMOVE;
+ }
+
+ sd->max_solutions = min(sd->max_solutions, MAXS);
+ /*TODO
+ Cube rotated = apply_alg(sd->pre_rotation, (Cube){0});
+ cube = apply_alg(inverse_cube(rotated), compose(cube, rotated));
+ */
+
+ uint64_t ret = 0;
+ for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) {
+ DfsData dd = { .d = i, .n = &ret };
+ solve_dfs(cube, sd, dd);
+ }
+
+ /* TODO: transform solutions with inverse of pre_rotation */
+ /*
+ for (uint64_t i = 0; i < ret; i++) {
+ if (sd->cleanup)
+ cleanup(sd->solutions[i], sd->max_moves*3);
+ }*/
+
+ return ret;
+}
+
+void prune_dfs(Cube cube, PruneData *pd, DfsData dd) {
+ uint64_t ind = pd->index(cube);
+ if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m)
+ return;
+ if (dd.m == dd.d) {
+ if (ind && !pd->ptable[ind]) {
+ pd->ptable[ind] = dd.m;
+ (*dd.n)++;
+ }
+ return;
+ }
+
+ for (int i = 0; i < NMOVES; i++) {
+ if (dd.m<20)
+ if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) {
+ DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n,
+ .last1 = i, .last2 = dd.last1 };
+ prune_dfs(move_cube(i, cube), pd, nn);
+ }
+ }
+}
+
+void init_ptable(PruneData *pd, bool read, bool write) {
+ if (read) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "rb")) != NULL) {
+ uint64_t r = fread(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ if (r == pd->n) return;
+ }
+ }
+
+ /* TODO: for now it behaves always as if copressed = false */
+ for (uint64_t i = 0; i < pd->n; i++)
+ pd->ptable[i] = 0;
+
+ uint64_t s = 1;
+ for (int i = 1; i < pd->max_moves && s < pd->n; i++) {
+ DfsData dd = { .d = i, .n = &s };
+ prune_dfs((Cube){0}, pd, dd);
+ }
+
+ if (write) {
+ FILE *ptf;
+ if ((ptf = fopen(pd->fname, "wb")) != NULL) {
+ fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf);
+ fclose(ptf);
+ return;
+ }
+ }
+}
+
+/* Solving steps (and indexing functions) */
+
+uint64_t index_eofb(Cube cube) { return cube.eofb; }
+int f_eofb(Cube cube) {
+ static bool initialized_ptable;
+ static uint8_t pt_eofb[pow2to11];
+ if (!initialized_ptable) {
+ PruneData pd = {
+ .compressed = false, .available = standard_moveset, .max_moves = 13,
+ .ptable = pt_eofb, .n = pow2to11, .index = index_eofb,
+ .fname = "ptable_eofb"
+ };
+ init_ptable(&pd, false, true);
+ initialized_ptable = true;
+ }
+ return cube.eofb ? pt_eofb[cube.eofb] : 0;
+}
+
diff --git a/old/2021-05-26-before-restyle/solve.h b/old/2021-05-26-before-restyle/solve.h
@@ -0,0 +1,59 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include <stdlib.h>
+#include "cube.h"
+#include "moves.h"
+#include "transformations.h"
+
+/* Maximum number of moves per solution and of solutions */
+#define MAXM 30
+#define MAXS 999
+
+/* Data for solving a step:
+ - can_niss is true niss can be used, false otherwise.
+ - optimal_only if true, dynamically updates max_moves so non-optimal
+ solutions are discarded.
+ - cleanup determines whether the cleaunup() function should be used on
+ the found solutions before returning.
+ - available[m] is true if the move m can be used, false otherwise.
+ (Rename to moveset[]?)
+ - min_moves and max_moves are the minimum and maximum number of moves that
+ can be used.
+ - max_solution is the maximum number of solutions that can be returned.
+ - precondition can be used to check wheter the step can actually be applied
+ to the cube. If it returns false, solve() stops immediately returning -1.
+ - f must return 0 if and only if the step is solve, otherwise it must return
+ a lower bound for the number of moves required (without niss).
+ - sorted_moves[] can be used to specify in which order moves are tried
+ by the solving algorithm (for example if one wants to always try F' before
+ F). If sorted_moves[0] == NULLMOVE, the list is generated automatically.
+ It is advised to list first all the moves that actually influence the
+ solved state of the step (this is the default choice). This is in order to
+ avoid cases like B2 F for EO and to NISS only when it makes sense.
+ - start_moves [Currently unused, REMOVE]
+ are the moves that will be used as first moves of all
+ solutions. For example giving R' U' F (F' U R) will generate FMC scrambles
+ and y (y) will solve the step on another axis.
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wrt a different orientation
+ - pre_rotation are the rotations to apply before the scamble to solve
+ the step wth respect to a different orientation.
+ TODO: cange name
+ - solutions[][] is the array where to store the found solutions. */
+typedef struct {
+ bool can_niss, optimal_only, cleanup, *available;
+ int min_moves, max_moves, max_solutions;
+ int (*f)(Cube);
+ bool (*precondition)(Cube);
+ Move sorted_moves[NMOVES];
+ Transformation pre_rotation;
+ NissMove solutions[MAXS][MAXM];
+} SolveData;
+
+int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */
+
+/* Steps */
+int f_eofb(Cube cube);
+
+#endif
diff --git a/old/2021-05-26-before-restyle/transformations.c b/old/2021-05-26-before-restyle/transformations.c
@@ -0,0 +1,200 @@
+#include "transformations.h"
+
+int edge_slice(int e); /* Return slice (e=0, s=1, m=2) to which e belongs */
+Cube rotate_via_compose(Transformation r, Cube c, PieceFilter f);
+bool read_rtables_file();
+bool write_rtables_file();
+
+/* Values mod 3 to determine from which side to take the state to convert */
+int epose_source[NTRANS]; /* 0 = epose, 1 = eposs, 2 = eposm */
+int eposs_source[NTRANS];
+int eposm_source[NTRANS];
+int eofb_source[NTRANS]; /* 0 = eoud, 1 = eorl, 2 = eofb */
+int eorl_source[NTRANS];
+int eoud_source[NTRANS];
+int coud_source[NTRANS]; /* 0 = coud, 1 = corl, 2 = cofb */
+int cofb_source[NTRANS];
+int corl_source[NTRANS];
+
+/* Transition tables for rotations (n+1 is mirror) */
+uint16_t epose_rtable[NTRANS][factorial12/factorial8];
+uint16_t eposs_rtable[NTRANS][factorial12/factorial8];
+uint16_t eposm_rtable[NTRANS][factorial12/factorial8];
+uint16_t eo_rtable[NTRANS][pow2to11];
+uint16_t cp_rtable[NTRANS][factorial8];
+uint16_t co_rtable[NTRANS][pow3to7];
+uint16_t cpos_rtable[NTRANS][factorial6];
+
+/* Same for moves */
+uint16_t moves_rtable[NTRANS][NMOVES];
+
+NissMove rotation_niss[NTRANS][6];
+
+int edge_slice(int e) {
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+ return 2;
+}
+
+Cube rotate_via_compose(Transformation r, Cube c, PieceFilter f) {
+ if (r != mirror) {
+ return apply_alg_filtered(rotation_niss[r], c, f);
+ } else {
+ static int zero12[12] = {0,0,0,0,0,0,0,0,0,0,0,0},
+ zero8[12] = {0,0,0,0,0,0,0,0},
+ mirror_ep[12] = {UF,UR,UB,UL,DF,DR,DB,DL,FL,FR,BR,BL},
+ mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL},
+ mirror_cpos[6] =
+ {U_center,D_center,L_center,R_center,F_center,B_center};
+ return move_via_arrays((CubeArray){
+ .ep = mirror_ep, .eofb = zero12, .eorl = zero12, .eoud = zero12,
+ .cp = mirror_cp, .coud = zero8, .corl = zero8, .cofb = zero8,
+ .cpos = mirror_cpos}, c, f);
+ }
+}
+
+bool read_rtables_file() {
+ FILE *ttf;
+ long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES };
+ if ((ttf = fopen("rtables", "rb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fread(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fread(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fread(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fread(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fread(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fread(cpos_rtable[m], sizeof(uint16_t), me[10], ttf) == me[10];
+ r = r && fread(moves_rtable[m], sizeof(uint16_t), me[11], ttf) == me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+bool write_rtables_file() {
+ FILE *ttf;
+ long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8,
+ factorial12/factorial8, pow2to11, pow2to11, pow2to11,
+ factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES };
+ if ((ttf = fopen("rtables", "wb")) != NULL) {
+ bool r = true;
+ for (int m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0];
+ r = r && fwrite(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1];
+ r = r && fwrite(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2];
+ r = r && fwrite(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3];
+ r = r && fwrite(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6];
+ r = r && fwrite(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7];
+ r = r && fwrite(cpos_rtable[m], sizeof(uint16_t), me[10],ttf) == me[10];
+ r = r && fwrite(moves_rtable[m], sizeof(uint16_t), me[11],ttf) == me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else return false;
+}
+
+void init_transformations(bool read, bool write) {
+ /* Compute sources */
+ for (int i = 0; i < NTRANS; i++) {
+ Cube cube = {0};
+ if (i != mirror)
+ cube = apply_alg(rotation_algs[i], (Cube){0});
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ /*TODO: maybe move down*/
+ /* Compute rotation_niss array, necessary for rotate_via_compose */
+ for (int r = 0; r != mirror; r++) {
+ concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]);
+ for (int i = len(rotation_algs[r]); rotation_niss[r][i].m != NULLMOVE; i++)
+ rotation_niss[r][i].inverse = true;
+ }
+
+ /* If I can read tables from file, I stop here */
+ if (read)
+ if (read_rtables_file())
+ return;
+
+ /* Initialize tables */
+ for (int m = 0; m < NTRANS; m++) {
+ int eparr[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, cparr[8] = {0,0,0,0,0,0,0,0};
+ CubeArray epcp = { .ep = eparr, .cp = cparr };
+ cube_to_arrays(apply_alg(rotation_algs[m], (Cube){0}), &epcp,
+ (PieceFilter){.epose=true,.eposs=true,.eposm=true,.cp=true});
+ for (uint16_t i = 0; i < factorial12/factorial8; i++) {
+ Cube c[3] = { admissible_ep((Cube){ .epose = i}, pf_e),
+ admissible_ep((Cube){ .eposs = i}, pf_s),
+ admissible_ep((Cube){ .eposm = i}, pf_m) };
+ epose_rtable[m][i]=rotate_via_compose(m,c[epose_source[m]],pf_ep).epose;
+ eposs_rtable[m][i]=rotate_via_compose(m,c[eposs_source[m]],pf_ep).eposs;
+ eposm_rtable[m][i]=rotate_via_compose(m,c[eposm_source[m]],pf_ep).eposm;
+ }
+ for (uint16_t i = 0; i < pow2to11; i++ ) {
+ int eoarr[12];
+ int_to_sum_zero_array(i, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_rtable[m][i] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (uint16_t i = 0; i < pow3to7; i++) {
+ int coarr[12];
+ int_to_sum_zero_array(i, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_rtable[m][i] = digit_array_to_int(coarr, 7, 3);
+ }
+ for (uint16_t i = 0; i < factorial8; i++)
+ cp_rtable[m][i] = rotate_via_compose(m, (Cube){.cp=i}, pf_cp).cp;
+ for (uint16_t i = 0; i < factorial6; i++)
+ cpos_rtable[m][i] = rotate_via_compose(m, (Cube){.cpos=i}, pf_cpos).cpos;
+ for (Move i = 0; i < NMOVES; i++) {
+ Cube aux = transform_cube(m, move_cube(i, (Cube){0}));
+ for (Move move = 0; move < NMOVES; move++)
+ if (is_solved(move_cube(inverse[move], aux)))
+ moves_rtable[m][i] = move;
+ }
+ }
+
+ if (write)
+ if (!write_rtables_file())
+ printf("Error in writing rtables: file not writable\n");
+}
+
+Cube transform_cube(Transformation t, Cube cube) {
+ Cube transformed = {0};
+
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm },
+ aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb },
+ aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]];
+ transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]];
+ transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]];
+ transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]];
+ transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]];
+ transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]];
+ transformed.coud = co_rtable[t][aux_co[coud_source[t]]];
+ transformed.corl = co_rtable[t][aux_co[corl_source[t]]];
+ transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]];
+ transformed.cp = cp_rtable[t][cube.cp];
+ transformed.cpos = cpos_rtable[t][cube.cpos];
+
+ return transformed;
+}
+
+void transform_alg(Transformation t, NissMove *alg) {
+ for (int i = 0; alg[i].m; i++)
+ alg[i].m = moves_rtable[t][alg[i].m];
+}
diff --git a/old/2021-05-26-before-restyle/transformations.h b/old/2021-05-26-before-restyle/transformations.h
@@ -0,0 +1,34 @@
+#ifndef TRANSFORMATIONS_H
+#define TRANSFORMATIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cube.h"
+#include "moves.h"
+#include "utils.h"
+
+#define NTRANS (mirror+1)
+
+/* Letters indicate top and front centers
+ * Mirror is wrt rl
+ * Lowercase letter to distinguish from pieces */
+
+typedef enum {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror,
+} Transformation;
+
+void print_transformation(Transformation t);
+
+Cube transform_cube(Transformation t, Cube cube);
+void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */
+
+void init_transformations(bool read, bool write);
+
+#endif
diff --git a/old/2021-05-26-before-restyle/utils.c b/old/2021-05-26-before-restyle/utils.c
@@ -0,0 +1,197 @@
+#include "utils.h"
+
+void swap(int *a, int *b) {
+ int aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+void intarrcopy(int *src, int *dst, int n) {
+ for (int i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+int sum(int *a, int n) {
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ ret += a[i];
+ return ret;
+}
+
+bool is_perm(int *a, int n) {
+ int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0;
+ for (int i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+ for (int i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+ return true;
+}
+
+bool is_subset(int *a, int n, int k) {
+ int sum = 0;
+ for (int i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+ return sum == k;
+}
+
+int powint(int a, int b) {
+ return 0;
+ if (b == 0 || a == 1)
+ return 1;
+ if (a == 0)
+ return 0;
+ if (b < 0)
+ return 0; /* Immediate truncate (integer part is 0) */
+ if (b % 2) {
+ return a * powint(a, b-1);
+ } else {
+ int x = powint(a, b/2);
+ return x*x;
+ }
+}
+
+int factorial(int n) {
+ if (n < 0)
+ return 0;
+ int ret = 1;
+ for (int i = 1; i <= n; i++)
+ ret *= i;
+ return ret;
+}
+
+int binomial(int n, int k) {
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+void int_to_digit_array(int a, int b, int n, int *r) {
+ if (b <= 1)
+ for (int i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (int i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+int digit_array_to_int(int *a, int n, int b) {
+ int ret = 0, p = 1;
+ for (int i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+ return ret;
+}
+
+int perm_to_index(int *a, int n) {
+ if (!is_perm(a, n))
+ return factorial(n); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ for (int j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+ return ret;
+}
+
+void index_to_perm(int p, int n, int *r) {
+ if (p < 0 || p >= factorial(n)) /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */
+ for (int i = 0; i < n; i++) {
+ int c = 0, j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+}
+
+int perm_sign(int *a, int n) {
+ if (!is_perm(a,n))
+ return false;
+ int ret = 0;
+ for (int i = 0; i < n; i++)
+ for (int j = i+1; j < n; j++)
+ ret += (a[i]>a[j]) ? 1 : 0;
+ return ret % 2;
+}
+
+int subset_to_index(int *a, int n, int k) {
+ /* TODO: better checks */
+ if (!is_subset(a, n, k))
+ return binomial(n, k); /* Error */
+ int ret = 0;
+ for (int i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+ return ret;
+}
+
+void index_to_subset(int s, int n, int k, int *r) {
+ if (s < 0 || s >= binomial(n, k)) { /* Error */
+ for (int i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (int j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+ if (k == 0) {
+ for (int j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+ /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/
+ int v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void int_to_sum_zero_array(int x, int b, int n, int *a) {
+ if (b <= 1) {
+ for (int i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ int s = 0;
+ for (int i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+void apply_permutation(int *perm, int *set, int n) {
+ if (!is_perm(perm, n))
+ return;
+ int aux[n];
+ for (int i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+ intarrcopy(aux, set, n);
+}
+
+void sum_arrays_mod(int *a, int *b, int n, int m) {
+ for (int i = 0; i < n; i++)
+ b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m;
+}
diff --git a/old/2021-05-26-before-restyle/utils.h b/old/2021-05-26-before-restyle/utils.h
@@ -0,0 +1,70 @@
+/* General utility functions */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Some useful constants */
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+/* Generic utility functions */
+void swap(int *a, int *b);
+void intarrcopy(int *src, int *dst, int n);
+int sum(int *a, int n);
+bool is_perm(int *a, int n);
+bool is_perm(int *a, int n);
+
+
+/* Standard mathematical functions */
+int powint(int a, int b);
+int factorial(int n);
+int binomial(int n, int k);
+
+/* Converts the integer a to its representation in base b (first n digits
+ * only) and saves the result in r. */
+void int_to_digit_array(int a, int b, int n, int *r);
+int digit_array_to_int(int *a, int n, int b);
+
+/* Converts the first n-1 digits of a number to an array a of digits in base b;
+ * then adds one element to the array, so that the sum of the elements of a is
+ * zero modulo b.
+ * This is used for determing the edge orientation from an 11-bits integer or
+ * the corner orientation from a 7-trits integer. */
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+
+/* Converts a permutation on [0..(n-1)] into the integer i which is the index
+ * of the permutation in the sorted list of all n! such permutations. */
+int perm_to_index(int *a, int n);
+void index_to_perm(int p, int n, int *r);
+
+/* Determine the sign of a permutation */
+int perm_sign(int a[], int n);
+
+/* Converts a k-element subset of a set from an array of n elements, of which k
+ * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */
+int subset_to_index(int *a, int n, int k);
+void index_to_subset(int s, int n, int k, int *r);
+
+int ordered_subset_to_index(int *a, int n, int k);
+void index_to_ordered_subset(int s, int n, int k, int *r);
+
+void apply_permutation(int *perm, int *set, int n);
+
+/* b[i] = (a[i]+b[i])%m for i=1,...,n */
+void sum_arrays_mod(int *a, int *b, int n, int m);
+
+#endif
diff --git a/old/2021-06-02-cleanedup/cube.c b/old/2021-06-02-cleanedup/cube.c
@@ -0,0 +1,2010 @@
+#include "cube.h"
+
+/* Constants and macros *****************************************************/
+
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+#define BASEALGLEN 50
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Local types ***************************************************************/
+
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+
+struct
+cubearray
+{
+ int *ep;
+ int *eofb;
+ int *eorl;
+ int *eoud;
+ int *cp;
+ int *coud;
+ int *corl;
+ int *cofb;
+ int *cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList *sols;
+ Alg current_alg;
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static CubeArray * alloc_cubearray(CubeArray *arr, PieceFilter f);
+static void append_alg(AlgList *l, Alg alg);
+static void append_move(Alg alg, NissMove m);
+static Cube apply_alg_filtered(Alg alg, Cube cube, PieceFilter f);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static Cube arrays_to_cube(CubeArray arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static uint16_t epos_from_arrays(int *epos, int *ep);
+static int factorial(int n);
+static void free_alglist(AlgList *l);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_moves();
+static void init_trans();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static void intarrcopy(int *src, int *dst, int n);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf);
+static Move * moveset_to_movelist(bool *moveset, Step step);
+static AlgList * new_alglist();
+static int perm_sign(int a[], int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static bool read_rtables_file();
+static bool read_ttables_file();
+static bool realloc_alg(Alg alg, int n);
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static void solve_dfs(Cube c, Step s, SolveOptions opts, DfsData dd);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_rtables_file();
+static bool write_ttables_file();
+
+/* Aux tables ****************************************************************/
+
+bool commute[NMOVES][NMOVES];
+bool possible_next[NMOVES][NMOVES][NMOVES];
+Move inverse_move_aux[NMOVES];
+Trans inverse_trans_aux[NTRANS];
+
+/* Some utility constants ****************************************************/
+
+static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static int epe_solved[4] = { FR, FL, BL, BR };
+static int eps_solved[4] = { UL, UR, DL, DR };
+static int epm_solved[4] = { UF, UB, DF, DB };
+
+static int ep_mirror[12] = { UF, UR, UB, UL, DF, DR, DB, DL, FL, FR, BR, BL };
+static int cp_mirror[8] = { UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL };
+static int cpos_mirror[6] = {
+ U_center, D_center,
+ L_center, R_center,
+ F_center, B_center
+};
+
+static long unsigned int me[12] = {
+ factorial12/factorial8,
+ factorial12/factorial8,
+ factorial12/factorial8,
+ pow2to11,
+ pow2to11,
+ pow2to11,
+ factorial8,
+ pow3to7,
+ pow3to7,
+ pow3to7,
+ factorial6,
+ NMOVES
+};
+
+static PieceFilter pf_all = {
+ true, true, true, true, true, true, true, true, true, true, true
+};
+static PieceFilter pf_epcp = {
+ .epose = true, .eposs = true, .eposm = true, .cp = true
+};
+static PieceFilter pf_cpos = { .cpos = true };
+static PieceFilter pf_cp = { .cp = true };
+static PieceFilter pf_ep = { .epose = true, .eposs = true, .eposm = true };
+static PieceFilter pf_e = { .epose = true };
+static PieceFilter pf_s = { .eposs = true };
+static PieceFilter pf_m = { .eposm = true };
+static PieceFilter pf_eo = { .eofb = true, .eorl = true, .eoud = true };
+static PieceFilter pf_co = { .coud = true, .cofb = true, .corl = true };
+
+/* Strings for moves and pieces **********************************************/
+
+char move_string[NMOVES][5] = {
+ "-",
+ "U", "U2", "U\'", "D", "D2", "D\'",
+ "R", "R2", "R\'", "L", "L2", "L\'",
+ "F", "F2", "F\'", "B", "B2", "B\'",
+ "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'",
+ "Rw", "Rw2", "Rw\'", "Lw", "Lw2", "Lw\'",
+ "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'",
+ "M", "M2", "M\'",
+ "S", "S2", "S\'",
+ "E", "E2", "E\'",
+ "x", "x2", "x\'",
+ "y", "y2", "y\'",
+ "z", "z2", "z\'"
+};
+char edge_string[12][5] = {
+ "UF", "UL", "UB", "UR",
+ "DF", "DL", "DB", "DR",
+ "FR", "FL", "BL", "BR"
+};
+char corner_string[8][5] = {
+ "UFR", "UFL", "UBL", "UBR",
+ "DFR", "DFL", "DBL", "DBR"
+};
+char center_string[6][5] = {
+ "U", "D",
+ "R", "L",
+ "F", "B"
+};
+
+/* Transition tables for transformations and moves ***************************/
+
+uint16_t epose_rtable[NTRANS][factorial12/factorial8];
+uint16_t eposs_rtable[NTRANS][factorial12/factorial8];
+uint16_t eposm_rtable[NTRANS][factorial12/factorial8];
+uint16_t eo_rtable[NTRANS][pow2to11];
+uint16_t cp_rtable[NTRANS][factorial8];
+uint16_t co_rtable[NTRANS][pow3to7];
+uint16_t cpos_rtable[NTRANS][factorial6];
+Move moves_rtable[NTRANS][NMOVES];
+
+uint16_t epose_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposs_ttable[NMOVES][factorial12/factorial8];
+uint16_t eposm_ttable[NMOVES][factorial12/factorial8];
+uint16_t eofb_ttable[NMOVES][pow2to11];
+uint16_t eorl_ttable[NMOVES][pow2to11];
+uint16_t eoud_ttable[NMOVES][pow2to11];
+uint16_t cp_ttable[NMOVES][factorial8];
+uint16_t coud_ttable[NMOVES][pow3to7];
+uint16_t cofb_ttable[NMOVES][pow3to7];
+uint16_t corl_ttable[NMOVES][pow3to7];
+uint16_t cpos_ttable[NMOVES][factorial6];
+
+/* Used to compute the full transition tables for moves and transformations **/
+
+int epose_source[NTRANS]; /* 0 = epose, 1 = eposs, 2 = eposm */
+int eposs_source[NTRANS];
+int eposm_source[NTRANS];
+int eofb_source[NTRANS]; /* 0 = eoud, 1 = eorl, 2 = eofb */
+int eorl_source[NTRANS];
+int eoud_source[NTRANS];
+int coud_source[NTRANS]; /* 0 = coud, 1 = corl, 2 = cofb */
+int cofb_source[NTRANS];
+int corl_source[NTRANS];
+
+NissMove rotation_niss[NTRANS][6];
+NissMove rotation_algs[NROTATIONS][3] = {
+ { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = z }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x }, { .m = y3 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } },
+ { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } },
+};
+
+/* For each type of pieces only the effects of U, x and y are described */
+int edge_cycle[NMOVES][12] = {
+ [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR },
+ [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR },
+ [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL }
+};
+int eofb_flipped[NMOVES][12] = {
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
+int eorl_flipped[NMOVES][12] = {
+ [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
+int eoud_flipped[NMOVES][12] = {
+ [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+};
+int corner_cycle[NMOVES][8] = {
+ [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR},
+ [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR},
+ [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL}
+};
+int coud_flipped[NMOVES][8] = {
+ [x] = {
+ [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2,
+ [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2
+ }
+};
+int corl_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ }
+};
+int cofb_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ },
+ [y] = {
+ [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1,
+ [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2
+ }
+};
+int center_cycle[NMOVES][6] = {
+ [x] = { F_center, B_center, R_center, L_center, D_center, U_center },
+ [y] = { U_center, D_center, B_center, F_center, R_center, L_center }
+};
+
+/* Each move is reduced to a combination of U, x and y using this table */
+Move equiv_moves[NMOVES][14] = {
+ [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 },
+ [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 },
+ [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 },
+ [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 },
+ [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 },
+ [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 },
+ [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 },
+ [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 },
+
+ [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 },
+ [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 },
+ [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 },
+ [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 },
+ [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 },
+ [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 },
+ [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 },
+ [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 },
+ [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 },
+ [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 },
+ [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+ [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 },
+
+ [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 },
+ [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 },
+ [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 },
+ [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 },
+ [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 },
+ [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 },
+ [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 },
+ [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 },
+ [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 },
+
+ [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray arr;
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ alloc_cubearray(&arr, f);
+ cube_to_arrays(cube, &arr, f);
+
+ for (i = 0; i < 12; i++)
+ if (arr.ep[i] != -1)
+ used[arr.ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr.ep[i] == -1)
+ arr.ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(&arr, f);
+
+ return ret;
+}
+
+static CubeArray *
+alloc_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ return arr;
+}
+
+static void
+append_alg(AlgList *l, Alg alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+
+ node->alg = new_alg("");
+ copy_alg(alg, node->alg);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg alg, NissMove m)
+{
+ int n = len(alg);
+
+ if (!realloc_alg(alg, n))
+ return;
+
+ alg[n] = m;
+ alg[n+1] = (NissMove) { .m = NULLMOVE };
+}
+
+static Cube
+apply_alg_filtered(Alg alg, Cube cube, PieceFilter f)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; alg[i].m != NULLMOVE; i++)
+ if (alg[i].inverse)
+ ret = apply_move(alg[i].m, ret);
+
+ ret = compose_filtered(cube, inverse_cube(ret), f);
+
+ for (i = 0; alg[i].m != NULLMOVE; i++)
+ if (!alg[i].inverse)
+ ret = apply_move(alg[i].m, ret);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ intarrcopy(aux, set, n);
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(m_arr, cube, f);
+}
+
+static Cube
+arrays_to_cube(CubeArray arr, PieceFilter f)
+{
+ Cube ret = {0};
+ int epose[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposs[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int eposm[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int epe[4], eps[4], epm[4];
+ int i, ie, is, im, j;
+
+ /* Again, ep is the hardest part */
+ if (f.epose) {
+ for (i = 0, ie = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (arr.ep[i] == epe_solved[j]) {
+ epe[ie++] = j;
+ epose[i] = 1;
+ }
+ }
+ }
+ ret.epose = epos_from_arrays(epose, epe);
+ }
+
+ if (f.eposs) {
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (arr.ep[i] == eps_solved[j]) {
+ eps[is++] = j;
+ eposs[i] = 1;
+ }
+ }
+ }
+ for (i = 0; i < 4; i++)
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ ret.eposs = epos_from_arrays(eposs, eps);
+ }
+
+ if (f.eposm) {
+ for (i = 0, im = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (arr.ep[i] == epm_solved[j]) {
+ epm[im++] = j;
+ eposm[i] = 1;
+ }
+ }
+ }
+ for (i = 0; i < 4; i++)
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ ret.eposm = epos_from_arrays(eposm, epm);
+ }
+
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr.eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr.eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr.eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr.cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr.coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr.corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr.cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr.cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray arr;
+ Cube ret;
+
+ alloc_cubearray(&arr, f);
+ cube_to_arrays(c2, &arr, f);
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(&arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int epose[12], eposs[12], eposm[12];
+ int epe[4], eps[4], epm[4];
+ int i, ie, is, im;
+
+ /* ep is the hardest */
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose) {
+ index_to_perm(cube.epose % factorial(4), 4, epe);
+ index_to_subset(cube.epose / factorial(4), 12, 4, epose);
+ for (i = 0, ie = 0; i < 12; i++)
+ if (epose[i])
+ arr->ep[i] = epe_solved[epe[ie++]];
+ }
+
+ if (f.eposs) {
+ index_to_perm(cube.eposs % factorial(4), 4, eps);
+ index_to_subset(cube.eposs / factorial(4), 12, 4, eposs);
+ for (i = 0; i < 4; i++)
+ swap(&eposs[eps_solved[i]], &eposs[i+8]);
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ arr->ep[i] = eps_solved[eps[is++]];
+ }
+
+ if (f.eposm) {
+ index_to_perm(cube.eposm % factorial(4), 4, epm);
+ index_to_subset(cube.eposm / factorial(4), 12, 4, eposm);
+ for (i = 0; i < 4; i++)
+ swap(&eposm[epm_solved[i]], &eposm[i+8]);
+ for (i = 0, im = 0; i < 12; i++)
+ if (eposm[i])
+ arr->ep[i] = epm_solved[epm[im++]];
+ }
+
+ /* All the others */
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static uint16_t
+epos_from_arrays(int *epos, int *ep)
+{
+ return factorial4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+static void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free(i);
+ i = aux;
+ }
+ free(l);
+
+ return;
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ int i, j, k;
+ bool cij, p1, p2;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2);
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && (j-(j-1)%3) == (k-(k-1)%3);
+ p2 = i && (i-(i-1)%3) == (k-(k-1)%3);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] =
+ i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3);
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i, j;
+ uint16_t ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = (Cube){0};
+ for (j = 0; equiv_moves[i][j]; j++)
+ c = apply_move_cubearray(equiv_moves[i][j], c, pf_all);
+
+ arrs = (CubeArray){
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "ttables not found, generating them.\n");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < factorial12/factorial8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_ttable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_ttable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_ttable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < pow2to11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_ttable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_ttable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_ttable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < pow3to7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_ttable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_ttable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_ttable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < factorial8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_ttable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < factorial6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_ttable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ bool b1, b2, b3;
+ uint16_t ui;
+ Move mi, move;
+ Trans r, m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(rotation_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ /* Compute rotation_niss array, necessary for rotate_via_compose */
+ for (r = 0; r != mirror; r++) {
+ concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]);
+ for (i = len(rotation_algs[r]); rotation_niss[r][i].m; i++)
+ rotation_niss[r][i].inverse = true;
+ }
+
+ if (read_rtables_file())
+ return;
+
+ fprintf(stderr, "rtables not found, generating them.\n");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ intarrcopy(ep_mirror, eparr, 12);
+ intarrcopy(cp_mirror, cparr, 8);
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < factorial12/factorial8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_rtable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_rtable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_rtable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < pow2to11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_rtable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < pow3to7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_rtable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_rtable[m][ui] =
+ invert_digits(co_rtable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < factorial8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_rtable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < factorial6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_rtable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_rtable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_rtable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_rtable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_rtable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_rtable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_rtables_file())
+ fprintf(stderr, "Error writing rtables\n");
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static void
+intarrcopy(int *src, int *dst, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ return digit_array_to_int(r, n, b);
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray arr, Cube c, PieceFilter f)
+{
+ CubeArray arrc;
+ Cube ret;
+
+ alloc_cubearray(&arrc, f);
+ cube_to_arrays(c, &arrc, f);
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr.ep, arrc.ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr.ep, arrc.eofb, 12);
+ sum_arrays_mod(arr.eofb, arrc.eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr.ep, arrc.eorl, 12);
+ sum_arrays_mod(arr.eorl, arrc.eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr.ep, arrc.eoud, 12);
+ sum_arrays_mod(arr.eoud, arrc.eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr.cp, arrc.cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr.cp, arrc.coud, 8);
+ sum_arrays_mod(arr.coud, arrc.coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr.cp, arrc.corl, 8);
+ sum_arrays_mod(arr.corl, arrc.corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr.cp, arrc.cofb, 8);
+ sum_arrays_mod(arr.cofb, arrc.cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr.cpos, arrc.cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(&arrc, f);
+
+ return ret;
+}
+
+static Move *
+moveset_to_movelist(bool *moveset, Step step)
+{
+ Cube c;
+ int *a = malloc(NMOVES * sizeof(int)), b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (moveset != NULL) {
+ for (i = NULLMOVE+1; i < NMOVES; i++) {
+ if (moveset[i]) {
+ c = apply_move(i, (Cube){0});
+ if (step.f != NULL && step.f(c)) {
+ a[na++] = i;
+ } else {
+ b[nb++] = i;
+ }
+ }
+ }
+ }
+
+ intarrcopy(b, a+na, nb);
+ a[na+nb] = NULLMOVE;
+
+ return (Move *)a;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0; /* Truncate */
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static bool
+read_rtables_file()
+{
+ FILE *ttf;
+ int b = sizeof(uint16_t);
+ bool r = true;
+ Move m;
+
+ if ((ttf = fopen("rtables", "rb")) != NULL) {
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_rtable[m],b,me[0],ttf) == me[0];
+ r = r && fread(eposs_rtable[m],b,me[1],ttf) == me[1];
+ r = r && fread(eposm_rtable[m],b,me[2],ttf) == me[2];
+ r = r && fread(eo_rtable[m],b,me[3],ttf) == me[3];
+ r = r && fread(cp_rtable[m],b,me[6],ttf) == me[6];
+ r = r && fread(co_rtable[m],b,me[7],ttf) == me[7];
+ r = r && fread(cpos_rtable[m],b,me[10],ttf) == me[10];
+ r = r && fread(moves_rtable[m],b,me[11],ttf) == me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else {
+ return false;
+ }
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *ttf;
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ if ((ttf = fopen("ttables", "rb")) != NULL) {
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_ttable[m],b,me[0],ttf) == me[0];
+ r = r && fread(eposs_ttable[m],b,me[1],ttf) == me[1];
+ r = r && fread(eposm_ttable[m],b,me[2],ttf) == me[2];
+ r = r && fread(eofb_ttable[m],b,me[3],ttf) == me[3];
+ r = r && fread(eorl_ttable[m],b,me[4],ttf) == me[4];
+ r = r && fread(eoud_ttable[m],b,me[5],ttf) == me[5];
+ r = r && fread(cp_ttable[m],b,me[6],ttf) == me[6];
+ r = r && fread(coud_ttable[m],b,me[7],ttf) == me[7];
+ r = r && fread(corl_ttable[m],b,me[8],ttf) == me[8];
+ r = r && fread(cofb_ttable[m],b,me[9],ttf) == me[9];
+ r = r && fread(cpos_ttable[m],b,me[10],ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else {
+ return false;
+ }
+}
+
+static bool
+realloc_alg(Alg alg, int n)
+{
+ if (n+1 >= BASEALGLEN) {
+ if (reallocarray(alg, n+2, sizeof(NissMove)) == NULL) {
+ fprintf(stderr, "Error reallocating alg\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Cube ret;
+ CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ if (r != mirror) {
+ ret = apply_alg_filtered(rotation_niss[r], c, f);
+ } else {
+ ret = move_via_arrays(ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(ma, ret, f);
+ }
+
+ return ret;
+}
+
+static void
+solve_dfs(Cube c, Step s, SolveOptions opts, DfsData dd)
+{
+ DfsData newdd;
+ Move move;
+ NissMove nm;
+ int i;
+ bool found_many, prune, niss_makes_sense;
+
+ found_many = dd.sols->len >= opts.max_solutions;
+ prune = (!opts.can_niss || dd.niss) && dd.m + s.f(c) > dd.d;
+ if (found_many || prune)
+ return;
+
+ if (!s.f(c)) {
+ if (dd.m == dd.d)
+ append_alg(dd.sols, dd.current_alg);
+ return;
+ }
+
+ for (i = 0; opts.sorted_moves[i] != NULLMOVE; i++) {
+ move = opts.sorted_moves[i];
+ if (possible_next[dd.last2][dd.last1][move]) {
+ nm = (NissMove){ .m = move, .inverse = dd.niss };
+ newdd = (DfsData) {
+ .d = dd.d,
+ .m = dd.m + 1,
+ .niss = dd.niss,
+ .last1 = move,
+ .last2 = dd.last1,
+ .sols = dd.sols,
+ .current_alg = dd.current_alg,
+ };
+
+ append_move(dd.current_alg, nm);
+ solve_dfs(apply_move(move, c), s, opts, newdd);
+ remove_last_moves(dd.current_alg, 1);
+ }
+ }
+
+ niss_makes_sense = !dd.m || (s.f(apply_move(dd.last1, (Cube){0})));
+ if (opts.can_niss && !dd.niss && niss_makes_sense) {
+ newdd = (DfsData) {
+ .d = dd.d,
+ .m = dd.m,
+ .niss = true,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = dd.sols,
+ .current_alg = dd.current_alg,
+ };
+ solve_dfs(inverse_cube(c), s, opts, newdd);
+ }
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_rtables_file()
+{
+ FILE *ttf;
+ bool r = true;
+ int b = sizeof(uint16_t);
+ Move m;
+
+ if ((ttf = fopen("rtables", "wb")) != NULL) {
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_rtable[m],b,me[0], ttf) == me[0];
+ r = r && fwrite(eposs_rtable[m],b,me[1], ttf) == me[1];
+ r = r && fwrite(eposm_rtable[m],b,me[2], ttf) == me[2];
+ r = r && fwrite(eo_rtable[m],b,me[3], ttf) == me[3];
+ r = r && fwrite(cp_rtable[m],b,me[6], ttf) == me[6];
+ r = r && fwrite(co_rtable[m],b,me[7], ttf) == me[7];
+ r = r && fwrite(cpos_rtable[m],b,me[10],ttf) == me[10];
+ r = r && fwrite(moves_rtable[m],b,me[11],ttf)== me[11];
+ }
+ fclose(ttf);
+ return r;
+ } else {
+ return false;
+ }
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *ttf;
+ int m, b = sizeof(uint16_t);;
+ bool r = true;
+
+ if ((ttf = fopen("ttables", "wb")) != NULL) {
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_ttable[m],b,me[0],ttf) == me[0];
+ r = r && fwrite(eposs_ttable[m],b,me[1],ttf) == me[1];
+ r = r && fwrite(eposm_ttable[m],b,me[2],ttf) == me[2];
+ r = r && fwrite(eofb_ttable[m],b,me[3],ttf) == me[3];
+ r = r && fwrite(eorl_ttable[m],b,me[4],ttf) == me[4];
+ r = r && fwrite(eoud_ttable[m],b,me[5],ttf) == me[5];
+ r = r && fwrite(cp_ttable[m],b,me[6],ttf) == me[6];
+ r = r && fwrite(coud_ttable[m],b,me[7],ttf) == me[7];
+ r = r && fwrite(corl_ttable[m],b,me[8],ttf) == me[8];
+ r = r && fwrite(cofb_ttable[m],b,me[9],ttf) == me[9];
+ r = r && fwrite(cpos_ttable[m],b,me[10],ttf) == me[10];
+ }
+ fclose(ttf);
+ return r;
+ } else {
+ return false;
+ }
+}
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg alg, Cube cube)
+{
+ return apply_alg_filtered(alg, cube, pf_all);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ Cube moved = {0};
+
+ moved.epose = epose_ttable[m][cube.epose];
+ moved.eposs = eposs_ttable[m][cube.eposs];
+ moved.eposm = eposm_ttable[m][cube.eposm];
+ moved.eofb = eofb_ttable[m][cube.eofb];
+ moved.eorl = eorl_ttable[m][cube.eorl];
+ moved.eoud = eoud_ttable[m][cube.eoud];
+ moved.coud = coud_ttable[m][cube.coud];
+ moved.cofb = cofb_ttable[m][cube.cofb];
+ moved.corl = corl_ttable[m][cube.corl];
+ moved.cp = cp_ttable[m][cube.cp];
+ moved.cpos = cpos_ttable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ Cube transformed = {0};
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]];
+ transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]];
+ transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]];
+ transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]];
+ transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]];
+ transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]];
+ transformed.coud = co_rtable[t][aux_co[coud_source[t]]];
+ transformed.corl = co_rtable[t][aux_co[corl_source[t]]];
+ transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]];
+ transformed.cp = cp_rtable[t][cube.cp];
+ transformed.cpos = cpos_rtable[t][cube.cpos];
+
+ return transformed;
+}
+
+/* TODO: this has to be changed using pre-computations */
+bool
+block_solved(Cube cube, Block block)
+{
+ CubeArray arr;
+ int i;
+ bool ret = true;
+
+ alloc_cubearray(&arr, pf_all);
+ cube_to_arrays(cube, &arr, pf_all);
+
+ for (i = 0; i < 12; i++)
+ ret = ret && !(block.edge[i] && (arr.ep[i] != i || arr.eofb[i]));
+ for (i = 0; i < 8; i++)
+ ret = ret && !(block.corner[i] && (arr.cp[i] != i || arr.coud[i]));
+ for (i = 0; i < 6; i++)
+ ret = ret && !(block.center[i] && arr.cpos[i] != i);
+
+ free_cubearray(&arr, pf_all);
+
+ return ret;
+}
+
+Center
+center_at(Cube cube, Center c)
+{
+ int ret;
+ CubeArray arr;
+
+ alloc_cubearray(&arr, pf_cpos);
+ cube_to_arrays(cube, &arr, pf_cpos);
+ ret = arr.cpos[c];
+ free_cubearray(&arr, pf_cpos);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+Corner
+corner_at(Cube cube, Corner c)
+{
+ int ret;
+ CubeArray arr;
+
+ alloc_cubearray(&arr, pf_cp);
+ cube_to_arrays(cube, &arr, pf_cp);
+ ret = arr.cp[c];
+ free_cubearray(&arr, pf_cp);
+
+ return ret;
+}
+
+Edge
+edge_at(Cube cube, Edge e)
+{
+ int ret;
+ CubeArray arr;
+
+ alloc_cubearray(&arr, pf_ep);
+ cube_to_arrays(cube, &arr, pf_ep);
+ ret = arr.ep[e];
+ free_cubearray(&arr, pf_ep);
+
+ return ret;
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray arr, inv;
+ Cube ret;
+ int i;
+
+ alloc_cubearray(&arr, pf_all);
+ alloc_cubearray(&inv, pf_all);
+
+ cube_to_arrays(cube, &arr, pf_all);
+
+ for (i = 0; i < 12; i++) {
+ inv.ep[arr.ep[i]] = i;
+ inv.eofb[arr.ep[i]] = arr.eofb[i];
+ inv.eorl[arr.ep[i]] = arr.eorl[i];
+ inv.eoud[arr.ep[i]] = arr.eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv.cp[arr.cp[i]] = i;
+ inv.coud[arr.cp[i]] = (3 - arr.coud[i]) % 3;
+ inv.corl[arr.cp[i]] = (3 - arr.corl[i]) % 3;
+ inv.cofb[arr.cp[i]] = (3 - arr.cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv.cpos[arr.cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(&arr, pf_all);
+ free_cubearray(&inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b;
+ uint16_t x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ CubeArray arr;
+
+ alloc_cubearray(&arr, pf_all);
+ cube_to_arrays(cube, &arr, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr.ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr.eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr.cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr.coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr.cpos[i]]);
+ printf("\n");
+
+ free_cubearray(&arr, pf_all);
+}
+
+void
+concat(Alg src1, Alg src2, Alg dest)
+{
+ int n1 = len(src1), n2 = len(src2);
+
+ if (!realloc_alg(dest, n1+n2))
+ return;
+
+ copy_alg(src1, dest);
+ copy_alg(src2, dest + n1);
+}
+
+void
+copy_alg(Alg src, Alg dest)
+{
+ int i, n = len(src);
+
+ if (!realloc_alg(dest, n))
+ return;
+
+ for (i = 0; src[i].m != NULLMOVE; i++)
+ dest[i] = src[i];
+ dest[i].m = NULLMOVE;
+}
+
+void
+invert_alg(Alg src, Alg dest)
+{
+ Move m;
+ int i, n = len(src);
+
+ if (!realloc_alg(dest, n))
+ return;
+
+ for (i = 0; i < n; i++) {
+ m = inverse_move_aux[src[i].m];
+ dest[n-i-1] = (NissMove) { .m = m, .inverse = src[i].inverse };
+ }
+ dest[n].m = NULLMOVE;
+}
+
+int
+len(Alg alg)
+{
+ int i;
+
+ for (i = 0; alg[i].m != NULLMOVE; i++);
+
+ return i;
+}
+
+Alg
+new_alg(char *str)
+{
+ Alg alg = malloc(BASEALGLEN * sizeof(NissMove));
+ int i;
+ bool niss = false;
+ Move j, m;
+ NissMove nm;
+
+ alg[0] = (NissMove){ .m = NULLMOVE };
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ nm = (NissMove){ .m = m, .inverse = niss };
+ append_move(alg, nm);
+ break;
+ }
+ }
+ }
+ append_move(alg, (NissMove){ .m = NULLMOVE });
+
+ return alg;
+}
+
+void
+print_alg(Alg alg)
+{
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; alg[i].m != NULLMOVE; i++) {
+ if (!niss && alg[i].inverse)
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg[i].inverse)
+ strcpy(fill, ") ");
+ if (niss == alg[i].inverse)
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg[i].m]);
+ niss = alg[i].inverse;
+ }
+ printf("%s\n", niss ? ")" : "");
+}
+
+void
+remove_last_moves(Alg alg, int k)
+{
+ int n = len(alg);
+ int i = max(0, n-k);
+
+ alg[i].m = NULLMOVE;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions opts)
+{
+ AlgList *sols = new_alglist();
+ AlgListNode *node;
+ Cube c;
+ DfsData dd;
+ int i;
+
+ if (step.ready != NULL && !step.ready(cube))
+ return sols;
+
+ if (opts.sorted_moves == NULL)
+ opts.sorted_moves = moveset_to_movelist(opts.moveset, step);
+
+ for (i = opts.min_moves; i <= opts.max_moves; i++) {
+ if (sols->len && opts.optimal_only)
+ break;
+
+ c = apply_trans(opts.pre_trans, cube);
+ dd = (DfsData) {
+ .d = i,
+ .m = 0,
+ .niss = false,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ solve_dfs(c, step, opts, dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[opts.pre_trans], node->alg);
+
+ return sols;
+}
+
+void
+transform_alg(Trans t, Alg alg)
+{
+ int i;
+
+ for (i = 0; alg[i].m != NULLMOVE; i++)
+ alg[i].m = moves_rtable[t][alg[i].m];
+}
+
+
+void
+init()
+{
+ init_moves();
+ init_auxtables();
+ init_trans();
+}
diff --git a/old/2021-06-02-cleanedup/cube.h b/old/2021-06-02-cleanedup/cube.h
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Constants ****************************************************************/
+
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+
+/* Typedefs *****************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct nissmove * Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct cube Cube;
+typedef struct nissmove NissMove;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+/* Type specifications *******************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+struct
+alglist
+{
+ AlgListNode *first;
+ AlgListNode *last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg alg;
+ AlgListNode *next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cube
+{
+ uint16_t epose;
+ uint16_t eposs;
+ uint16_t eposm;
+ uint16_t eofb;
+ uint16_t eorl;
+ uint16_t eoud;
+ uint16_t cp;
+ uint16_t coud;
+ uint16_t cofb;
+ uint16_t corl;
+ uint16_t cpos;
+};
+
+struct
+nissmove
+{
+ Move m;
+ bool inverse;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool *moveset;
+ Move *sorted_moves;
+ Trans pre_trans;
+};
+
+struct
+step
+{
+ int (*f)(Cube);
+ bool (*ready)(Cube);
+};
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Center center_at(Cube cube, Center c);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Corner corner_at(Cube cube, Corner c);
+Edge edge_at(Cube cube, Edge e);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_solved(Cube cube, bool reorient);
+int piece_orientation(Cube cube, int piece, char *orientation);
+void print_cube(Cube cube);
+AlgList * solve(Cube cube, Step step, SolveOptions opts);
+
+void concat(Alg src1, Alg src2, Alg dest);
+void copy_alg(Alg src, Alg dest);
+void invert_alg(Alg src, Alg dest);
+int len(Alg alg);
+Alg new_alg(char *str);
+void print_alg(Alg alg);
+void remove_last_moves(Alg alg, int k);
+void transform_alg(Trans t, Alg alg);
+
+void init();
diff --git a/old/2021-06-02-cleanedup/main.c b/old/2021-06-02-cleanedup/main.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+
+ init();
+
+ algo = new_alg("R U R' F");
+ cube = apply_alg(algo, (Cube){0});
+
+ print_cube(apply_trans(rd, cube));
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 5,
+ .optimal_only = true,
+ .max_solutions = 3,
+ .can_niss = false,
+ .moveset = standard_moveset,
+ .pre_trans = fu
+ };
+ sols = solve(cube, step_eofb, opts);
+ if (sols->len == 0)
+ fprintf(stderr, "No solution found\n");
+ else
+ print_alg(sols->first->alg);
+
+
+ return 0;
+}
diff --git a/old/2021-06-02-cleanedup/steps.h b/old/2021-06-02-cleanedup/steps.h
@@ -0,0 +1,22 @@
+/* Pre-conditions, i.e. "ready(cube)" functions ******************************/
+
+bool always_ready(Cube cube) { return true; }
+
+/* Main functions, i.e. "f(cube)" ********************************************/
+
+int f_eofb(Cube cube) { return (cube.eofb != 0) ? 1 : 0; }
+
+/* Steps *********************************************************************/
+
+Step step_eofb = { .f = f_eofb, .ready = always_ready };
+
+/* Movesets ******************************************************************/
+
+bool standard_moveset[NMOVES] = {
+ [U] = true, [U2] = true, [U3] = true,
+ [D] = true, [D2] = true, [D3] = true,
+ [R] = true, [R2] = true, [R3] = true,
+ [L] = true, [L2] = true, [L3] = true,
+ [F] = true, [F2] = true, [F3] = true,
+ [B] = true, [B2] = true, [B3] = true,
+};
diff --git a/old/2021-06-14-oldalg/cube.c b/old/2021-06-14-oldalg/cube.c
@@ -0,0 +1,2849 @@
+#include "cube.h"
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define BASEALGLEN 50
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+#define PTABLESIZE(n) ((n+1) / 2)
+#define PTABLEVAL(tab,ind) (((ind)%2) ? (tab[(ind)/2] / 16) : \
+ (tab[(ind)/2] % 16))
+
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static uint16_t array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static Move base_move(Move m);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent(int pos1, int pos2);
+static uint16_t epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void generate_ptable(PruneData *pd);
+static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void moveset_to_list(bool (*ms)(Move), int (*f)(Cube), Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_set_reached(PruneData *pd, uint64_t ind);
+static void ptable_update(PruneData *pd, uint64_t ind, int m);
+static bool read_ptable_file(PruneData *pd);
+static bool read_ttables_file();
+static bool read_mtables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_ptable_file(PruneData *pd);
+static bool write_ttables_file();
+static bool write_mtables_file();
+
+static void init_auxtables();
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_steps();
+static void init_strings();
+static void init_trans();
+static void init_trans_aux();
+
+static bool moveset_HTM(Move m);
+static bool moveset_URF(Move m);
+
+/* Steps and related functions and data **************************************/
+
+Step eofb_HTM;
+Step eorl_HTM;
+Step eoud_HTM;
+Step coud_HTM;
+Step corl_HTM;
+Step cofb_HTM;
+Step coud_URF;
+Step corl_URF;
+Step cofb_URF;
+Step corners_HTM;
+Step corners_URF;
+Step edges_HTM;
+Step drud_HTM;
+Step optimal_HTM;
+
+PruneData pd_eofb_HTM;
+PruneData pd_coud_HTM;
+PruneData pd_corners_HTM;
+PruneData pd_ep_HTM;
+PruneData pd_drud_HTM;
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_ep(Cube cube);
+static uint64_t index_drud(Cube cube);
+
+static int check_nothing(Cube cube);
+static int check_eofb_HTM(Cube cube);
+static int check_coud_HTM(Cube cube);
+static int check_coud_URF(Cube cube);
+static int check_corners_HTM(Cube cube);
+static int check_corners_URF(Cube cube);
+static int check_edges_HTM(Cube cube);
+static int check_drud_HTM(Cube cube);
+static int check_optimal_HTM(Cube cube);
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+
+static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eo_ttable[NTRANS][POW2TO11];
+static uint16_t cp_ttable[NTRANS][FACTORIAL8];
+static uint16_t co_ttable[NTRANS][POW3TO7];
+static uint16_t cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eofb_mtable[NMOVES][POW2TO11];
+static uint16_t eorl_mtable[NMOVES][POW2TO11];
+static uint16_t eoud_mtable[NMOVES][POW2TO11];
+static uint16_t cp_mtable[NMOVES][FACTORIAL8];
+static uint16_t coud_mtable[NMOVES][POW3TO7];
+static uint16_t cofb_mtable[NMOVES][POW3TO7];
+static uint16_t corl_mtable[NMOVES][POW3TO7];
+static uint16_t cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * trans_algs[NROTATIONS];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ AlgNode *i;
+
+ node->alg = new_alg("");
+ for (i = alg->first; i != NULL; i = i->next)
+ append_move(node->alg, i->m, i->inverse);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ AlgNode *node = malloc(sizeof(AlgNode));
+
+ node->m = m;
+ node->inverse = inverse;
+ node->next = NULL;
+ node->prev = alg->last;
+
+ if (++alg->len == 1)
+ alg->first = node;
+ else
+ alg->last->next = node;
+ alg->last = node;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ AlgNode *i;
+
+ for (i = alg->first; i != NULL; i = i->next)
+ if (i->inverse)
+ ret = a ? apply_move(i->m, ret) :
+ apply_move_cubearray(i->m, ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = alg->first; i != NULL; i = i->next)
+ if (!i->inverse)
+ ret = a ? apply_move(i->m, ret) :
+ apply_move_cubearray(i->m, ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static uint16_t
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 1)
+ ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0;
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static uint16_t
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(uint16_t epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ AlgNode *aux, *i = alg->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free(i);
+ i = aux;
+ }
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+generate_ptable(PruneData *pd)
+{
+ uint64_t j;
+ DfsData dd;
+
+ if (pd->generated)
+ return;
+
+ pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ dd.m = 0;
+ dd.last1 = NULLMOVE;
+ dd.last2 = NULLMOVE;
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->size; j++)
+ ptable_update(pd, j, 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+
+ pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) {
+ memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t));
+ generate_ptable_dfs((Cube){0}, pd, &dd);
+ fprintf(stderr, "Depth %d completed, generated %lu/%lu\n",
+ dd.d, pd->n, pd->size);
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+static void
+generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ uint64_t ind = pd->index(c);
+ int oldval = PTABLEVAL(pd->ptable, ind);
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size)
+ return;
+
+ ptable_set_reached(pd, ind);
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, ind, dd->m);
+ return;
+ }
+
+ dd->m++;
+ dd->last2 = dd->last1;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (possible_next[l2][l1][move]) {
+ dd->last1 = move;
+ generate_ptable_dfs(apply_move(move, c), pd, dd);
+ }
+ }
+
+ dd->m--;
+ dd->last1 = l1;
+ dd->last2 = l2;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r)
+{
+ Cube c;
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ c = apply_move(i, (Cube){0});
+ if (f != NULL && f(c))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0; /* Truncate */
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_set_reached(PruneData *pd, uint64_t ind)
+{
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return r == PTABLESIZE(pd->size);
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(uint16_t);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Alg *inv;
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Cube ret;
+
+ if (r != mirror) {
+ ret = apply_alg_generic(trans_algs[r], c, f, true);
+ inv = on_inverse(trans_algs[r]);
+ ret = apply_alg_generic(inv, ret, f, true);
+ free_alg(inv);
+ } else {
+ ret = move_via_arrays(&ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(&ma, ret, f);
+ }
+
+ return ret;
+}
+
+static void
+solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move move, l1 = dd->last1, l2 = dd->last2;
+ int i, lower_bound = s.check(c);
+ bool found_many, prune, niss_make_sense, nissbackup = dd->niss;
+
+ if (opts->can_niss && !dd->niss)
+ lower_bound = MIN(1, lower_bound);
+
+ found_many = dd->sols->len >= opts->max_solutions;
+ prune = dd->current_alg->len + lower_bound > dd->d;
+ if (found_many || prune)
+ return;
+
+ if (!lower_bound) { /* solved */
+ if (dd->current_alg->len == dd->d)
+ append_alg(dd->sols, dd->current_alg);
+ return;
+ }
+
+ dd->last2 = dd->last1;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (possible_next[l2][l1][move]) {
+ dd->last1 = move;
+ append_move(dd->current_alg, move, dd->niss);
+ solve_dfs(apply_move(move, c), s, opts, dd);
+ remove_last_move(dd->current_alg);
+ }
+ }
+
+ niss_make_sense = !dd->current_alg->len ||
+ (s.check(apply_move(l1,(Cube){0})));
+ if (opts->can_niss && !dd->niss && niss_make_sense) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+ solve_dfs(inverse_cube(c), s, opts, dd);
+ }
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = nissbackup;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return written == PTABLESIZE(pd->size);
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(uint16_t);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ uint64_t ui, uj;
+ int i, j, k;
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj] = epos_dependent(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2);
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ uint16_t ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray){
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_steps()
+{
+ /* PruneData */
+ pd_eofb_HTM.filename = "ptable_eofb_HTM";
+ pd_eofb_HTM.size = POW2TO11;
+ pd_eofb_HTM.index = index_eofb;
+ pd_eofb_HTM.moveset = moveset_HTM;
+
+ pd_coud_HTM.filename = "ptable_coud_HTM";
+ pd_coud_HTM.size = POW3TO7;
+ pd_coud_HTM.index = index_coud;
+ pd_coud_HTM.moveset = moveset_HTM;
+
+ pd_corners_HTM.filename = "ptable_corners_HTM";
+ pd_corners_HTM.size = POW3TO7 * FACTORIAL8;
+ pd_corners_HTM.index = index_corners;
+ pd_corners_HTM.moveset = moveset_HTM;
+
+ pd_ep_HTM.filename = "ptable_ep_HTM";
+ pd_ep_HTM.size = FACTORIAL12;
+ pd_ep_HTM.index = index_ep;
+ pd_ep_HTM.moveset = moveset_HTM;
+
+ pd_drud_HTM.filename = "ptable_drud_HTM";
+ pd_drud_HTM.size = POW2TO11 * POW3TO7 * BINOM12ON4;
+ pd_drud_HTM.index = index_drud;
+ pd_drud_HTM.moveset = moveset_HTM;
+
+
+ /* Actual steps */
+ eofb_HTM.check = check_eofb_HTM;
+ eofb_HTM.ready = check_nothing;
+ eofb_HTM.pre_trans = uf;
+ eofb_HTM.moveset = moveset_HTM;
+
+ eorl_HTM.check = check_eofb_HTM;
+ eorl_HTM.ready = check_nothing;
+ eorl_HTM.pre_trans = ur;
+ eorl_HTM.moveset = moveset_HTM;
+
+ eoud_HTM.check = check_eofb_HTM;
+ eoud_HTM.ready = check_nothing;
+ eoud_HTM.pre_trans = bu;
+ eoud_HTM.moveset = moveset_HTM;
+
+
+ coud_HTM.check = check_coud_HTM;
+ coud_HTM.ready = check_nothing;
+ coud_HTM.pre_trans = uf;
+ coud_HTM.moveset = moveset_HTM;
+
+ corl_HTM.check = check_coud_HTM;
+ corl_HTM.ready = check_nothing;
+ corl_HTM.pre_trans = rf;
+ corl_HTM.moveset = moveset_HTM;
+
+ cofb_HTM.check = check_coud_HTM;
+ cofb_HTM.ready = check_nothing;
+ cofb_HTM.pre_trans = fd;
+ cofb_HTM.moveset = moveset_HTM;
+
+ coud_URF.check = check_coud_URF;
+ coud_URF.ready = check_nothing;
+ coud_URF.pre_trans = uf;
+ coud_URF.moveset = moveset_URF;
+
+ corl_URF.check = check_coud_URF;
+ corl_URF.ready = check_nothing;
+ corl_URF.pre_trans = rf;
+ corl_URF.moveset = moveset_URF;
+
+ cofb_URF.check = check_coud_URF;
+ cofb_URF.ready = check_nothing;
+ cofb_URF.pre_trans = fd;
+ cofb_URF.moveset = moveset_URF;
+
+ corners_HTM.check = check_corners_HTM;
+ corners_HTM.ready = check_nothing;
+ corners_HTM.pre_trans = uf;
+ corners_HTM.moveset = moveset_HTM;
+
+ corners_URF.check = check_corners_URF;
+ corners_URF.ready = check_nothing;
+ corners_URF.pre_trans = uf;
+ corners_URF.moveset = moveset_URF;
+
+ edges_HTM.check = check_edges_HTM;
+ edges_HTM.ready = check_nothing;
+ edges_HTM.pre_trans = uf;
+ edges_HTM.moveset = moveset_HTM;
+
+ drud_HTM.check = check_drud_HTM;
+ drud_HTM.ready = check_nothing;
+ drud_HTM.pre_trans = uf;
+ drud_HTM.moveset = moveset_HTM;
+
+ optimal_HTM.check = check_optimal_HTM;
+ optimal_HTM.ready = check_nothing;
+ optimal_HTM.pre_trans = uf;
+ optimal_HTM.moveset = moveset_HTM;
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ bool b1, b2, b3;
+ uint16_t ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(trans_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ memcpy(eparr, ep_mirror, 12 * sizeof(int));
+ memcpy(cparr, cp_mirror, 8 * sizeof(int));
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(trans_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_ttable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_ttable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_ttable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_ttable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ trans_algs[uf] = new_alg("");
+ trans_algs[ur] = new_alg("y");
+ trans_algs[ub] = new_alg("y2");
+ trans_algs[ul] = new_alg("y3");
+
+ trans_algs[df] = new_alg("z2");
+ trans_algs[dr] = new_alg("y z2");
+ trans_algs[db] = new_alg("x2");
+ trans_algs[dl] = new_alg("y3 z2");
+
+ trans_algs[rf] = new_alg("z3");
+ trans_algs[rd] = new_alg("z3 y");
+ trans_algs[rb] = new_alg("z3 y2");
+ trans_algs[ru] = new_alg("z3 y3");
+
+ trans_algs[lf] = new_alg("z");
+ trans_algs[ld] = new_alg("z y3");
+ trans_algs[lb] = new_alg("z y2");
+ trans_algs[lu] = new_alg("z y");
+
+ trans_algs[fu] = new_alg("x y2");
+ trans_algs[fr] = new_alg("x y");
+ trans_algs[fd] = new_alg("x");
+ trans_algs[fl] = new_alg("x y3");
+
+ trans_algs[bu] = new_alg("x3");
+ trans_algs[br] = new_alg("x3 y");
+ trans_algs[bd] = new_alg("x3 y2");
+ trans_algs[bl] = new_alg("x3 y3");
+}
+
+/* Linearization functions implementation ************************************/
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_ep(Cube cube)
+{
+ uint64_t a, b, c;
+
+ /* TODO: remove this check */
+ if (epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] ==BINOM8ON4)
+ fprintf(stderr, "Impossible\n");
+
+ a = cube.eposs;
+ b = (cube.epose % FACTORIAL4) +
+ epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] *
+ FACTORIAL4;
+ c = cube.eposm % FACTORIAL4;
+
+ b *= FACTORIAL4 * BINOM12ON4;
+ c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+/* Step checking functions implementation ************************************/
+
+static int
+check_nothing(Cube cube)
+{
+ return true;
+}
+
+static int
+check_eofb_HTM(Cube cube)
+{
+ if (!pd_eofb_HTM.generated)
+ generate_ptable(&pd_eofb_HTM);
+
+ return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb);
+}
+
+static int
+check_coud_HTM(Cube cube)
+{
+ if (!pd_coud_HTM.generated)
+ generate_ptable(&pd_coud_HTM);
+
+ return PTABLEVAL(pd_coud_HTM.ptable, cube.coud);
+}
+
+static int
+check_coud_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ int ud = check_coud_HTM(cube);
+ int rl = check_coud_HTM(apply_move(z, cube));
+ int fb = check_coud_HTM(apply_move(x, cube));
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+check_corners_HTM(Cube cube)
+{
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+
+ return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+}
+
+static int
+check_corners_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int ret = 15;
+ Cube c;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ c = apply_alg(trans_algs[i], cube);
+ ret = MIN(ret, check_corners_HTM(c));
+ }
+
+ return ret;
+}
+
+static int
+check_edges_HTM(Cube cube)
+{
+ int ret = 0;
+
+ if (!pd_ep_HTM.generated)
+ generate_ptable(&pd_ep_HTM);
+
+ ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)));
+ ret = MAX(ret, check_eofb_HTM(cube));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube)));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube)));
+
+ return ret;
+}
+
+static int
+check_drud_HTM(Cube cube)
+{
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+
+ return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+}
+
+static int
+check_optimal_HTM(Cube cube)
+{
+ int dr1, dr2, dr3, drmax, cor; /*ep;*/
+
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+ /*
+ *if (!pd_ep_HTM.generated)
+ * generate_ptable(&pd_ep_HTM);
+ */
+
+ dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+ dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube)));
+ dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube)));
+
+ drmax = MAX(dr1, MAX(dr2, dr3));
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ drmax++;
+
+ cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+ /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */
+
+ /*return MAX(drmax, MAX(ep, cor));*/
+ if (drmax == 0 && cor == 0)
+ return is_solved(cube, false) ? 0 : 1;
+ return MAX(drmax, cor);
+}
+
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ Cube moved = {0};
+
+ moved.epose = epose_mtable[m][cube.epose];
+ moved.eposs = eposs_mtable[m][cube.eposs];
+ moved.eposm = eposm_mtable[m][cube.eposm];
+ moved.eofb = eofb_mtable[m][cube.eofb];
+ moved.eorl = eorl_mtable[m][cube.eorl];
+ moved.eoud = eoud_mtable[m][cube.eoud];
+ moved.coud = coud_mtable[m][cube.coud];
+ moved.cofb = cofb_mtable[m][cube.cofb];
+ moved.corl = corl_mtable[m][cube.corl];
+ moved.cp = cp_mtable[m][cube.cp];
+ moved.cpos = cpos_mtable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ Cube transformed = {0};
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ transformed.epose = epose_ttable[t][aux_epos[epose_source[t]]];
+ transformed.eposs = eposs_ttable[t][aux_epos[eposs_source[t]]];
+ transformed.eposm = eposm_ttable[t][aux_epos[eposm_source[t]]];
+ transformed.eofb = eo_ttable[t][aux_eo[eofb_source[t]]];
+ transformed.eorl = eo_ttable[t][aux_eo[eorl_source[t]]];
+ transformed.eoud = eo_ttable[t][aux_eo[eoud_source[t]]];
+ transformed.coud = co_ttable[t][aux_co[coud_source[t]]];
+ transformed.corl = co_ttable[t][aux_co[corl_source[t]]];
+ transformed.cofb = co_ttable[t][aux_co[cofb_source[t]]];
+ transformed.cp = cp_ttable[t][cube.cp];
+ transformed.cpos = cpos_ttable[t][cube.cpos];
+
+ return transformed;
+}
+
+/* TODO: this has to be changed using pre-computations */
+bool
+block_solved(Cube cube, Block block)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ int i;
+ bool ret = true;
+
+ for (i = 0; i < 12; i++)
+ ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i]));
+ for (i = 0; i < 8; i++)
+ ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i]));
+ for (i = 0; i < 6; i++)
+ ret = ret && !(block.center[i] && arr->cpos[i] != i);
+
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+Center
+center_at(Cube cube, Center c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cpos);
+
+ ret = arr->cpos[c];
+ free_cubearray(arr, pf_cpos);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+Corner
+corner_at(Cube cube, Corner c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cp);
+
+ ret = arr->cp[c];
+ free_cubearray(arr, pf_cp);
+
+ return ret;
+}
+
+Edge
+edge_at(Cube cube, Edge e)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ Trans i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(trans_algs[i],cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b;
+ uint16_t x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c = apply_trans(step.pre_trans, cube);
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(c))
+ return sols;
+
+ moveset_to_list(step.moveset, step.check, dd.sorted_moves);
+
+ for (dd.d = MAX(opts->min_moves, step.check(c));
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ solve_dfs(c, step, opts, &dd);
+ if (opts->feedback)
+ fprintf(stderr,
+ "Depth %d completed, found %d solutions\n",
+ dd.d, sols->len);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[step.pre_trans], node->alg);
+
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ AlgNode *i;
+
+ for (i = alg->last; i != NULL; i = i->prev)
+ append_move(ret, i->m, i->inverse);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->first = NULL;
+ alg->last = NULL;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ AlgNode *i;
+
+ for (i = alg->first; i != NULL; i = i->next)
+ append_move(ret, i->m, !i->inverse);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ char fill[4];
+ AlgNode *i;
+ bool niss = false;
+
+ for (i = alg->first; i != NULL; i = i->next) {
+ if (!niss && i->inverse)
+ strcpy(fill, i == alg->first ? "(" : " (");
+ if (niss && !i->inverse)
+ strcpy(fill, ") ");
+ if (niss == i->inverse)
+ strcpy(fill, i == alg->first ? "" : " ");
+
+ printf("%s%s", fill, move_string[i->m]);
+ niss = i->inverse;
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ memset(a, 0, 16 * sizeof(uint64_t));
+
+ if (!pd->generated)
+ generate_ptable(pd);
+
+ for (i = 0; i < pd->size; i++)
+ a[PTABLEVAL(pd->ptable, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+remove_last_move(Alg *alg)
+{
+ AlgNode *newlast = alg->last->prev;
+ free(alg->last);
+ if (newlast != NULL)
+ newlast->next = NULL;
+ alg->last = newlast;
+ alg->len--;
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ AlgNode *i;
+
+ for (i = alg->first; i != NULL; i = i->next)
+ i->m = moves_ttable[t][i->m];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_trans_aux();
+ init_trans();
+ init_steps();
+}
+
+
diff --git a/old/2021-06-14-oldalg/cube.h b/old/2021-06-14-oldalg/cube.h
@@ -0,0 +1,73 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "cubetypes.h"
+
+/* Steps *********************************************************************/
+
+extern Step eofb_HTM;
+extern Step eorl_HTM;
+extern Step eoud_HTM;
+extern Step coud_HTM;
+extern Step corl_HTM;
+extern Step cofb_HTM;
+extern Step coud_URF;
+extern Step corl_URF;
+extern Step cofb_URF;
+extern Step corners_HTM;
+extern Step corners_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Center center_at(Cube cube, Center c);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Corner corner_at(Cube cube, Corner c);
+Edge edge_at(Cube cube, Edge e);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+int piece_orientation(Cube cube, int piece, char *orientation);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+void remove_last_move(Alg *alg);
+void transform_alg(Trans t, Alg *alg);
+
+void print_ptable(PruneData *pd);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-14-oldalg/cubetypes.h b/old/2021-06-14-oldalg/cubetypes.h
@@ -0,0 +1,210 @@
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct algnode AlgNode;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ AlgNode * first;
+ AlgNode * last;
+ int len;
+};
+
+struct
+algnode
+{
+ Move m;
+ bool inverse;
+ AlgNode * next;
+ AlgNode * prev;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cube
+{
+ uint16_t epose;
+ uint16_t eposs;
+ uint16_t eposm;
+ uint16_t eofb;
+ uint16_t eorl;
+ uint16_t eoud;
+ uint16_t cp;
+ uint16_t coud;
+ uint16_t cofb;
+ uint16_t corl;
+ uint16_t cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[z3+1];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ uint64_t size;
+ uint64_t (*index)(Cube);
+ bool (*moveset)(Move);
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ int (*check)(Cube);
+ int (*ready)(Cube);
+ bool (*moveset)(Move);
+ Trans pre_trans;
+};
diff --git a/old/2021-06-14-oldalg/main.c b/old/2021-06-14-oldalg/main.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include "cube.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 2, nrand = 100000, sum;
+
+ Step *stps[20] = {&drud_HTM, &optimal_HTM};
+ char sss[30][30] = {"DR on U/D", "Optimal solution"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true
+ };
+
+ init();
+
+/*
+ print_ptable(&pd_drud_HTM);
+ print_ptable(&pd_ep_HTM);
+ print_ptable(&pd_corners_HTM);
+*/
+
+ srand(time(NULL));
+ sum = 0;
+ for (i = 0; i < nrand; i++)
+ sum += optimal_HTM.check(random_cube());
+ printf("Average pruning value: %lf\n", ((double)sum) / ((double) nrand));
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ for (i = 0; i < ns; i++) {
+ if (stps[i]->check == NULL)
+ fprintf(stderr, "Check function for step %d is null\n", i);
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free(algo);
+ }
+
+ return 0;
+}
diff --git a/old/2021-06-15-before-separating-steps/cube.c b/old/2021-06-15-before-separating-steps/cube.c
@@ -0,0 +1,2836 @@
+#include "cube.h"
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define BASEALGLEN 50
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+#define PTABLESIZE(n) ((n+1) / 2)
+#define PTABLEVAL(tab,ind) (((ind)%2) ? (tab[(ind)/2] / 16) : \
+ (tab[(ind)/2] % 16))
+
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static uint16_t array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static Move base_move(Move m);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent(int pos1, int pos2);
+static uint16_t epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void generate_ptable(PruneData *pd);
+static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void moveset_to_list(bool (*ms)(Move), int (*f)(Cube), Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_set_reached(PruneData *pd, uint64_t ind);
+static void ptable_update(PruneData *pd, uint64_t ind, int m);
+static bool read_ptable_file(PruneData *pd);
+static bool read_ttables_file();
+static bool read_mtables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_ptable_file(PruneData *pd);
+static bool write_ttables_file();
+static bool write_mtables_file();
+
+static void init_auxtables();
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_steps();
+static void init_strings();
+static void init_trans();
+static void init_trans_aux();
+
+static bool moveset_HTM(Move m);
+static bool moveset_URF(Move m);
+
+/* Steps and related functions and data **************************************/
+
+Step eofb_HTM;
+Step eorl_HTM;
+Step eoud_HTM;
+Step coud_HTM;
+Step corl_HTM;
+Step cofb_HTM;
+Step coud_URF;
+Step corl_URF;
+Step cofb_URF;
+Step corners_HTM;
+Step corners_URF;
+Step edges_HTM;
+Step drud_HTM;
+Step optimal_HTM;
+
+PruneData pd_eofb_HTM;
+PruneData pd_coud_HTM;
+PruneData pd_corners_HTM;
+PruneData pd_ep_HTM;
+PruneData pd_drud_HTM;
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_ep(Cube cube);
+static uint64_t index_drud(Cube cube);
+
+static int check_nothing(Cube cube);
+static int check_eofb_HTM(Cube cube);
+static int check_coud_HTM(Cube cube);
+static int check_coud_URF(Cube cube);
+static int check_corners_HTM(Cube cube);
+static int check_corners_URF(Cube cube);
+static int check_edges_HTM(Cube cube);
+static int check_drud_HTM(Cube cube);
+static int check_optimal_HTM(Cube cube);
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+
+static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eo_ttable[NTRANS][POW2TO11];
+static uint16_t cp_ttable[NTRANS][FACTORIAL8];
+static uint16_t co_ttable[NTRANS][POW3TO7];
+static uint16_t cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eofb_mtable[NMOVES][POW2TO11];
+static uint16_t eorl_mtable[NMOVES][POW2TO11];
+static uint16_t eoud_mtable[NMOVES][POW2TO11];
+static uint16_t cp_mtable[NMOVES][FACTORIAL8];
+static uint16_t coud_mtable[NMOVES][POW3TO7];
+static uint16_t cofb_mtable[NMOVES][POW3TO7];
+static uint16_t corl_mtable[NMOVES][POW3TO7];
+static uint16_t cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * trans_algs[NROTATIONS];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated) {
+ if (alg->len > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might be wrong.\n");
+ }
+ alg->move = realloc(alg->move, 2 * alg->len * sizeof(Move));
+ alg->inv = realloc(alg->inv, 2 * alg->len * sizeof(bool));
+ alg->allocated *= 2;
+ }
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static uint16_t
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 1)
+ ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0;
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static uint16_t
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(uint16_t epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+generate_ptable(PruneData *pd)
+{
+ uint64_t j;
+ DfsData dd;
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ dd.m = 0;
+ dd.last1 = NULLMOVE;
+ dd.last2 = NULLMOVE;
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->size; j++)
+ ptable_update(pd, j, 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+
+ pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) {
+ memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t));
+ generate_ptable_dfs((Cube){0}, pd, &dd);
+ fprintf(stderr, "Depth %d completed, generated %lu/%lu\n",
+ dd.d, pd->n, pd->size);
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+static void
+generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ uint64_t ind = pd->index(c);
+ int oldval = PTABLEVAL(pd->ptable, ind);
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size)
+ return;
+
+ ptable_set_reached(pd, ind);
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, ind, dd->m);
+ return;
+ }
+
+ dd->m++;
+ dd->last2 = dd->last1;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (possible_next[l2][l1][move]) {
+ dd->last1 = move;
+ generate_ptable_dfs(apply_move(move, c), pd, dd);
+ }
+ }
+
+ dd->m--;
+ dd->last1 = l1;
+ dd->last2 = l2;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r)
+{
+ Cube c;
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ c = apply_move(i, (Cube){0});
+ if (f != NULL && f(c))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0; /* Truncate */
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_set_reached(PruneData *pd, uint64_t ind)
+{
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return r == PTABLESIZE(pd->size);
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(uint16_t);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Alg *inv;
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Cube ret;
+
+ if (r != mirror) {
+ ret = apply_alg_generic(trans_algs[r], c, f, true);
+ inv = on_inverse(trans_algs[r]);
+ ret = apply_alg_generic(inv, ret, f, true);
+ free_alg(inv);
+ } else {
+ ret = move_via_arrays(&ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(&ma, ret, f);
+ }
+
+ return ret;
+}
+
+static void
+solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move move, l1 = dd->last1, l2 = dd->last2;
+ int i, lower_bound = s.check(c);
+ bool found_many, prune, niss_make_sense, nissbackup = dd->niss;
+
+ if (opts->can_niss && !dd->niss)
+ lower_bound = MIN(1, lower_bound);
+
+ found_many = dd->sols->len >= opts->max_solutions;
+ prune = dd->current_alg->len + lower_bound > dd->d;
+ if (found_many || prune)
+ return;
+
+ if (lower_bound == 0) {
+ if (dd->current_alg->len == dd->d)
+ append_alg(dd->sols, dd->current_alg);
+ return;
+ }
+
+ dd->last2 = dd->last1;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (possible_next[l2][l1][move]) {
+ dd->last1 = move;
+ append_move(dd->current_alg, move, dd->niss);
+ solve_dfs(apply_move(move, c), s, opts, dd);
+ dd->current_alg->len--;
+ }
+ }
+
+ niss_make_sense = !dd->current_alg->len ||
+ (s.check(apply_move(l1,(Cube){0})));
+ if (opts->can_niss && !dd->niss && niss_make_sense) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+ solve_dfs(inverse_cube(c), s, opts, dd);
+ }
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = nissbackup;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return written == PTABLESIZE(pd->size);
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(uint16_t);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ uint64_t ui, uj;
+ int i, j, k;
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj] = epos_dependent(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2);
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ uint16_t ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray){
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_steps()
+{
+ /* PruneData */
+ pd_eofb_HTM.filename = "ptable_eofb_HTM";
+ pd_eofb_HTM.size = POW2TO11;
+ pd_eofb_HTM.index = index_eofb;
+ pd_eofb_HTM.moveset = moveset_HTM;
+
+ pd_coud_HTM.filename = "ptable_coud_HTM";
+ pd_coud_HTM.size = POW3TO7;
+ pd_coud_HTM.index = index_coud;
+ pd_coud_HTM.moveset = moveset_HTM;
+
+ pd_corners_HTM.filename = "ptable_corners_HTM";
+ pd_corners_HTM.size = POW3TO7 * FACTORIAL8;
+ pd_corners_HTM.index = index_corners;
+ pd_corners_HTM.moveset = moveset_HTM;
+
+ pd_ep_HTM.filename = "ptable_ep_HTM";
+ pd_ep_HTM.size = FACTORIAL12;
+ pd_ep_HTM.index = index_ep;
+ pd_ep_HTM.moveset = moveset_HTM;
+
+ pd_drud_HTM.filename = "ptable_drud_HTM";
+ pd_drud_HTM.size = POW2TO11 * POW3TO7 * BINOM12ON4;
+ pd_drud_HTM.index = index_drud;
+ pd_drud_HTM.moveset = moveset_HTM;
+
+
+ /* Actual steps */
+ eofb_HTM.check = check_eofb_HTM;
+ eofb_HTM.ready = check_nothing;
+ eofb_HTM.pre_trans = uf;
+ eofb_HTM.moveset = moveset_HTM;
+
+ eorl_HTM.check = check_eofb_HTM;
+ eorl_HTM.ready = check_nothing;
+ eorl_HTM.pre_trans = ur;
+ eorl_HTM.moveset = moveset_HTM;
+
+ eoud_HTM.check = check_eofb_HTM;
+ eoud_HTM.ready = check_nothing;
+ eoud_HTM.pre_trans = bu;
+ eoud_HTM.moveset = moveset_HTM;
+
+
+ coud_HTM.check = check_coud_HTM;
+ coud_HTM.ready = check_nothing;
+ coud_HTM.pre_trans = uf;
+ coud_HTM.moveset = moveset_HTM;
+
+ corl_HTM.check = check_coud_HTM;
+ corl_HTM.ready = check_nothing;
+ corl_HTM.pre_trans = rf;
+ corl_HTM.moveset = moveset_HTM;
+
+ cofb_HTM.check = check_coud_HTM;
+ cofb_HTM.ready = check_nothing;
+ cofb_HTM.pre_trans = fd;
+ cofb_HTM.moveset = moveset_HTM;
+
+ coud_URF.check = check_coud_URF;
+ coud_URF.ready = check_nothing;
+ coud_URF.pre_trans = uf;
+ coud_URF.moveset = moveset_URF;
+
+ corl_URF.check = check_coud_URF;
+ corl_URF.ready = check_nothing;
+ corl_URF.pre_trans = rf;
+ corl_URF.moveset = moveset_URF;
+
+ cofb_URF.check = check_coud_URF;
+ cofb_URF.ready = check_nothing;
+ cofb_URF.pre_trans = fd;
+ cofb_URF.moveset = moveset_URF;
+
+ corners_HTM.check = check_corners_HTM;
+ corners_HTM.ready = check_nothing;
+ corners_HTM.pre_trans = uf;
+ corners_HTM.moveset = moveset_HTM;
+
+ corners_URF.check = check_corners_URF;
+ corners_URF.ready = check_nothing;
+ corners_URF.pre_trans = uf;
+ corners_URF.moveset = moveset_URF;
+
+ edges_HTM.check = check_edges_HTM;
+ edges_HTM.ready = check_nothing;
+ edges_HTM.pre_trans = uf;
+ edges_HTM.moveset = moveset_HTM;
+
+ drud_HTM.check = check_drud_HTM;
+ drud_HTM.ready = check_nothing;
+ drud_HTM.pre_trans = uf;
+ drud_HTM.moveset = moveset_HTM;
+
+ optimal_HTM.check = check_optimal_HTM;
+ optimal_HTM.ready = check_nothing;
+ optimal_HTM.pre_trans = uf;
+ optimal_HTM.moveset = moveset_HTM;
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ bool b1, b2, b3;
+ uint16_t ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(trans_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ memcpy(eparr, ep_mirror, 12 * sizeof(int));
+ memcpy(cparr, cp_mirror, 8 * sizeof(int));
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(trans_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_ttable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_ttable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_ttable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_ttable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ trans_algs[uf] = new_alg("");
+ trans_algs[ur] = new_alg("y");
+ trans_algs[ub] = new_alg("y2");
+ trans_algs[ul] = new_alg("y3");
+
+ trans_algs[df] = new_alg("z2");
+ trans_algs[dr] = new_alg("y z2");
+ trans_algs[db] = new_alg("x2");
+ trans_algs[dl] = new_alg("y3 z2");
+
+ trans_algs[rf] = new_alg("z3");
+ trans_algs[rd] = new_alg("z3 y");
+ trans_algs[rb] = new_alg("z3 y2");
+ trans_algs[ru] = new_alg("z3 y3");
+
+ trans_algs[lf] = new_alg("z");
+ trans_algs[ld] = new_alg("z y3");
+ trans_algs[lb] = new_alg("z y2");
+ trans_algs[lu] = new_alg("z y");
+
+ trans_algs[fu] = new_alg("x y2");
+ trans_algs[fr] = new_alg("x y");
+ trans_algs[fd] = new_alg("x");
+ trans_algs[fl] = new_alg("x y3");
+
+ trans_algs[bu] = new_alg("x3");
+ trans_algs[br] = new_alg("x3 y");
+ trans_algs[bd] = new_alg("x3 y2");
+ trans_algs[bl] = new_alg("x3 y3");
+}
+
+/* Linearization functions implementation ************************************/
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_ep(Cube cube)
+{
+ uint64_t a, b, c;
+
+ /* TODO: remove this check */
+ if (epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] ==BINOM8ON4)
+ fprintf(stderr, "Impossible\n");
+
+ a = cube.eposs;
+ b = (cube.epose % FACTORIAL4) +
+ epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] *
+ FACTORIAL4;
+ c = cube.eposm % FACTORIAL4;
+
+ b *= FACTORIAL4 * BINOM12ON4;
+ c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+/* Step checking functions implementation ************************************/
+
+static int
+check_nothing(Cube cube)
+{
+ return true;
+}
+
+static int
+check_eofb_HTM(Cube cube)
+{
+ if (!pd_eofb_HTM.generated)
+ generate_ptable(&pd_eofb_HTM);
+
+ return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb);
+}
+
+static int
+check_coud_HTM(Cube cube)
+{
+ if (!pd_coud_HTM.generated)
+ generate_ptable(&pd_coud_HTM);
+
+ return PTABLEVAL(pd_coud_HTM.ptable, cube.coud);
+}
+
+static int
+check_coud_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ int ud = check_coud_HTM(cube);
+ int rl = check_coud_HTM(apply_move(z, cube));
+ int fb = check_coud_HTM(apply_move(x, cube));
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+check_corners_HTM(Cube cube)
+{
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+
+ return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+}
+
+static int
+check_corners_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int ret = 15;
+ Cube c;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ c = apply_alg(trans_algs[i], cube);
+ ret = MIN(ret, check_corners_HTM(c));
+ }
+
+ return ret;
+}
+
+static int
+check_edges_HTM(Cube cube)
+{
+ int ret = 0;
+
+ if (!pd_ep_HTM.generated)
+ generate_ptable(&pd_ep_HTM);
+
+ ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)));
+ ret = MAX(ret, check_eofb_HTM(cube));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube)));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube)));
+
+ return ret;
+}
+
+static int
+check_drud_HTM(Cube cube)
+{
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+
+ return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+}
+
+static int
+check_optimal_HTM(Cube cube)
+{
+ int dr1, dr2, dr3, drmax, cor; /*ep;*/
+
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+ /*
+ *if (!pd_ep_HTM.generated)
+ * generate_ptable(&pd_ep_HTM);
+ */
+
+ dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+ dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube)));
+ dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube)));
+
+ drmax = MAX(dr1, MAX(dr2, dr3));
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ drmax++;
+
+ cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+ /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */
+
+ /*return MAX(drmax, MAX(ep, cor));*/
+ if (drmax == 0 && cor == 0)
+ return is_solved(cube, false) ? 0 : 1;
+ return MAX(drmax, cor);
+}
+
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ Cube moved = {0};
+
+ moved.epose = epose_mtable[m][cube.epose];
+ moved.eposs = eposs_mtable[m][cube.eposs];
+ moved.eposm = eposm_mtable[m][cube.eposm];
+ moved.eofb = eofb_mtable[m][cube.eofb];
+ moved.eorl = eorl_mtable[m][cube.eorl];
+ moved.eoud = eoud_mtable[m][cube.eoud];
+ moved.coud = coud_mtable[m][cube.coud];
+ moved.cofb = cofb_mtable[m][cube.cofb];
+ moved.corl = corl_mtable[m][cube.corl];
+ moved.cp = cp_mtable[m][cube.cp];
+ moved.cpos = cpos_mtable[m][cube.cpos];
+
+ return moved;
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ Cube transformed = {0};
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ transformed.epose = epose_ttable[t][aux_epos[epose_source[t]]];
+ transformed.eposs = eposs_ttable[t][aux_epos[eposs_source[t]]];
+ transformed.eposm = eposm_ttable[t][aux_epos[eposm_source[t]]];
+ transformed.eofb = eo_ttable[t][aux_eo[eofb_source[t]]];
+ transformed.eorl = eo_ttable[t][aux_eo[eorl_source[t]]];
+ transformed.eoud = eo_ttable[t][aux_eo[eoud_source[t]]];
+ transformed.coud = co_ttable[t][aux_co[coud_source[t]]];
+ transformed.corl = co_ttable[t][aux_co[corl_source[t]]];
+ transformed.cofb = co_ttable[t][aux_co[cofb_source[t]]];
+ transformed.cp = cp_ttable[t][cube.cp];
+ transformed.cpos = cpos_ttable[t][cube.cpos];
+
+ return transformed;
+}
+
+/* TODO: this has to be changed using pre-computations */
+bool
+block_solved(Cube cube, Block block)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ int i;
+ bool ret = true;
+
+ for (i = 0; i < 12; i++)
+ ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i]));
+ for (i = 0; i < 8; i++)
+ ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i]));
+ for (i = 0; i < 6; i++)
+ ret = ret && !(block.center[i] && arr->cpos[i] != i);
+
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+Center
+center_at(Cube cube, Center c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cpos);
+
+ ret = arr->cpos[c];
+ free_cubearray(arr, pf_cpos);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+Corner
+corner_at(Cube cube, Corner c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cp);
+
+ ret = arr->cp[c];
+ free_cubearray(arr, pf_cp);
+
+ return ret;
+}
+
+Edge
+edge_at(Cube cube, Edge e)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ Trans i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(trans_algs[i],cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b;
+ uint16_t x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c = apply_trans(step.pre_trans, cube);
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(c))
+ return sols;
+
+ moveset_to_list(step.moveset, step.check, dd.sorted_moves);
+
+ for (dd.d = MAX(opts->min_moves, step.check(c));
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ solve_dfs(c, step, opts, &dd);
+ if (opts->feedback)
+ fprintf(stderr,
+ "Depth %d completed, found %d solutions\n",
+ dd.d, sols->len);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[step.pre_trans], node->alg);
+
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, alg->move[i], alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ memset(a, 0, 16 * sizeof(uint64_t));
+
+ if (!pd->generated)
+ generate_ptable(pd);
+
+ for (i = 0; i < pd->size; i++)
+ a[PTABLEVAL(pd->ptable, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_trans_aux();
+ init_trans();
+ init_steps();
+}
+
+
diff --git a/old/2021-06-15-before-separating-steps/cube.h b/old/2021-06-15-before-separating-steps/cube.h
@@ -0,0 +1,72 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "cubetypes.h"
+
+/* Steps *********************************************************************/
+
+extern Step eofb_HTM;
+extern Step eorl_HTM;
+extern Step eoud_HTM;
+extern Step coud_HTM;
+extern Step corl_HTM;
+extern Step cofb_HTM;
+extern Step coud_URF;
+extern Step corl_URF;
+extern Step cofb_URF;
+extern Step corners_HTM;
+extern Step corners_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Center center_at(Cube cube, Center c);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Corner corner_at(Cube cube, Corner c);
+Edge edge_at(Cube cube, Edge e);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+int piece_orientation(Cube cube, int piece, char *orientation);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+void transform_alg(Trans t, Alg *alg);
+
+void print_ptable(PruneData *pd);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-15-before-separating-steps/cubetypes.h b/old/2021-06-15-before-separating-steps/cubetypes.h
@@ -0,0 +1,201 @@
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cube
+{
+ uint16_t epose;
+ uint16_t eposs;
+ uint16_t eposm;
+ uint16_t eofb;
+ uint16_t eorl;
+ uint16_t eoud;
+ uint16_t cp;
+ uint16_t coud;
+ uint16_t cofb;
+ uint16_t corl;
+ uint16_t cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[z3+1];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ uint64_t size;
+ uint64_t (*index)(Cube);
+ bool (*moveset)(Move);
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ int (*check)(Cube);
+ int (*ready)(Cube);
+ bool (*moveset)(Move);
+ Trans pre_trans;
+};
diff --git a/old/2021-06-15-before-separating-steps/main.c b/old/2021-06-15-before-separating-steps/main.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include "cube.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 2, nrand = 100000, sum1, sum2;
+
+ Step *stps[20] = {&drud_HTM, &optimal_HTM};
+ char sss[30][30] = {"DR on U/D", "Optimal solution"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true
+ };
+
+ init();
+
+/*
+ print_ptable(&pd_drud_HTM);
+ print_ptable(&pd_ep_HTM);
+ print_ptable(&pd_corners_HTM);
+*/
+
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ for (i = 0; i < nrand; i++) {
+ sum1 += optimal_HTM.check(random_cube());
+ sum2 += corners_HTM.check(random_cube());
+ }
+ printf("Average optimal pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners pruning: %lf\n", ((double)sum2) / ((double) nrand));
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ for (i = 0; i < ns; i++) {
+ if (stps[i]->check == NULL)
+ fprintf(stderr, "Check function for step %d is null\n", i);
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free(algo);
+ }
+
+ return 0;
+}
diff --git a/old/2021-06-15-before-separating-steps/steps.h b/old/2021-06-15-before-separating-steps/steps.h
@@ -0,0 +1,20 @@
+extern Step eofb_HTM;
+extern Step eorl_HTM;
+extern Step eoud_HTM;
+extern Step coud_HTM;
+extern Step corl_HTM;
+extern Step cofb_HTM;
+extern Step coud_URF;
+extern Step corl_URF;
+extern Step cofb_URF;
+extern Step corners_HTM;
+extern Step corners_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
diff --git a/old/2021-06-17-cachedata/cube.c b/old/2021-06-17-cachedata/cube.c
@@ -0,0 +1,2738 @@
+#include "cube.h"
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+#define PTABLESIZE(n) ((n+1) / 2)
+
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static bool allowed_next_move(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static uint16_t array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent(int pos1, int pos2);
+static uint16_t epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void generate_ctable_dfs(Cube c, CacheData *cd, DfsData *dd);
+static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Checker f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+/*static int powint(int a, int b);*/
+static void ptable_set_reached(PruneData *pd, uint64_t ind);
+static void ptable_update(PruneData *pd, uint64_t ind, int m);
+static void push_cachenode(CacheData *cd, Cube c, Alg *alg);
+static void realloc_alg(Alg *alg, int n);
+static bool read_ctable_file(CacheData *cd);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static void solve_cd(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_ctable_file(CacheData *cd);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_ttables_file();
+
+static void init_auxtables();
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_trans();
+static void init_trans_aux();
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+
+static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eo_ttable[NTRANS][POW2TO11];
+static uint16_t cp_ttable[NTRANS][FACTORIAL8];
+static uint16_t co_ttable[NTRANS][POW3TO7];
+static uint16_t cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eofb_mtable[NMOVES][POW2TO11];
+static uint16_t eorl_mtable[NMOVES][POW2TO11];
+static uint16_t eoud_mtable[NMOVES][POW2TO11];
+static uint16_t cp_mtable[NMOVES][FACTORIAL8];
+static uint16_t coud_mtable[NMOVES][POW3TO7];
+static uint16_t cofb_mtable[NMOVES][POW3TO7];
+static uint16_t corl_mtable[NMOVES][POW3TO7];
+static uint16_t cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * trans_algs[NROTATIONS];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static bool
+allowed_next_move(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static uint16_t
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 1)
+ ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0;
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static uint16_t
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(uint16_t epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+generate_ctable_dfs(Cube c, CacheData *cd, DfsData *dd)
+{
+ int i;
+ bool flag;
+ Move move, l1 = dd->last1, l2 = dd->last2;
+
+ if (dd->current_alg->len > 0) {
+ for (i = 0, flag = true; i < cd->nind; i++) {
+ if (cd->index[i](c) != cd->index[i]((Cube){0})) {
+ flag = false;
+ break;
+ }
+ }
+ if (flag)
+ return;
+ }
+
+ if (dd->current_alg->len == dd->d) {
+ push_cachenode(cd, c, dd->current_alg);
+ return;
+ }
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next_move(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+ append_move(dd->current_alg, move, false);
+ generate_ctable_dfs(apply_move(move, c), cd, dd);
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static void
+generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ uint64_t ind = pd->index(c);
+ int oldval = PTABLEVAL(pd->ptable, ind);
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size)
+ return;
+
+ ptable_set_reached(pd, ind);
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, ind, dd->m);
+ return;
+ }
+
+ dd->m++;
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next_move(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+ generate_ptable_dfs(apply_move(move, c), pd, dd);
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+ dd->last1 = l1;
+ dd->last2 = l2;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r)
+{
+ Cube c;
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ c = apply_move(i, (Cube){0});
+ if (f != NULL && f(c))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+/*
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+*/
+
+static void
+ptable_set_reached(PruneData *pd, uint64_t ind)
+{
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+push_cachenode(CacheData *cd, Cube c, Alg *alg)
+{
+ CacheNode *node = malloc(sizeof(CacheNode));
+ uint64_t ind0 = cd->index[0](c);
+ int i;
+
+ node->indexval = malloc((cd->nind - 1) * sizeof(uint64_t));
+ for (i = 1; i < cd->nind; i++)
+ node->indexval[i-1] = cd->index[i](c);
+
+ node->sol = inverse_alg(alg);
+ realloc_alg(node->sol, cd->len);
+ node->next = cd->ctable[ind0];
+ cd->ctable[ind0] = node;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n);
+ alg->inv = realloc(alg->inv, n);
+ alg->allocated = n;
+}
+
+static bool
+read_ctable_file(CacheData *cd)
+{
+ return false;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return r == PTABLESIZE(pd->size);
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(uint16_t);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Alg *inv;
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Cube ret;
+
+ if (r != mirror) {
+ ret = apply_alg_generic(trans_algs[r], c, f, true);
+ inv = on_inverse(trans_algs[r]);
+ ret = apply_alg_generic(inv, ret, f, true);
+ free_alg(inv);
+ } else {
+ ret = move_via_arrays(&ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(&ma, ret, f);
+ }
+
+ return ret;
+}
+
+static void
+solve_cd(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ int j, k;
+ bool flag;
+ uint64_t ind0;
+ CacheNode *i;
+
+ if (!s.cd->generated)
+ generate_ctable(s.cd);
+
+ ind0 = s.cd->index[0](c);
+ for (i = s.cd->ctable[ind0];
+ i != NULL && dd->sols->len < opts->max_solutions;
+ i = i->next) {
+ for (j = 1, flag = true; j < s.cd->nind; j++) {
+ if (s.cd->index[j](c) != i->indexval[j-1]) {
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ for (k = 0; k < s.cd->len; k++)
+ append_move(dd->current_alg,
+ i->sol->move[k], dd->niss);
+ append_alg(dd->sols, dd->current_alg);
+ if (opts->feedback) {
+ printf("[cached] ");
+ print_alg(dd->current_alg, false);
+ }
+ }
+ }
+}
+
+static void
+solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move move, l1 = dd->last1, l2 = dd->last2;
+ int i, lower_bound;
+ bool nissbackup = dd->niss;
+
+ if (dd->sols->len >= opts->max_solutions)
+ return;
+
+ lower_bound = s.check(c);
+ if (opts->can_niss && !dd->niss)
+ lower_bound = MIN(1, lower_bound);
+
+ if (dd->current_alg->len + lower_bound > dd->d)
+ return;
+
+ if (lower_bound == 0) {
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+ return;
+ }
+
+ if (s.cd != NULL && s.cd->len == dd->d - dd->current_alg->len) {
+ solve_cd(c, s, opts, dd);
+ } else {
+ for (i = 0;
+ dd->sorted_moves[i] != NULLMOVE &&
+ dd->sols->len < opts->max_solutions;
+ i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next_move(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+ append_move(dd->current_alg, move, dd->niss);
+ solve_dfs(apply_move(move, c), s, opts, dd);
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+ }
+
+ if (opts->can_niss && !dd->niss) {
+ if (dd->current_alg->len == 0 ||
+ (s.check(apply_move(inverse_move(l1), (Cube){0})))) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+ solve_dfs(inverse_cube(c), s, opts, dd);
+ }
+ }
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = nissbackup;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_ctable_file(CacheData *cd)
+{
+ return false;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f);
+ fclose(f);
+
+ return written == PTABLESIZE(pd->size);
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(uint16_t);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ uint64_t ui, uj;
+ int i, j, k;
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj] = epos_dependent(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ uint16_t ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ bool b1, b2, b3;
+ uint16_t ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(trans_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(edge_at(cube, FR));
+ eposs_source[i] = edge_slice(edge_at(cube, UR));
+ eposm_source[i] = edge_slice(edge_at(cube, UF));
+ eofb_source[i] = center_at(cube, F_center)/2;
+ eorl_source[i] = center_at(cube, R_center)/2;
+ eoud_source[i] = center_at(cube, U_center)/2;
+ coud_source[i] = center_at(cube, U_center)/2;
+ cofb_source[i] = center_at(cube, F_center)/2;
+ corl_source[i] = center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ memcpy(eparr, ep_mirror, 12 * sizeof(int));
+ memcpy(cparr, cp_mirror, 8 * sizeof(int));
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(trans_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_ttable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_ttable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_ttable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_ttable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ trans_algs[uf] = new_alg("");
+ trans_algs[ur] = new_alg("y");
+ trans_algs[ub] = new_alg("y2");
+ trans_algs[ul] = new_alg("y3");
+
+ trans_algs[df] = new_alg("z2");
+ trans_algs[dr] = new_alg("y z2");
+ trans_algs[db] = new_alg("x2");
+ trans_algs[dl] = new_alg("y3 z2");
+
+ trans_algs[rf] = new_alg("z3");
+ trans_algs[rd] = new_alg("z3 y");
+ trans_algs[rb] = new_alg("z3 y2");
+ trans_algs[ru] = new_alg("z3 y3");
+
+ trans_algs[lf] = new_alg("z");
+ trans_algs[ld] = new_alg("z y3");
+ trans_algs[lb] = new_alg("z y2");
+ trans_algs[lu] = new_alg("z y");
+
+ trans_algs[fu] = new_alg("x y2");
+ trans_algs[fr] = new_alg("x y");
+ trans_algs[fd] = new_alg("x");
+ trans_algs[fl] = new_alg("x y3");
+
+ trans_algs[bu] = new_alg("x3");
+ trans_algs[br] = new_alg("x3 y");
+ trans_algs[bd] = new_alg("x3 y2");
+ trans_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+/* TODO: this has to be changed using pre-computations */
+bool
+block_solved(Cube cube, Block block)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ int i;
+ bool ret = true;
+
+ for (i = 0; i < 12; i++)
+ ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i]));
+ for (i = 0; i < 8; i++)
+ ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i]));
+ for (i = 0; i < 6; i++)
+ ret = ret && !(block.center[i] && arr->cpos[i] != i);
+
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+Center
+center_at(Cube cube, Center c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cpos);
+
+ ret = arr->cpos[c];
+ free_cubearray(arr, pf_cpos);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+Corner
+corner_at(Cube cube, Corner c)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_cp);
+
+ ret = arr->cp[c];
+ free_cubearray(arr, pf_cp);
+
+ return ret;
+}
+
+Edge
+edge_at(Cube cube, Edge e)
+{
+ int ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+uint64_t
+epos_dependent_cube(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+generate_ctable(CacheData *cd)
+{
+ uint64_t i;
+ DfsData dd = {
+ .d = cd->len,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .current_alg = new_alg("")
+ };
+
+ if (cd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ cd->ctable = malloc(cd->maxind0 * sizeof(CacheNode *));
+ for (i = 0; i < cd->maxind0; i++)
+ cd->ctable[i] = NULL;
+
+ if (read_ctable_file(cd)) {
+ cd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", cd->filename);
+
+ moveset_to_list(cd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ generate_ctable_dfs((Cube){0}, cd, &dd);
+
+ if (!write_ctable_file(cd))
+ fprintf(stderr, "Error writing ctable file\n");
+
+ cd->generated = true;
+}
+
+void
+generate_ptable(PruneData *pd)
+{
+ uint64_t j;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->size; j++)
+ ptable_update(pd, j, 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) {
+ memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t));
+ generate_ptable_dfs((Cube){0}, pd, &dd);
+ fprintf(stderr, "Depth %d completed, generated %lu/%lu\n",
+ dd.d, pd->n, pd->size);
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ Trans i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(trans_algs[i],cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b;
+ uint16_t x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c = apply_trans(opts->pre_trans, cube);
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(c)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.check, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = MAX(opts->min_moves, step.check(c));
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ solve_dfs(c, step, opts, &dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[opts->pre_trans], node->alg);
+
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, alg->move[i], alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_cachedata(CacheData *cd, bool printalgs)
+{
+ CacheNode *j;
+ uint64_t i, s, maxa = 0, maxs = 0, n = 0, empty = 0;
+
+ if (!cd->generated)
+ generate_ctable(cd);
+
+ for (i = 0; i < cd->maxind0; i++) {
+ if (cd->ctable[i] == NULL) {
+ empty++;
+ continue;
+ }
+
+ for (j = cd->ctable[i], s = 0;
+ j != NULL; j = j->next, n++, s++)
+ if (printalgs)
+ print_alg(j->sol, false);
+
+ if (s > maxs) {
+ maxs = s;
+ maxa = i;
+ }
+ }
+
+ printf("Cache data %s\n", cd->filename);
+ printf("Length: %d\n", cd->len);
+ printf("Main table cells: %lu (of which %lu empty)\n", i, empty);
+ printf("%lu solutions cached ", n);
+ printf("(most crowded cell: %lu with %lu)\n", maxa, maxs);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ generate_ptable(pd);
+
+ for (i = 0; i < pd->size; i++)
+ a[PTABLEVAL(pd->ptable, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+
+Alg *
+trans_alg(Trans i)
+{
+ return trans_algs[i];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_trans_aux();
+ init_trans();
+}
+
diff --git a/old/2021-06-17-cachedata/cube.h b/old/2021-06-17-cachedata/cube.h
@@ -0,0 +1,79 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+#define PTABLESIZE(n) ((n+1) / 2)
+#define PTABLEVAL(t,i) (((i)%2) ? (t[(i)/2] / 16) : (t[(i)/2] % 16))
+
+/* Type definitions **********************************************************/
+
+#include "cubetypes.h"
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Center center_at(Cube cube, Center c);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Corner corner_at(Cube cube, Corner c);
+Edge edge_at(Cube cube, Edge e);
+uint64_t epos_dependent_cube(Cube cube);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+int piece_orientation(Cube cube, int piece, char *orientation);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * trans_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void generate_ctable(CacheData *cd);
+void generate_ptable(PruneData *pd);
+void print_cachedata(CacheData *cd, bool printalgs);
+void print_ptable(PruneData *pd);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-17-cachedata/cubetypes.h b/old/2021-06-17-cachedata/cubetypes.h
@@ -0,0 +1,232 @@
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct cachedata CacheData;
+typedef struct cachenode CacheNode;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+typedef int (*Checker)(Cube);
+typedef uint64_t (*Indexer)(Cube);
+typedef bool (*Moveset)(Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cachedata
+{
+ char * filename;
+ CacheNode ** ctable;
+ bool generated;
+ uint64_t maxind0;
+ int len;
+ int nind;
+ Indexer index[20]; /* Should be enough */
+ Checker check;
+ Moveset moveset;
+};
+
+struct
+cachenode
+{
+ uint64_t * indexval;
+ Alg * sol;
+ CacheNode * next;
+};
+
+struct
+cube
+{
+ uint16_t epose;
+ uint16_t eposs;
+ uint16_t eposm;
+ uint16_t eofb;
+ uint16_t eorl;
+ uint16_t eoud;
+ uint16_t cp;
+ uint16_t coud;
+ uint16_t cofb;
+ uint16_t corl;
+ uint16_t cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ uint64_t size;
+ Indexer index;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+ Trans pre_trans;
+};
+
+struct
+step
+{
+ Checker check;
+ Checker ready;
+ Moveset moveset;
+ CacheData * cd;
+};
diff --git a/old/2021-06-17-cachedata/main.c b/old/2021-06-17-cachedata/main.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 3; /*, nrand = 100000, sum1, sum2;*/
+
+ Step *stps[20] = {&eofb_HTM, &corners_URF, &optimal_HTM};
+ char sss[30][30] = {"EO on F/B", "Corners URF", "Optimal solution"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ .pre_trans = uf
+ };
+
+ init();
+
+/*
+ print_ptable(&pd_corners_HTM);
+
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ for (i = 0; i < nrand; i++) {
+ sum1 += optimal_HTM.check(random_cube());
+ sum2 += corners_HTM.check(random_cube());
+ }
+ printf("Average optimal pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners pruning: %lf\n", ((double)sum2) / ((double) nrand));
+*/
+
+ print_cachedata(&cd_optimal_HTM_6, false);
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ for (i = 0; i < ns; i++) {
+ if (stps[i]->check == NULL)
+ fprintf(stderr, "Check function for step %d is null\n", i);
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-06-17-cachedata/steps.c b/old/2021-06-17-cachedata/steps.c
@@ -0,0 +1,327 @@
+#include "steps.h"
+
+/* Check functions ***********************************************************/
+
+static int check_nothing(Cube cube);
+static int check_eofb_HTM(Cube cube);
+static int check_coud_HTM(Cube cube);
+static int check_coud_URF(Cube cube);
+static int check_corners_HTM(Cube cube);
+static int check_corners_URF(Cube cube);
+static int check_edges_HTM(Cube cube);
+static int check_drud_HTM(Cube cube);
+static int check_optimal_HTM(Cube cube);
+
+
+/* Index functions ***********************************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_ep(Cube cube);
+static uint64_t index_drud(Cube cube);
+
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .check = check_eofb_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .check = check_coud_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .check = check_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .check = check_corners_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM
+};
+
+Step
+corners_URF = {
+ .check = check_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+edges_HTM = {
+ .check = check_edges_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM
+};
+
+Step
+drud_HTM = {
+ .check = check_drud_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .check = check_optimal_HTM,
+ .ready = check_nothing,
+ .moveset = moveset_HTM,
+ .cd = &cd_optimal_HTM_6
+};
+
+
+/* Cache solutions ***********************************************************/
+
+CacheData
+cd_optimal_HTM_6 = {
+ .filename = "cd_optimal_HTM_6",
+ .maxind0 = POW3TO7 * FACTORIAL8,
+ .len = 6,
+ .nind = 3,
+ .index = { index_corners, index_eofb, index_ep },
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .size = POW2TO11,
+ .index = index_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .size = POW3TO7,
+ .index = index_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .size = POW3TO7 * FACTORIAL8,
+ .index = index_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_ep_HTM = {
+ .filename = "ptable_ep_HTM",
+ .size = FACTORIAL12,
+ .index = index_ep,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .size = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .index = index_drud,
+ .moveset = moveset_HTM
+};
+
+
+/* Check functions implementation ********************************************/
+
+static int
+check_nothing(Cube cube)
+{
+ /* At least we check that it is admissible */
+ return is_admissible(cube);
+}
+
+static int
+check_eofb_HTM(Cube cube)
+{
+ if (!pd_eofb_HTM.generated)
+ generate_ptable(&pd_eofb_HTM);
+
+ return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb);
+}
+
+static int
+check_coud_HTM(Cube cube)
+{
+ if (!pd_coud_HTM.generated)
+ generate_ptable(&pd_coud_HTM);
+
+ return PTABLEVAL(pd_coud_HTM.ptable, cube.coud);
+}
+
+static int
+check_coud_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ int ud = check_coud_HTM(cube);
+ int rl = check_coud_HTM(apply_move(z, cube));
+ int fb = check_coud_HTM(apply_move(x, cube));
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+check_corners_HTM(Cube cube)
+{
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+
+ return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+}
+
+static int
+check_corners_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ ret = MIN(ret,check_corners_HTM(apply_alg(trans_alg(i),cube)));
+
+ return ret;
+}
+
+static int
+check_edges_HTM(Cube cube)
+{
+ int ret = 0;
+
+ if (!pd_ep_HTM.generated)
+ generate_ptable(&pd_ep_HTM);
+
+ ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)));
+ ret = MAX(ret, check_eofb_HTM(cube));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube)));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube)));
+
+ return ret;
+}
+
+static int
+check_drud_HTM(Cube cube)
+{
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+
+ return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+}
+
+static int
+check_optimal_HTM(Cube cube)
+{
+ int dr1, dr2, dr3, drmax, cor; /*ep;*/
+
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+ /*
+ *if (!pd_ep_HTM.generated)
+ * generate_ptable(&pd_ep_HTM);
+ */
+
+ dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+ dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube)));
+ dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube)));
+
+ drmax = MAX(dr1, MAX(dr2, dr3));
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ drmax++;
+
+ cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+ /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */
+
+ /*return MAX(drmax, MAX(ep, cor));*/
+ if (drmax == 0 && cor == 0)
+ return is_solved(cube, false) ? 0 : 1;
+ return MAX(drmax, cor);
+}
+
+
+/* Index functions implementation ********************************************/
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_ep(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eposs;
+ b = (cube.epose % FACTORIAL4) + epos_dependent_cube(cube)*FACTORIAL4;
+ c = cube.eposm % FACTORIAL4;
+
+ b *= FACTORIAL4 * BINOM12ON4;
+ c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
diff --git a/old/2021-06-17-cachedata/steps.h b/old/2021-06-17-cachedata/steps.h
@@ -0,0 +1,34 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+/* Steps *********************************************************************/
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step corners_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+/* Cache solutions ***********************************************************/
+
+extern CacheData cd_optimal_HTM_6;
+
+/* Pruning tables ************************************************************/
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
+
+/* Movesets ******************************************************************/
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+#endif
diff --git a/old/2021-06-23-realizing-bad-tables/HERE b/old/2021-06-23-realizing-bad-tables/HERE
@@ -0,0 +1 @@
+Here you can also find some tables data that I have removed, like ep and tripleeo
diff --git a/old/2021-06-23-realizing-bad-tables/cube.c b/old/2021-06-23-realizing-bad-tables/cube.c
@@ -0,0 +1,2844 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Checker f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static bool ptable_has_reached(PruneData *pd, uint64_t ind);
+static void ptable_set_reached(PruneData *pd, uint64_t ind);
+static void ptable_update(PruneData *pd, uint64_t ind, int m);
+static void realloc_alg(Alg *alg, int n);
+static bool read_algset_file(AlgSet *as);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_algset_file(AlgSet *as);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_ttables_file();
+
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_cosets_bfs(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_trans();
+static void init_trans_aux();
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_cosets[FACTORIAL8];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * trans_algs[NROTATIONS];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; (m=moves[i]) != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+
+ if (dd->current_alg->len == 0 ||
+ (s.check(apply_move(inverse_move(l1), (Cube){0}), 1))) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.check(c, dd->d - dd->current_alg->len);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ uint64_t ind = pd->index(c);
+ int oldval = ptableval(pd, ind);
+
+ if (oldval < dd->m || ptable_has_reached(pd, ind) || pd->n == pd->size)
+ return;
+
+ ptable_set_reached(pd, ind);
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, ind, dd->m);
+ return;
+ }
+
+ genptable_dfs_branch(c, pd, dd);
+}
+
+static void
+genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd)
+{
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ dd->m++;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+
+ genptable_dfs(apply_move(move, c), pd, dd);
+
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Checker f, Move *r)
+{
+ Cube c;
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ c = apply_move(i, (Cube){0});
+ if (f != NULL && f(c, 1))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static bool
+ptable_has_reached(PruneData *pd, uint64_t ind)
+{
+ return (ind % 2) ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16;
+}
+
+static void
+ptable_set_reached(PruneData *pd, uint64_t ind)
+{
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = (ind % 2) ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_algset_file(AlgSet *as)
+{
+ return false;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Alg *inv;
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Cube ret;
+
+ if (r != mirror) {
+ ret = apply_alg_generic(trans_algs[r], c, f, true);
+ inv = on_inverse(trans_algs[r]);
+ ret = apply_alg_generic(inv, ret, f, true);
+ free_alg(inv);
+ } else {
+ ret = move_via_arrays(&ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(&ma, ret, f);
+ }
+
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_algset_file(AlgSet *as)
+{
+ return false;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the cosets for cphtr.
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0;
+
+ for (i = 0; i < FACTORIAL8; i++)
+ cphtr_cosets[i] = -1;
+
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_cosets[i] == -1)
+ init_cphtr_cosets_bfs(i, c++);
+}
+
+static void
+init_cphtr_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_cosets[jj] == -1) {
+ cphtr_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ unsigned int ui;
+ bool b1, b2, b3;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(trans_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ memcpy(eparr, ep_mirror, 12 * sizeof(int));
+ memcpy(cparr, cp_mirror, 8 * sizeof(int));
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(trans_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_ttable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_ttable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_ttable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_ttable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ trans_algs[uf] = new_alg("");
+ trans_algs[ur] = new_alg("y");
+ trans_algs[ub] = new_alg("y2");
+ trans_algs[ul] = new_alg("y3");
+
+ trans_algs[df] = new_alg("z2");
+ trans_algs[dr] = new_alg("y z2");
+ trans_algs[db] = new_alg("x2");
+ trans_algs[dl] = new_alg("y3 z2");
+
+ trans_algs[rf] = new_alg("z3");
+ trans_algs[rd] = new_alg("z3 y");
+ trans_algs[rb] = new_alg("z3 y2");
+ trans_algs[ru] = new_alg("z3 y3");
+
+ trans_algs[lf] = new_alg("z");
+ trans_algs[ld] = new_alg("z y3");
+ trans_algs[lb] = new_alg("z y2");
+ trans_algs[lu] = new_alg("z y");
+
+ trans_algs[fu] = new_alg("x y2");
+ trans_algs[fr] = new_alg("x y");
+ trans_algs[fd] = new_alg("x");
+ trans_algs[fl] = new_alg("x y3");
+
+ trans_algs[bu] = new_alg("x3");
+ trans_algs[br] = new_alg("x3 y");
+ trans_algs[bd] = new_alg("x3 y2");
+ trans_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_cosets[cube.cp];
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genalgset(AlgSet *as)
+{
+ uint64_t i;
+
+ if (as->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ as->table = malloc(as->size * sizeof(AlgList *));
+
+ if (read_algset_file(as)) {
+ as->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", as->filename);
+
+ for (i = 0; i < as->size; i++) {
+ as->table[i] = solve(as->antindex(i), as->step, &as->opts);
+ fprintf(stderr, "Generated %lu / %lu cases\n", i, as->size);
+ }
+
+ if (!write_algset_file(as))
+ fprintf(stderr, "Error writing algset file\n");
+
+ as->generated = true;
+}
+
+void
+genptable(PruneData *pd)
+{
+ uint64_t j;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->size; j++)
+ ptable_update(pd, j, 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) {
+ memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t));
+ genptable_dfs((Cube){0}, pd, &dd);
+ fprintf(stderr, "Depth %d completed, generated %lu/%lu\n",
+ dd.d, pd->n, pd->size);
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ Trans i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(trans_algs[i],cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c = apply_trans(opts->pre_trans, cube);
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && step.ready(c, 1) != 0) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.check, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(c, step, opts, &dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[opts->pre_trans], node->alg);
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, alg->move[i], alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->size; i++)
+ a[ptableval(pd, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->size + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, uint64_t ind)
+{
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+
+Alg *
+trans_alg(Trans i)
+{
+ return trans_algs[i];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+}
+
diff --git a/old/2021-06-23-realizing-bad-tables/cube.h b/old/2021-06-23-realizing-bad-tables/cube.h
@@ -0,0 +1,88 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+
+/* Type definitions **********************************************************/
+
+#include "cubetypes.h"
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t cphtr(Cube cube);
+uint64_t epos_dependent(Cube cube);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * trans_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void genalgset(AlgSet *as);
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, uint64_t ind);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-23-realizing-bad-tables/cubetypes.h b/old/2021-06-23-realizing-bad-tables/cubetypes.h
@@ -0,0 +1,224 @@
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct algset AlgSet;
+typedef struct block Block;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef int (*Checker) (Cube, int);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ uint64_t size;
+ Indexer index;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+ Trans pre_trans;
+};
+
+struct
+step
+{
+ Checker check;
+ Checker ready;
+ Moveset moveset;
+};
+
+/* TODO: I put it here because it needs the definition of Step, sort it out */
+struct
+algset
+{
+ char * filename;
+ AlgList ** table;
+ bool generated;
+ uint64_t size;
+ Indexer index;
+ AntiIndexer antindex;
+ Step step;
+ SolveOptions opts;
+};
diff --git a/old/2021-06-23-realizing-bad-tables/main.c b/old/2021-06-23-realizing-bad-tables/main.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 3;
+/* int nrand = 10000, sum1, sum2, sum3;*/
+
+ Step *stps[20] = {&corners_HTM, &eofb_HTM, &optimal_HTM};
+ char sss[30][30] = {"Corners", "EO on F/B", "Optimal solution"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ .pre_trans = uf
+ };
+
+ init();
+
+ print_ptable(&pd_corners_HTM);
+
+/*
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ sum3 = 0;
+ for (i = 0; i < nrand; i++) {
+ cube = random_cube();
+ sum1 += corners_HTM.check(cube, 20);
+ sum2 += cornershtr_HTM.check(cube, 20);
+ sum3 += optimal_HTM.check(cube, 20);
+ }
+ printf("Average corners pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand));
+ printf("Average optimal pruning: %lf\n", ((double)sum3) / ((double) nrand));
+*/
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ print_cube(cube);
+ printf("Index: %lu\n", pd_cornershtr_HTM.index(cube));
+
+ for (i = 0; i < ns; i++) {
+ if (stps[i]->check == NULL)
+ fprintf(stderr, "Check function for step %d is null\n", i);
+ printf("Check: %d\n", stps[i]->check(cube, 20));
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free_alg(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-06-23-realizing-bad-tables/steps.c b/old/2021-06-23-realizing-bad-tables/steps.c
@@ -0,0 +1,486 @@
+#include "steps.h"
+
+/* Check functions ***********************************************************/
+
+static int check_nothing(Cube cube, int target);
+static int check_centers(Cube cube, int target);
+static int check_eofb_HTM(Cube cube, int target);
+static int check_coud_HTM(Cube cube, int target);
+static int check_coud_URF(Cube cube, int target);
+static int check_corners_HTM(Cube cube, int target);
+static int check_cornershtr_HTM(Cube cube, int target);
+static int check_corners_URF(Cube cube, int target);
+static int check_cornershtr_URF(Cube cube, int target);
+static int check_ep_HTM(Cube cube, int target);
+static int check_edges_HTM(Cube cube, int target);
+static int check_drud_HTM(Cube cube, int target);
+static int check_optimal_HTM(Cube cube, int target);
+static int check_corners6eo_HTM(Cube cube, int target);
+static int check_tripleeo_HTM(Cube cube, int target);
+
+
+/* Index functions ***********************************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_ep(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_corners6eo(Cube cube);
+static uint64_t index_tripleeo(Cube cube);
+
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .check = check_eofb_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .check = check_coud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .check = check_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .check = check_corners_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_HTM = {
+ .check = check_cornershtr_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_URF = {
+ .check = check_cornershtr_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_URF = {
+ .check = check_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+edges_HTM = {
+ .check = check_edges_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+drud_HTM = {
+ .check = check_drud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .check = check_optimal_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+tripleeo_HTM = {
+ .check = check_tripleeo_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+
+/* Blocks ********************************************************************/
+
+Block
+block_223dl = {
+ .edge = { [DF] = 1, [DL] = 1, [DB] = 1, [FL] = 1, [BL] = 1 },
+ .corner = { [DFL] = 1, [DBL] = 1 },
+ .center = { [D_center] = 1, [L_center] = 1 }
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .size = POW2TO11,
+ .index = index_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .size = POW3TO7,
+ .index = index_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "ptable_cornershtr_HTM",
+ .size = POW3TO7 * BINOM8ON4 * 6, /*TODO: check */
+ .index = index_cornershtr,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .size = POW3TO7 * FACTORIAL8,
+ .index = index_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_ep_HTM = {
+ .filename = "ptable_ep_HTM",
+ .size = FACTORIAL12,
+ .index = index_ep,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .size = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .index = index_drud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners6eo_HTM = {
+ .filename = "ptable_corners6eo_HTM",
+ .size = POW2TO6 * POW3TO7 * FACTORIAL8,
+ .index = index_corners6eo,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_tripleeo_HTM = {
+ .filename = "ptable_tripleeo_HTM",
+ .size = BINOM12ON4 * BINOM8ON4 * POW2TO11,
+ .index = index_tripleeo,
+ .moveset = moveset_HTM
+};
+
+
+/* Alg sets ******************************************************************/
+
+/* TODO: remove this (I am only keeping it as an example for future algesets)
+AlgSet
+as_ephtr_HTM = {
+ .filename = "algset_ephtr_HTM",
+ .size = FACTORIAL4 * FACTORIAL4 * FACTORIAL4,
+ .index = index_ephtr,
+ .antindex = index_to_cube_ephtr,
+ .step = (Step) {
+ .check = check_optimal_HTM,
+ .ready = check_htr_solved_corners_HTM,
+ .moveset = moveset_HTM
+ },
+ .opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .max_solutions = 1,
+ .optimal_only = true,
+ .can_niss = false,
+ .feedback = true,
+ .pre_trans = uf
+ }
+};
+*/
+
+
+/* Check functions implementation ********************************************/
+
+static int
+check_nothing(Cube cube, int target)
+{
+ return 0;
+}
+
+static int
+check_centers(Cube cube, int target)
+{
+ return (cube.cpos == 0) ? 0 : 1;
+}
+
+static int
+check_eofb_HTM(Cube cube, int target)
+{
+ if (!pd_eofb_HTM.generated)
+ genptable(&pd_eofb_HTM);
+
+ return ptableval(&pd_eofb_HTM, cube.eofb);
+}
+
+static int
+check_coud_HTM(Cube cube, int target)
+{
+ if (!pd_coud_HTM.generated)
+ genptable(&pd_coud_HTM);
+
+ return ptableval(&pd_coud_HTM, cube.coud);
+}
+
+static int
+check_coud_URF(Cube cube, int target)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ int ud = check_coud_HTM(cube, target);
+ int rl = check_coud_HTM(apply_move(z, cube), target);
+ int fb = check_coud_HTM(apply_move(x, cube), target);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+check_corners_HTM(Cube cube, int target)
+{
+ if (!pd_corners_HTM.generated)
+ genptable(&pd_corners_HTM);
+
+ return ptableval(&pd_corners_HTM, index_corners(cube));
+}
+
+static int
+check_cornershtr_HTM(Cube cube, int target)
+{
+ if (!pd_cornershtr_HTM.generated)
+ genptable(&pd_cornershtr_HTM);
+
+ return ptableval(&pd_cornershtr_HTM, index_cornershtr(cube));
+}
+
+static int
+check_cornershtr_URF(Cube cube, int target)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ c = check_cornershtr_HTM(apply_alg(trans_alg(i),cube), target);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+check_corners_URF(Cube cube, int target)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ ret = MIN(ret, check_corners_HTM(
+ apply_alg(trans_alg(i), cube), target));
+
+ return ret;
+}
+
+static int
+check_ep_HTM(Cube cube, int target)
+{
+ if (!pd_ep_HTM.generated)
+ genptable(&pd_ep_HTM);
+
+ return ptableval(&pd_ep_HTM, index_ep(cube));
+}
+
+static int
+check_edges_HTM(Cube cube, int target)
+{
+ return MAX(check_ep_HTM(cube, target),
+ check_tripleeo_HTM(cube, target));
+}
+
+static int
+check_drud_HTM(Cube cube, int target)
+{
+ if (!pd_drud_HTM.generated)
+ genptable(&pd_drud_HTM);
+
+ return ptableval(&pd_drud_HTM, index_drud(cube));
+}
+
+static int
+check_optimal_HTM(Cube cube, int target)
+{
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube2, cube3;
+
+ dr1 = check_drud_HTM(cube, target);
+ /*cor = check_corners6eo_HTM(cube, target);*/
+ cor = check_corners_HTM(cube, target);
+ ret = MAX(dr1, cor);
+
+ if (ret > target)
+ return ret;
+
+ cube2 = apply_trans(rf, cube);
+ dr2 = check_drud_HTM(cube2, target);
+ /*cor = check_corners6eo_HTM(cube2, target);
+ ret = MAX(ret, MAX(dr2, cor));*/
+ ret = MAX(ret, dr2);
+
+ if (ret > target)
+ return ret;
+
+ cube3 = apply_trans(bd, cube);
+ dr3 = check_drud_HTM(cube3, target);
+ /*cor = check_corners6eo_HTM(cube3, target);*/
+
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ /*ret = MAX(ret, MAX(dr3, cor));*/
+ ret = MAX(ret, dr3);
+
+ if (ret == 0)
+ return (!cube.epose && !cube.eposs && !cube.eposm) ? 0 : 6;
+
+ return ret;
+}
+
+static int
+check_corners6eo_HTM(Cube cube, int target)
+{
+ if (!pd_corners6eo_HTM.generated)
+ genptable(&pd_corners6eo_HTM);
+
+ return ptableval(&pd_corners6eo_HTM, index_corners6eo(cube));
+}
+
+static int
+check_tripleeo_HTM(Cube cube, int target)
+{
+ if (!pd_tripleeo_HTM.generated)
+ genptable(&pd_tripleeo_HTM);
+
+ return ptableval(&pd_tripleeo_HTM, index_tripleeo(cube));
+}
+
+
+/* Index functions implementation ********************************************/
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_ep(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.epose;
+ b = (cube.eposs % FACTORIAL4) + epos_dependent(cube)*FACTORIAL4;
+ c = cube.eposm % FACTORIAL4;
+
+ b *= FACTORIAL4 * BINOM12ON4;
+ c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_corners6eo(Cube cube)
+{
+ return (cube.coud*FACTORIAL8 + cube.cp)*POW2TO6 + (cube.eofb%POW2TO6);
+}
+
+static uint64_t
+index_tripleeo(Cube cube)
+{
+ uint64_t ee, es;
+
+ ee = cube.epose / FACTORIAL4;
+ es = epos_dependent(cube);
+
+ return (ee * BINOM8ON4 + es) * POW2TO11 + cube.eofb;
+}
+
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
diff --git a/old/2021-06-23-realizing-bad-tables/steps.h b/old/2021-06-23-realizing-bad-tables/steps.h
@@ -0,0 +1,42 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+/* Steps *********************************************************************/
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step cornershtr_HTM;
+extern Step corners_URF;
+extern Step cornershtr_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+extern Step tripleeo_HTM;
+
+/* Blocks ********************************************************************/
+
+extern Block block_223dl;
+
+/* Pruning tables ************************************************************/
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
+extern PruneData pd_tripleeo_HTM;
+
+/* Alg sets ******************************************************************/
+
+
+/* Movesets ******************************************************************/
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+#endif
diff --git a/old/2021-06-23-uint16t/cube.c b/old/2021-06-23-uint16t/cube.c
@@ -0,0 +1,2762 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static uint16_t array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent(int pos1, int pos2);
+static uint16_t epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Checker f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static bool ptable_has_reached(PruneData *pd, uint64_t ind);
+static void ptable_set_reached(PruneData *pd, uint64_t ind);
+static void ptable_update(PruneData *pd, uint64_t ind, int m);
+static void realloc_alg(Alg *alg, int n);
+static bool read_algset_file(AlgSet *as);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_algset_file(AlgSet *as);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_ttables_file();
+
+static void init_auxtables();
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_trans();
+static void init_trans_aux();
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static uint16_t eo_ttable[NTRANS][POW2TO11];
+static uint16_t cp_ttable[NTRANS][FACTORIAL8];
+static uint16_t co_ttable[NTRANS][POW3TO7];
+static uint16_t cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static uint16_t eofb_mtable[NMOVES][POW2TO11];
+static uint16_t eorl_mtable[NMOVES][POW2TO11];
+static uint16_t eoud_mtable[NMOVES][POW2TO11];
+static uint16_t cp_mtable[NMOVES][FACTORIAL8];
+static uint16_t coud_mtable[NMOVES][POW3TO7];
+static uint16_t cofb_mtable[NMOVES][POW3TO7];
+static uint16_t corl_mtable[NMOVES][POW3TO7];
+static uint16_t cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * trans_algs[NROTATIONS];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static uint16_t
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; (m=moves[i]) != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+
+ if (dd->current_alg->len == 0 ||
+ (s.check(apply_move(inverse_move(l1), (Cube){0}), 1))) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.check(c, dd->d - dd->current_alg->len);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static uint16_t
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(uint16_t epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ uint64_t ind = pd->index(c);
+ int oldval = ptableval(pd, ind);
+
+ if (oldval < dd->m || ptable_has_reached(pd, ind) || pd->n == pd->size)
+ return;
+
+ ptable_set_reached(pd, ind);
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, ind, dd->m);
+ return;
+ }
+
+ genptable_dfs_branch(c, pd, dd);
+}
+
+static void
+genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd)
+{
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ dd->m++;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+
+ genptable_dfs(apply_move(move, c), pd, dd);
+
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Checker f, Move *r)
+{
+ Cube c;
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ c = apply_move(i, (Cube){0});
+ if (f != NULL && f(c, 1))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static bool
+ptable_has_reached(PruneData *pd, uint64_t ind)
+{
+ return ind % 2 ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16;
+}
+
+static void
+ptable_set_reached(PruneData *pd, uint64_t ind)
+{
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = ind % 2 ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_algset_file(AlgSet *as)
+{
+ return false;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(uint16_t);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ Alg *inv;
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Cube ret;
+
+ if (r != mirror) {
+ ret = apply_alg_generic(trans_algs[r], c, f, true);
+ inv = on_inverse(trans_algs[r]);
+ ret = apply_alg_generic(inv, ret, f, true);
+ free_alg(inv);
+ } else {
+ ret = move_via_arrays(&ma, (Cube){0}, f);
+ ret = compose_filtered(c, ret, f);
+ ret = move_via_arrays(&ma, ret, f);
+ }
+
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_algset_file(AlgSet *as)
+{
+ return false;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(uint16_t);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(uint16_t);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj] = epos_dependent(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[mirror] = mirror;
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ uint16_t ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_trans() {
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int eparr[12], eoarr[12];
+ int cparr[8], coarr[8];
+ int i;
+ bool b1, b2, b3;
+ uint16_t ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ if (i == mirror)
+ cube = (Cube){0};
+ else
+ cube = apply_alg(trans_algs[i], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ if (m == mirror) {
+ memcpy(eparr, ep_mirror, 12 * sizeof(int));
+ memcpy(cparr, cp_mirror, 8 * sizeof(int));
+ } else {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(trans_algs[m], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m == mirror)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ if (m == mirror) {
+ b1 = (mi >= U && mi <= Bw3);
+ b2 = (mi >= S && mi <= E3);
+ b3 = (mi >= x && mi <= z3);
+ if (b1 || b2 || b3)
+ moves_ttable[m][mi] =
+ inverse_move_aux[mi];
+ else
+ moves_ttable[m][mi] = mi;
+
+ if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3)
+ moves_ttable[m][mi] += 3;
+ if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3)
+ moves_ttable[m][mi] -= 3;
+ } else {
+ aux = apply_trans(m, apply_move(mi,(Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(
+ inverse_move_aux[move], aux);
+ if (is_solved(cube, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ trans_algs[uf] = new_alg("");
+ trans_algs[ur] = new_alg("y");
+ trans_algs[ub] = new_alg("y2");
+ trans_algs[ul] = new_alg("y3");
+
+ trans_algs[df] = new_alg("z2");
+ trans_algs[dr] = new_alg("y z2");
+ trans_algs[db] = new_alg("x2");
+ trans_algs[dl] = new_alg("y3 z2");
+
+ trans_algs[rf] = new_alg("z3");
+ trans_algs[rd] = new_alg("z3 y");
+ trans_algs[rb] = new_alg("z3 y2");
+ trans_algs[ru] = new_alg("z3 y3");
+
+ trans_algs[lf] = new_alg("z");
+ trans_algs[ld] = new_alg("z y3");
+ trans_algs[lb] = new_alg("z y2");
+ trans_algs[lu] = new_alg("z y");
+
+ trans_algs[fu] = new_alg("x y2");
+ trans_algs[fr] = new_alg("x y");
+ trans_algs[fd] = new_alg("x");
+ trans_algs[fl] = new_alg("x y3");
+
+ trans_algs[bu] = new_alg("x3");
+ trans_algs[br] = new_alg("x3 y");
+ trans_algs[bd] = new_alg("x3 y2");
+ trans_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+uint64_t
+epos_dependent_cube(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genalgset(AlgSet *as)
+{
+ uint64_t i;
+
+ if (as->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ as->table = malloc(as->size * sizeof(AlgList *));
+
+ if (read_algset_file(as)) {
+ as->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", as->filename);
+
+ for (i = 0; i < as->size; i++) {
+ as->table[i] = solve(as->antindex(i), as->step, &as->opts);
+ fprintf(stderr, "Generated %lu / %lu cases\n", i, as->size);
+ }
+
+ if (!write_algset_file(as))
+ fprintf(stderr, "Error writing algset file\n");
+
+ as->generated = true;
+}
+
+void
+genptable(PruneData *pd)
+{
+ uint64_t j;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->size; j++)
+ ptable_update(pd, j, 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) {
+ memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t));
+ genptable_dfs((Cube){0}, pd, &dd);
+ fprintf(stderr, "Depth %d completed, generated %lu/%lu\n",
+ dd.d, pd->n, pd->size);
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ Trans i;
+
+ if (reorient)
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(trans_algs[i],cube), false))
+ return true;
+
+ return equal(cube, (Cube){0});
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b;
+ uint16_t x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c = apply_trans(opts->pre_trans, cube);
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && step.ready(c, 1) != 0) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.check, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(c, step, opts, &dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans_aux[opts->pre_trans], node->alg);
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, alg->move[i], alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->size; i++)
+ a[ptableval(pd, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->size + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, uint64_t ind)
+{
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+
+Alg *
+trans_alg(Trans i)
+{
+ return trans_algs[i];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_trans_aux();
+ init_trans();
+}
+
diff --git a/old/2021-06-23-uint16t/cube.h b/old/2021-06-23-uint16t/cube.h
@@ -0,0 +1,87 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Constants and macros *****************************************************/
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS (mirror+1)
+#define NROTATIONS (NTRANS-1)
+
+/* Type definitions **********************************************************/
+
+#include "cubetypes.h"
+
+/* Public functions **********************************************************/
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t epos_dependent_cube(Cube cube);
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * trans_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void genalgset(AlgSet *as);
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, uint64_t ind);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-23-uint16t/cubetypes.h b/old/2021-06-23-uint16t/cubetypes.h
@@ -0,0 +1,224 @@
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct algset AlgSet;
+typedef struct block Block;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef int (*Checker) (Cube, int);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ mirror, /* R|L */
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+cube
+{
+ uint16_t epose;
+ uint16_t eposs;
+ uint16_t eposm;
+ uint16_t eofb;
+ uint16_t eorl;
+ uint16_t eoud;
+ uint16_t cp;
+ uint16_t coud;
+ uint16_t cofb;
+ uint16_t corl;
+ uint16_t cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ uint64_t size;
+ Indexer index;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+ Trans pre_trans;
+};
+
+struct
+step
+{
+ Checker check;
+ Checker ready;
+ Moveset moveset;
+};
+
+/* TODO: I put it here because it needs the definition of Step, sort it out */
+struct
+algset
+{
+ char * filename;
+ AlgList ** table;
+ bool generated;
+ uint64_t size;
+ Indexer index;
+ AntiIndexer antindex;
+ Step step;
+ SolveOptions opts;
+};
diff --git a/old/2021-06-30-noreached/cube.c b/old/2021-06-30-noreached/cube.c
@@ -0,0 +1,3394 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd);
+static void gensym(SymData *sd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_symdata();
+static void init_trans();
+static void init_trans_aux();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Estimator f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static void realloc_alg(Alg *alg, int n);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_symdata_file(SymData *sd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_symdata_file(SymData *sd);
+static bool write_ttables_file();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * rotation_algs[NROTATIONS];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 2;
+static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 };
+
+/* Coordinates and their implementation **************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430
+};
+
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+
+/* TODO: rename */
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ return admissible_ee_aux[ind];
+}
+
+/* TODO: rename */
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.eofb = ind % POW2TO11;
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4;
+
+ return c;
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+
+/* Checkers ******************************************************************/
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Local functions implementation ********************************************/
+
+/* TODO: this should be an anti index (maybe?) */
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s.estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ int oldval = ptableval(pd, c);
+
+ if (oldval < dd->m || pd->n == pd->coord->max ||
+ (dd->m != 0 && pd->coord->check(c)) )
+ return;
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, c, dd->m);
+ return;
+ }
+
+ genptable_dfs_branch(c, pd, dd);
+}
+
+static void
+genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd)
+{
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ dd->m++;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+
+ genptable_dfs(apply_move(move, c), pd, dd);
+
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Estimator f, Move *r)
+{
+ CubeTarget ct = { .target = 1 };
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ ct.cube = apply_move(i, (Cube){0});
+ if (f != NULL && f(ct))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]);
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[uf_mirror] = uf_mirror;
+ inverse_trans_aux[ur_mirror] = ur_mirror;
+ inverse_trans_aux[ul_mirror] = ul_mirror;
+ inverse_trans_aux[ub_mirror] = ub_mirror;
+
+ inverse_trans_aux[df_mirror] = df_mirror;
+ inverse_trans_aux[dr_mirror] = dl_mirror;
+ inverse_trans_aux[dl_mirror] = dr_mirror;
+ inverse_trans_aux[db_mirror] = db_mirror;
+
+ inverse_trans_aux[rf_mirror] = rf_mirror;
+ inverse_trans_aux[rd_mirror] = br_mirror;
+ inverse_trans_aux[rb_mirror] = lb_mirror;
+ inverse_trans_aux[ru_mirror] = fl_mirror;
+
+ inverse_trans_aux[lf_mirror] = lf_mirror;
+ inverse_trans_aux[ld_mirror] = bl_mirror;
+ inverse_trans_aux[lb_mirror] = rb_mirror;
+ inverse_trans_aux[lu_mirror] = fr_mirror;
+
+ inverse_trans_aux[fu_mirror] = fu_mirror;
+ inverse_trans_aux[fr_mirror] = lu_mirror;
+ inverse_trans_aux[fd_mirror] = bu_mirror;
+ inverse_trans_aux[fl_mirror] = ru_mirror;
+
+ inverse_trans_aux[bu_mirror] = fd_mirror;
+ inverse_trans_aux[br_mirror] = rd_mirror;
+ inverse_trans_aux[bd_mirror] = bd_mirror;
+ inverse_trans_aux[bl_mirror] = ld_mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+static void
+init_trans() {
+ Cube aux, cube, mirr, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move_aux[move], aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube, false) ||
+ is_solved(mirr, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ rotation_algs[uf] = new_alg("");
+ rotation_algs[ur] = new_alg("y");
+ rotation_algs[ub] = new_alg("y2");
+ rotation_algs[ul] = new_alg("y3");
+
+ rotation_algs[df] = new_alg("z2");
+ rotation_algs[dr] = new_alg("y z2");
+ rotation_algs[db] = new_alg("x2");
+ rotation_algs[dl] = new_alg("y3 z2");
+
+ rotation_algs[rf] = new_alg("z3");
+ rotation_algs[rd] = new_alg("z3 y");
+ rotation_algs[rb] = new_alg("z3 y2");
+ rotation_algs[ru] = new_alg("z3 y3");
+
+ rotation_algs[lf] = new_alg("z");
+ rotation_algs[ld] = new_alg("z y3");
+ rotation_algs[lb] = new_alg("z y2");
+ rotation_algs[lu] = new_alg("z y");
+
+ rotation_algs[fu] = new_alg("x y2");
+ rotation_algs[fr] = new_alg("x y");
+ rotation_algs[fd] = new_alg("x");
+ rotation_algs[fl] = new_alg("x y3");
+
+ rotation_algs[bu] = new_alg("x3");
+ rotation_algs[br] = new_alg("x3 y");
+ rotation_algs[bd] = new_alg("x3 y2");
+ rotation_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genptable(PruneData *pd)
+{
+ Cube cube;
+ uint64_t j, oldn = 0;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update(pd, pd->coord->cube(j), 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) {
+ for (j = 0; j < pd->coord->max; j++) {
+ cube = pd->coord->cube(j);
+ if (pd->coord->check(cube))
+ genptable_dfs(cube, pd, &dd);
+ }
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ dd.d, pd->n-oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+/* TODO: clean pre_trans or put it back */
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ /*AlgListNode *node;*/
+ AlgList *sols = new_alglist();
+ /*Cube c = apply_trans(opts->pre_trans, cube);*/
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(cube)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.estimate, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(cube, step, opts, &dd);
+ }
+
+/*
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(opts->pre_trans), node->alg);
+*/
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval(pd, pd->coord->cube(i))]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ uint64_t ind = pd->coord->index(cube);
+
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+
+Alg *
+rotation_alg(Trans i)
+{
+ return rotation_algs[i % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+ init_symdata();
+}
+
diff --git a/old/2021-06-30-noreached/cube.h b/old/2021-06-30-noreached/cube.h
@@ -0,0 +1,90 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "macros.h"
+#include "cubetypes.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_drud;
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_khuge;
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */
+Cube anti_cphtr(uint64_t ind); /*TODO also this */
+uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-30-noreached/cubetypes.h b/old/2021-06-30-noreached/cubetypes.h
@@ -0,0 +1,250 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ Checker check;
+ uint64_t max;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ Estimator estimate;
+ Checker ready;
+ Moveset moveset;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/old/2021-06-30-noreached/macros.h b/old/2021-06-30-noreached/macros.h
@@ -0,0 +1,23 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS 48
+#define NROTATIONS 24
+
+#endif
diff --git a/old/2021-06-30-noreached/main.c b/old/2021-06-30-noreached/main.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 2;
+/* Move m;*/
+/* int nrand = 10000, sum1, sum2, sum3;*/
+
+ Step *stps[20] = {&drud_HTM, &optimal_HTM};
+ char sss[30][30] = {"DR on UD", "Optimal solve"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ };
+
+ init();
+
+/*
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ sum3 = 0;
+ for (i = 0; i < nrand; i++) {
+ cube = random_cube();
+ sum1 += drud_HTM.check(cube, 20);
+ sum2 += optimal_HTM.check(cube, 20);
+ sum3 += cornershtreofb_HTM.check(cube, 20);
+ }
+ printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand));
+ printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand));
+*/
+
+/*
+ for (m = U; m <= B3; m++) {
+ printf("Class eofbepos after %d: ", m);
+ printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0})));
+ }
+*/
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ print_alg(inverse_alg(algo), false);
+/*
+ printf("After rb_mirror:\n");
+ cube = apply_trans(rb_mirror, cube);
+ print_cube(cube);
+ printf("Going back:\n");
+ cube = apply_trans(inverse_trans(rb_mirror), cube);
+*/
+
+ print_cube(cube);
+
+ for (i = 0; i < ns; i++) {
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free_alg(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-06-30-noreached/steps.c b/old/2021-06-30-noreached/steps.c
@@ -0,0 +1,307 @@
+#include "steps.h"
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .estimate = estimate_coud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .estimate = estimate_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .estimate = estimate_corners_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_HTM = {
+ .estimate = estimate_cornershtr_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_URF = {
+ .estimate = estimate_cornershtr_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_URF = {
+ .estimate = estimate_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+drud_HTM = {
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "ptable_cornershtr_withcosets_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .coord = &coord_drud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "ptable_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "ptable_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM
+};
+
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ if (!pd_eofb_HTM.generated)
+ genptable(&pd_eofb_HTM);
+
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ if (!pd_coud_HTM.generated)
+ genptable(&pd_coud_HTM);
+
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ if (!pd_corners_HTM.generated)
+ genptable(&pd_corners_HTM);
+
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ if (!pd_cornershtr_HTM.generated)
+ genptable(&pd_cornershtr_HTM);
+
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+/*
+ if (!pd_drud_HTM.generated)
+ genptable(&pd_drud_HTM);
+
+ return ptableval(&pd_drud_HTM, ct.cube);
+*/
+
+ if (!pd_drud_sym16_HTM.generated)
+ genptable(&pd_drud_sym16_HTM);
+
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+/* TODO: if lucky, remove this */
+/*
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t1 = { .rot = rf, .mirror = false };
+ static Trans t2 = { .rot = bd, .mirror = false };
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ dr1 = estimate_drud_HTM(ct);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t1, cube);
+ dr2 = estimate_drud_HTM(ct);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t2, cube);
+ dr3 = estimate_drud_HTM(ct);
+
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ ret = MAX(ret, dr3);
+
+ if (ret == 0)
+ return check_ep(cube) ? 0 : 6;
+
+ return ret;
+}
+*/
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t2 = rf, t3 = bd;
+
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ if (!pd_khuge_HTM.generated)
+ genptable(&pd_khuge_HTM);
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t2, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t3, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ return MAX(ret, dr3);
+}
diff --git a/old/2021-06-30-noreached/steps.h b/old/2021-06-30-noreached/steps.h
@@ -0,0 +1,24 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step cornershtr_HTM;
+extern Step corners_URF;
+extern Step cornershtr_URF;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_cornershtreofb_HTM;
+extern PruneData pd_drud_HTM;
+extern PruneData pd_khuge_HTM;
+
+#endif
diff --git a/old/2021-06-30-reached/cube.c b/old/2021-06-30-reached/cube.c
@@ -0,0 +1,3419 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd);
+static void gensym(SymData *sd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_symdata();
+static void init_trans();
+static void init_trans_aux();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Estimator f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static bool ptable_has_reached(PruneData *pd, Cube cube);
+static void ptable_set_reached(PruneData *pd, Cube cube);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static void realloc_alg(Alg *alg, int n);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_symdata_file(SymData *sd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_symdata_file(SymData *sd);
+static bool write_ttables_file();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * rotation_algs[NROTATIONS];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 2;
+static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 };
+
+/* Coordinates and their implementation **************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430
+};
+
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+
+/* TODO: rename */
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ return admissible_ee_aux[ind];
+}
+
+/* TODO: rename */
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.eofb = ind % POW2TO11;
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4;
+
+ return c;
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+
+/* Checkers ******************************************************************/
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Local functions implementation ********************************************/
+
+/* TODO: this should be an anti index (maybe?) */
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s.estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ int oldval = ptableval(pd, c);
+
+ if (oldval < dd->m || ptable_has_reached(pd, c) ||
+ pd->n == pd->coord->max || (dd->m != 0 && pd->coord->check(c)) )
+ return;
+
+ /*ptable_set_reached(pd, c);*/
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, c, dd->m);
+ return;
+ }
+
+ genptable_dfs_branch(c, pd, dd);
+}
+
+static void
+genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd)
+{
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ dd->m++;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+
+ genptable_dfs(apply_move(move, c), pd, dd);
+
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Estimator f, Move *r)
+{
+ CubeTarget ct = { .target = 1 };
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ ct.cube = apply_move(i, (Cube){0});
+ if (f != NULL && f(ct))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static bool
+ptable_has_reached(PruneData *pd, Cube cube)
+{
+ uint64_t ind = pd->coord->index(cube);
+
+ return (ind % 2) ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16;
+}
+
+static void
+ptable_set_reached(PruneData *pd, Cube cube)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->reached[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->reached[ind/2] = (ind % 2) ? 16 + other : 16*other + 1;
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]);
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[uf_mirror] = uf_mirror;
+ inverse_trans_aux[ur_mirror] = ur_mirror;
+ inverse_trans_aux[ul_mirror] = ul_mirror;
+ inverse_trans_aux[ub_mirror] = ub_mirror;
+
+ inverse_trans_aux[df_mirror] = df_mirror;
+ inverse_trans_aux[dr_mirror] = dl_mirror;
+ inverse_trans_aux[dl_mirror] = dr_mirror;
+ inverse_trans_aux[db_mirror] = db_mirror;
+
+ inverse_trans_aux[rf_mirror] = rf_mirror;
+ inverse_trans_aux[rd_mirror] = br_mirror;
+ inverse_trans_aux[rb_mirror] = lb_mirror;
+ inverse_trans_aux[ru_mirror] = fl_mirror;
+
+ inverse_trans_aux[lf_mirror] = lf_mirror;
+ inverse_trans_aux[ld_mirror] = bl_mirror;
+ inverse_trans_aux[lb_mirror] = rb_mirror;
+ inverse_trans_aux[lu_mirror] = fr_mirror;
+
+ inverse_trans_aux[fu_mirror] = fu_mirror;
+ inverse_trans_aux[fr_mirror] = lu_mirror;
+ inverse_trans_aux[fd_mirror] = bu_mirror;
+ inverse_trans_aux[fl_mirror] = ru_mirror;
+
+ inverse_trans_aux[bu_mirror] = fd_mirror;
+ inverse_trans_aux[br_mirror] = rd_mirror;
+ inverse_trans_aux[bd_mirror] = bd_mirror;
+ inverse_trans_aux[bl_mirror] = ld_mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+static void
+init_trans() {
+ Cube aux, cube, mirr, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move_aux[move], aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube, false) ||
+ is_solved(mirr, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ rotation_algs[uf] = new_alg("");
+ rotation_algs[ur] = new_alg("y");
+ rotation_algs[ub] = new_alg("y2");
+ rotation_algs[ul] = new_alg("y3");
+
+ rotation_algs[df] = new_alg("z2");
+ rotation_algs[dr] = new_alg("y z2");
+ rotation_algs[db] = new_alg("x2");
+ rotation_algs[dl] = new_alg("y3 z2");
+
+ rotation_algs[rf] = new_alg("z3");
+ rotation_algs[rd] = new_alg("z3 y");
+ rotation_algs[rb] = new_alg("z3 y2");
+ rotation_algs[ru] = new_alg("z3 y3");
+
+ rotation_algs[lf] = new_alg("z");
+ rotation_algs[ld] = new_alg("z y3");
+ rotation_algs[lb] = new_alg("z y2");
+ rotation_algs[lu] = new_alg("z y");
+
+ rotation_algs[fu] = new_alg("x y2");
+ rotation_algs[fr] = new_alg("x y");
+ rotation_algs[fd] = new_alg("x");
+ rotation_algs[fl] = new_alg("x y3");
+
+ rotation_algs[bu] = new_alg("x3");
+ rotation_algs[br] = new_alg("x3 y");
+ rotation_algs[bd] = new_alg("x3 y2");
+ rotation_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genptable(PruneData *pd)
+{
+ Cube cube;
+ uint64_t j, oldn = 0;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update(pd, pd->coord->cube(j), 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t));
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) {
+ memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t));
+ for (j = 0; j < pd->coord->max; j++) {
+ cube = pd->coord->cube(j);
+ if (pd->coord->check(cube))
+ genptable_dfs(cube, pd, &dd);
+ }
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ dd.d, pd->n-oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+ free(pd->reached);
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+/* TODO: clean pre_trans or put it back */
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ /*AlgListNode *node;*/
+ AlgList *sols = new_alglist();
+ /*Cube c = apply_trans(opts->pre_trans, cube);*/
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(cube)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.estimate, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(cube, step, opts, &dd);
+ }
+
+/*
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(opts->pre_trans), node->alg);
+*/
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval(pd, pd->coord->cube(i))]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ uint64_t ind = pd->coord->index(cube);
+
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+
+Alg *
+rotation_alg(Trans i)
+{
+ return rotation_algs[i % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+ init_symdata();
+}
+
diff --git a/old/2021-06-30-reached/cube.h b/old/2021-06-30-reached/cube.h
@@ -0,0 +1,90 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "macros.h"
+#include "cubetypes.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_drud;
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_khuge;
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */
+Cube anti_cphtr(uint64_t ind); /*TODO also this */
+uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+void init();
+
+#endif
+
diff --git a/old/2021-06-30-reached/cubetypes.h b/old/2021-06-30-reached/cubetypes.h
@@ -0,0 +1,251 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ Checker check;
+ uint64_t max;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ uint8_t * reached;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ Estimator estimate;
+ Checker ready;
+ Moveset moveset;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/old/2021-06-30-reached/macros.h b/old/2021-06-30-reached/macros.h
@@ -0,0 +1,23 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS 48
+#define NROTATIONS 24
+
+#endif
diff --git a/old/2021-06-30-reached/main.c b/old/2021-06-30-reached/main.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 2;
+/* Move m;*/
+/* int nrand = 10000, sum1, sum2, sum3;*/
+
+ Step *stps[20] = {&drud_HTM, &optimal_HTM};
+ char sss[30][30] = {"DR on UD", "Optimal solve"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ };
+
+ init();
+
+/*
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ sum3 = 0;
+ for (i = 0; i < nrand; i++) {
+ cube = random_cube();
+ sum1 += drud_HTM.check(cube, 20);
+ sum2 += optimal_HTM.check(cube, 20);
+ sum3 += cornershtreofb_HTM.check(cube, 20);
+ }
+ printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand));
+ printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand));
+*/
+
+/*
+ for (m = U; m <= B3; m++) {
+ printf("Class eofbepos after %d: ", m);
+ printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0})));
+ }
+*/
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ print_alg(inverse_alg(algo), false);
+/*
+ printf("After rb_mirror:\n");
+ cube = apply_trans(rb_mirror, cube);
+ print_cube(cube);
+ printf("Going back:\n");
+ cube = apply_trans(inverse_trans(rb_mirror), cube);
+*/
+
+ print_cube(cube);
+
+ for (i = 0; i < ns; i++) {
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free_alg(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-06-30-reached/steps.c b/old/2021-06-30-reached/steps.c
@@ -0,0 +1,307 @@
+#include "steps.h"
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .estimate = estimate_coud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .estimate = estimate_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .estimate = estimate_corners_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_HTM = {
+ .estimate = estimate_cornershtr_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_URF = {
+ .estimate = estimate_cornershtr_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_URF = {
+ .estimate = estimate_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+drud_HTM = {
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "ptable_cornershtr_withcosets_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .coord = &coord_drud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "pd_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "pd_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM
+};
+
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ if (!pd_eofb_HTM.generated)
+ genptable(&pd_eofb_HTM);
+
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ if (!pd_coud_HTM.generated)
+ genptable(&pd_coud_HTM);
+
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ if (!pd_corners_HTM.generated)
+ genptable(&pd_corners_HTM);
+
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ if (!pd_cornershtr_HTM.generated)
+ genptable(&pd_cornershtr_HTM);
+
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+/*
+ if (!pd_drud_HTM.generated)
+ genptable(&pd_drud_HTM);
+
+ return ptableval(&pd_drud_HTM, ct.cube);
+*/
+
+ if (!pd_drud_sym16_HTM.generated)
+ genptable(&pd_drud_sym16_HTM);
+
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+/* TODO: if lucky, remove this */
+/*
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t1 = { .rot = rf, .mirror = false };
+ static Trans t2 = { .rot = bd, .mirror = false };
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ dr1 = estimate_drud_HTM(ct);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t1, cube);
+ dr2 = estimate_drud_HTM(ct);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t2, cube);
+ dr3 = estimate_drud_HTM(ct);
+
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ ret = MAX(ret, dr3);
+
+ if (ret == 0)
+ return check_ep(cube) ? 0 : 6;
+
+ return ret;
+}
+*/
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t2 = rf, t3 = bd;
+
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ if (!pd_khuge_HTM.generated)
+ genptable(&pd_khuge_HTM);
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t2, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t3, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ return MAX(ret, dr3);
+}
diff --git a/old/2021-06-30-reached/steps.h b/old/2021-06-30-reached/steps.h
@@ -0,0 +1,24 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step cornershtr_HTM;
+extern Step corners_URF;
+extern Step cornershtr_URF;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_cornershtreofb_HTM;
+extern PruneData pd_drud_HTM;
+extern PruneData pd_khuge_HTM;
+
+#endif
diff --git a/old/2021-07-02-genptable-dfs/cube.c b/old/2021-07-02-genptable-dfs/cube.c
@@ -0,0 +1,3394 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd);
+static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd);
+static void gensym(SymData *sd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_symdata();
+static void init_trans();
+static void init_trans_aux();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Estimator f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static void realloc_alg(Alg *alg, int n);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_symdata_file(SymData *sd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_symdata_file(SymData *sd);
+static bool write_ttables_file();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * rotation_algs[NROTATIONS];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 2;
+static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 };
+
+/* Coordinates and their implementation **************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430
+};
+
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+
+/* TODO: rename */
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ return admissible_ee_aux[ind];
+}
+
+/* TODO: rename */
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.eofb = ind % POW2TO11;
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4;
+
+ return c;
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+
+/* Checkers ******************************************************************/
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Local functions implementation ********************************************/
+
+/* TODO: this should be an anti index (maybe?) */
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s.estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_dfs(Cube c, PruneData *pd, DfsData *dd)
+{
+ int oldval = ptableval(pd, c);
+
+ if (oldval < dd->m || pd->n == pd->coord->max ||
+ (dd->m != 0 && pd->coord->check(c)) )
+ return;
+
+ if (dd->m == dd->d) {
+ if (dd->m < oldval)
+ ptable_update(pd, c, dd->m);
+ return;
+ }
+
+ genptable_dfs_branch(c, pd, dd);
+}
+
+static void
+genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd)
+{
+ Move i, move, l1 = dd->last1, l2 = dd->last2;
+
+ dd->m++;
+
+ for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) {
+ move = dd->sorted_moves[i];
+ if (allowed_next(move, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = move;
+
+ genptable_dfs(apply_move(move, c), pd, dd);
+
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+
+ dd->m--;
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Estimator f, Move *r)
+{
+ CubeTarget ct = { .target = 1 };
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ ct.cube = apply_move(i, (Cube){0});
+ if (f != NULL && f(ct))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]);
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[uf_mirror] = uf_mirror;
+ inverse_trans_aux[ur_mirror] = ur_mirror;
+ inverse_trans_aux[ul_mirror] = ul_mirror;
+ inverse_trans_aux[ub_mirror] = ub_mirror;
+
+ inverse_trans_aux[df_mirror] = df_mirror;
+ inverse_trans_aux[dr_mirror] = dl_mirror;
+ inverse_trans_aux[dl_mirror] = dr_mirror;
+ inverse_trans_aux[db_mirror] = db_mirror;
+
+ inverse_trans_aux[rf_mirror] = rf_mirror;
+ inverse_trans_aux[rd_mirror] = br_mirror;
+ inverse_trans_aux[rb_mirror] = lb_mirror;
+ inverse_trans_aux[ru_mirror] = fl_mirror;
+
+ inverse_trans_aux[lf_mirror] = lf_mirror;
+ inverse_trans_aux[ld_mirror] = bl_mirror;
+ inverse_trans_aux[lb_mirror] = rb_mirror;
+ inverse_trans_aux[lu_mirror] = fr_mirror;
+
+ inverse_trans_aux[fu_mirror] = fu_mirror;
+ inverse_trans_aux[fr_mirror] = lu_mirror;
+ inverse_trans_aux[fd_mirror] = bu_mirror;
+ inverse_trans_aux[fl_mirror] = ru_mirror;
+
+ inverse_trans_aux[bu_mirror] = fd_mirror;
+ inverse_trans_aux[br_mirror] = rd_mirror;
+ inverse_trans_aux[bd_mirror] = bd_mirror;
+ inverse_trans_aux[bl_mirror] = ld_mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+static void
+init_trans() {
+ Cube aux, cube, mirr, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move_aux[move], aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube, false) ||
+ is_solved(mirr, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ rotation_algs[uf] = new_alg("");
+ rotation_algs[ur] = new_alg("y");
+ rotation_algs[ub] = new_alg("y2");
+ rotation_algs[ul] = new_alg("y3");
+
+ rotation_algs[df] = new_alg("z2");
+ rotation_algs[dr] = new_alg("y z2");
+ rotation_algs[db] = new_alg("x2");
+ rotation_algs[dl] = new_alg("y3 z2");
+
+ rotation_algs[rf] = new_alg("z3");
+ rotation_algs[rd] = new_alg("z3 y");
+ rotation_algs[rb] = new_alg("z3 y2");
+ rotation_algs[ru] = new_alg("z3 y3");
+
+ rotation_algs[lf] = new_alg("z");
+ rotation_algs[ld] = new_alg("z y3");
+ rotation_algs[lb] = new_alg("z y2");
+ rotation_algs[lu] = new_alg("z y");
+
+ rotation_algs[fu] = new_alg("x y2");
+ rotation_algs[fr] = new_alg("x y");
+ rotation_algs[fd] = new_alg("x");
+ rotation_algs[fl] = new_alg("x y3");
+
+ rotation_algs[bu] = new_alg("x3");
+ rotation_algs[br] = new_alg("x3 y");
+ rotation_algs[bd] = new_alg("x3 y2");
+ rotation_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genptable(PruneData *pd)
+{
+ Cube cube;
+ uint64_t j, oldn = 0;
+ DfsData dd = {
+ .m = 0,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE
+ };
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update(pd, pd->coord->cube(j), 15);
+
+ moveset_to_list(pd->moveset, NULL, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) {
+ for (j = 0; j < pd->coord->max; j++) {
+ cube = pd->coord->cube(j);
+ if (pd->coord->check(cube))
+ genptable_dfs(cube, pd, &dd);
+ }
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ dd.d, pd->n-oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+/* TODO: clean pre_trans or put it back */
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ /*AlgListNode *node;*/
+ AlgList *sols = new_alglist();
+ /*Cube c = apply_trans(opts->pre_trans, cube);*/
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(cube)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.estimate, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(cube, step, opts, &dd);
+ }
+
+/*
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(opts->pre_trans), node->alg);
+*/
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval(pd, pd->coord->cube(i))]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ uint64_t ind = pd->coord->index(cube);
+
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+
+Alg *
+rotation_alg(Trans i)
+{
+ return rotation_algs[i % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+ init_symdata();
+}
+
diff --git a/old/2021-07-02-genptable-dfs/cube.h b/old/2021-07-02-genptable-dfs/cube.h
@@ -0,0 +1,90 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "macros.h"
+#include "cubetypes.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_drud;
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_khuge;
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */
+Cube anti_cphtr(uint64_t ind); /*TODO also this */
+uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+void init();
+
+#endif
+
diff --git a/old/2021-07-02-genptable-dfs/cubetypes.h b/old/2021-07-02-genptable-dfs/cubetypes.h
@@ -0,0 +1,250 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ Checker check;
+ uint64_t max;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ Estimator estimate;
+ Checker ready;
+ Moveset moveset;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/old/2021-07-02-genptable-dfs/macros.h b/old/2021-07-02-genptable-dfs/macros.h
@@ -0,0 +1,23 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS 48
+#define NROTATIONS 24
+
+#endif
diff --git a/old/2021-07-02-genptable-dfs/main.c b/old/2021-07-02-genptable-dfs/main.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 2;
+/* Move m;*/
+/* int nrand = 10000, sum1, sum2, sum3;*/
+
+ Step *stps[20] = {&drud_HTM, &optimal_HTM};
+ char sss[30][30] = {"DR on UD", "Optimal solve"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ };
+
+ init();
+
+/*
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ sum3 = 0;
+ for (i = 0; i < nrand; i++) {
+ cube = random_cube();
+ sum1 += drud_HTM.check(cube, 20);
+ sum2 += optimal_HTM.check(cube, 20);
+ sum3 += cornershtreofb_HTM.check(cube, 20);
+ }
+ printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand));
+ printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand));
+*/
+
+/*
+ for (m = U; m <= B3; m++) {
+ printf("Class eofbepos after %d: ", m);
+ printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0})));
+ }
+*/
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ print_alg(inverse_alg(algo), false);
+/*
+ printf("After rb_mirror:\n");
+ cube = apply_trans(rb_mirror, cube);
+ print_cube(cube);
+ printf("Going back:\n");
+ cube = apply_trans(inverse_trans(rb_mirror), cube);
+*/
+
+ print_cube(cube);
+
+ for (i = 0; i < ns; i++) {
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free_alg(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-07-02-genptable-dfs/steps.c b/old/2021-07-02-genptable-dfs/steps.c
@@ -0,0 +1,307 @@
+#include "steps.h"
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .estimate = estimate_coud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .estimate = estimate_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .estimate = estimate_corners_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_HTM = {
+ .estimate = estimate_cornershtr_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_URF = {
+ .estimate = estimate_cornershtr_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_URF = {
+ .estimate = estimate_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+drud_HTM = {
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "ptable_cornershtr_withcosets_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .coord = &coord_drud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "ptable_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "ptable_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM
+};
+
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ if (!pd_eofb_HTM.generated)
+ genptable(&pd_eofb_HTM);
+
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ if (!pd_coud_HTM.generated)
+ genptable(&pd_coud_HTM);
+
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ if (!pd_corners_HTM.generated)
+ genptable(&pd_corners_HTM);
+
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ if (!pd_cornershtr_HTM.generated)
+ genptable(&pd_cornershtr_HTM);
+
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+/*
+ if (!pd_drud_HTM.generated)
+ genptable(&pd_drud_HTM);
+
+ return ptableval(&pd_drud_HTM, ct.cube);
+*/
+
+ if (!pd_drud_sym16_HTM.generated)
+ genptable(&pd_drud_sym16_HTM);
+
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+/* TODO: if lucky, remove this */
+/*
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t1 = { .rot = rf, .mirror = false };
+ static Trans t2 = { .rot = bd, .mirror = false };
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ dr1 = estimate_drud_HTM(ct);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t1, cube);
+ dr2 = estimate_drud_HTM(ct);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ ct.cube = apply_trans(t2, cube);
+ dr3 = estimate_drud_HTM(ct);
+
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ ret = MAX(ret, dr3);
+
+ if (ret == 0)
+ return check_ep(cube) ? 0 : 6;
+
+ return ret;
+}
+*/
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ static Trans t2 = rf, t3 = bd;
+
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ if (!pd_khuge_HTM.generated)
+ genptable(&pd_khuge_HTM);
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t2, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(t3, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ return MAX(ret, dr3);
+}
diff --git a/old/2021-07-02-genptable-dfs/steps.h b/old/2021-07-02-genptable-dfs/steps.h
@@ -0,0 +1,24 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step cornershtr_HTM;
+extern Step corners_URF;
+extern Step cornershtr_URF;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_cornershtreofb_HTM;
+extern PruneData pd_drud_HTM;
+extern PruneData pd_khuge_HTM;
+
+#endif
diff --git a/old/2021-07-15-almostbeforerefactor/README b/old/2021-07-15-almostbeforerefactor/README
@@ -0,0 +1,3 @@
+I started refactorig (splitting code into more files) before realizing I had no backup of
+the fast version that works.
+Take some stuff from alg.h back into cube.h, remove alg.* ad it should be ok.
diff --git a/old/2021-07-15-almostbeforerefactor/alg.c b/old/2021-07-15-almostbeforerefactor/alg.c
@@ -0,0 +1,3388 @@
+#include "alg.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_bfs(PruneData *pd, int d, Move *ms);
+static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m);
+static void gensym(SymData *sd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_symdata();
+static void init_trans();
+static void init_trans_aux();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Estimator f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static int ptableval_index(PruneData *pd, uint64_t ind);
+static void realloc_alg(Alg *alg, int n);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_symdata_file(SymData *sd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_symdata_file(SymData *sd);
+static bool write_ttables_file();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * rotation_algs[NROTATIONS];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+Trans
+trans_group_trivial[1] = { uf };
+
+Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 2;
+static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 };
+
+/* Coordinates and their implementation **************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430
+};
+
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+
+/* TODO: rename */
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ return admissible_ee_aux[ind];
+}
+
+/* TODO: rename */
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+/* DONE: temporary fix, make it better */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ uint64_t epos, eofb;
+ Cube c;
+
+ eofb = ind % POW2TO11;
+ epos = ind / (POW2TO11 * POW3TO7);
+ c = admissible_ee_aux[eofb + POW2TO11 * epos];
+
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.corl = c.coud;
+ c.cofb = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+
+/* Checkers ******************************************************************/
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Local functions implementation ********************************************/
+
+/* TODO: this should be an anti index (maybe?) */
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s.estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_bfs(PruneData *pd, int d, Move *ms)
+{
+ uint64_t i;
+
+ for (i = 0; i < pd->coord->max; i++)
+ if (ptableval_index(pd, i) == d)
+ genptable_branch(pd, i, d, ms);
+}
+
+static void
+genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms)
+{
+ int i, j;
+ Cube cc, c;
+
+
+ for (i = 0; i < pd->ntrans; i++) {
+ c = apply_trans(pd->trans[i], pd->coord->cube(ind));
+ for (j = 0; ms[j] != NULLMOVE; j++) {
+ cc = apply_move(ms[j], c);
+ if (ptableval(pd, cc) > d+1)
+ ptable_update(pd, cc, d+1);
+ }
+ }
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Estimator f, Move *r)
+{
+ CubeTarget ct = { .target = 1 };
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ ct.cube = apply_move(i, (Cube){0});
+ if (f != NULL && f(ct))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static int
+ptableval_index(PruneData *pd, uint64_t ind)
+{
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]);
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[uf_mirror] = uf_mirror;
+ inverse_trans_aux[ur_mirror] = ur_mirror;
+ inverse_trans_aux[ul_mirror] = ul_mirror;
+ inverse_trans_aux[ub_mirror] = ub_mirror;
+
+ inverse_trans_aux[df_mirror] = df_mirror;
+ inverse_trans_aux[dr_mirror] = dl_mirror;
+ inverse_trans_aux[dl_mirror] = dr_mirror;
+ inverse_trans_aux[db_mirror] = db_mirror;
+
+ inverse_trans_aux[rf_mirror] = rf_mirror;
+ inverse_trans_aux[rd_mirror] = br_mirror;
+ inverse_trans_aux[rb_mirror] = lb_mirror;
+ inverse_trans_aux[ru_mirror] = fl_mirror;
+
+ inverse_trans_aux[lf_mirror] = lf_mirror;
+ inverse_trans_aux[ld_mirror] = bl_mirror;
+ inverse_trans_aux[lb_mirror] = rb_mirror;
+ inverse_trans_aux[lu_mirror] = fr_mirror;
+
+ inverse_trans_aux[fu_mirror] = fu_mirror;
+ inverse_trans_aux[fr_mirror] = lu_mirror;
+ inverse_trans_aux[fd_mirror] = bu_mirror;
+ inverse_trans_aux[fl_mirror] = ru_mirror;
+
+ inverse_trans_aux[bu_mirror] = fd_mirror;
+ inverse_trans_aux[br_mirror] = rd_mirror;
+ inverse_trans_aux[bd_mirror] = bd_mirror;
+ inverse_trans_aux[bl_mirror] = ld_mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+static void
+init_trans() {
+ Cube aux, cube, mirr, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move_aux[move], aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube, false) ||
+ is_solved(mirr, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ rotation_algs[uf] = new_alg("");
+ rotation_algs[ur] = new_alg("y");
+ rotation_algs[ub] = new_alg("y2");
+ rotation_algs[ul] = new_alg("y3");
+
+ rotation_algs[df] = new_alg("z2");
+ rotation_algs[dr] = new_alg("y z2");
+ rotation_algs[db] = new_alg("x2");
+ rotation_algs[dl] = new_alg("y3 z2");
+
+ rotation_algs[rf] = new_alg("z3");
+ rotation_algs[rd] = new_alg("z3 y");
+ rotation_algs[rb] = new_alg("z3 y2");
+ rotation_algs[ru] = new_alg("z3 y3");
+
+ rotation_algs[lf] = new_alg("z");
+ rotation_algs[ld] = new_alg("z y3");
+ rotation_algs[lb] = new_alg("z y2");
+ rotation_algs[lu] = new_alg("z y");
+
+ rotation_algs[fu] = new_alg("x y2");
+ rotation_algs[fr] = new_alg("x y");
+ rotation_algs[fd] = new_alg("x");
+ rotation_algs[fl] = new_alg("x y3");
+
+ rotation_algs[bu] = new_alg("x3");
+ rotation_algs[br] = new_alg("x3 y");
+ rotation_algs[bd] = new_alg("x3 y2");
+ rotation_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genptable(PruneData *pd)
+{
+ Move ms[NMOVES];
+ int d;
+ uint64_t j, oldn = 0;
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ moveset_to_list(pd->moveset, NULL, ms);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update(pd, pd->coord->cube(j), 15);
+
+ /*TODO: change, set to 0 for every solved state (might be more than 1)*/
+ ptable_update(pd, (Cube){0}, 0);
+ pd->n = 1;
+
+ for (d = 0; d < 15 && pd->n < pd->coord->max; d++) {
+ genptable_bfs(pd, d, ms);
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ d, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+/* TODO: clean pre_trans or put it back */
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ /*AlgListNode *node;*/
+ AlgList *sols = new_alglist();
+ /*Cube c = apply_trans(opts->pre_trans, cube);*/
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(cube)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.estimate, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(cube, step, opts, &dd);
+ }
+
+/*
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(opts->pre_trans), node->alg);
+*/
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval(pd, pd->coord->cube(i))]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ return ptableval_index(pd, pd->coord->index(cube));
+}
+
+Alg *
+rotation_alg(Trans i)
+{
+ return rotation_algs[i % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+ init_symdata();
+}
+
diff --git a/old/2021-07-15-almostbeforerefactor/alg.h b/old/2021-07-15-almostbeforerefactor/alg.h
@@ -0,0 +1,27 @@
+#ifndef ALG_H
+#define ALG_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macros.h"
+#include "cubetypes.h"
+
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Move inverse_move(Move m);
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+Alg * new_alg(char *str);
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans t, Alg *alg);
+
+#endif
+
diff --git a/old/2021-07-15-almostbeforerefactor/cube.c b/old/2021-07-15-almostbeforerefactor/cube.c
@@ -0,0 +1,3391 @@
+#include "cube.h"
+
+/* TODO: remove! */
+#include "steps.h"
+
+/* Local functions **********************************************************/
+
+static Cube admissible_ep(Cube cube, PieceFilter f);
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static bool allowed_next(Move move, DfsData *dd);
+static void append_alg(AlgList *l, Alg *alg);
+static void append_move(Alg *alg, Move m, bool inverse);
+static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+static void apply_permutation(int *perm, int *set, int n);
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+static int binomial(int n, int k);
+static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_check_solved(SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd);
+static int digit_array_to_int(int *a, int n, int b);
+static int edge_slice(Edge e); /* E=0, S=1, M=2 */
+static int epos_dependent_pos(int pos1, int pos2);
+static int epos_from_arrays(int *epos, int *ep);
+static void epos_to_partial_ep(int epos, int *ep, int *ss);
+static int factorial(int n);
+static void free_alglistnode(AlgListNode *aln);
+static void free_cubearray(CubeArray *arr, PieceFilter f);
+static void genptable_bfs(PruneData *pd, int d, Move *ms);
+static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m);
+static void gensym(SymData *sd);
+static void index_to_perm(int p, int n, int *r);
+static void index_to_subset(int s, int n, int k, int *r);
+static void init_auxtables();
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_environment();
+static void init_moves();
+static void init_moves_aux();
+static void init_strings();
+static void init_symdata();
+static void init_trans();
+static void init_trans_aux();
+static void int_to_digit_array(int a, int b, int n, int *r);
+static void int_to_sum_zero_array(int x, int b, int n, int *a);
+static int invert_digits(int a, int b, int n);
+static bool is_perm(int *a, int n);
+static bool is_subset(int *a, int n, int k);
+static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+static void movelist_to_position(Move *movelist, int *position);
+static void moveset_to_list(Moveset ms, Estimator f, Move *r);
+static AlgList * new_alglist();
+static CubeArray * new_cubearray(Cube cube, PieceFilter f);
+static int perm_sign(int *a, int n);
+static int perm_to_index(int *a, int n);
+static int powint(int a, int b);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static int ptableval_index(PruneData *pd, uint64_t ind);
+static void realloc_alg(Alg *alg, int n);
+static bool read_mtables_file();
+static bool read_ptable_file(PruneData *pd);
+static bool read_symdata_file(SymData *sd);
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static int subset_to_index(int *a, int n, int k);
+static void sum_arrays_mod(int *src, int *dst, int n, int m);
+static void swap(int *a, int *b);
+static bool write_mtables_file();
+static bool write_ptable_file(PruneData *pd);
+static bool write_symdata_file(SymData *sd);
+static bool write_ttables_file();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static char * tabledir;
+
+static PieceFilter pf_all;
+static PieceFilter pf_4val;
+static PieceFilter pf_epcp;
+static PieceFilter pf_cpos;
+static PieceFilter pf_cp;
+static PieceFilter pf_ep;
+static PieceFilter pf_e;
+static PieceFilter pf_s;
+static PieceFilter pf_m;
+static PieceFilter pf_eo;
+static PieceFilter pf_co;
+
+static int epe_solved[4];
+static int eps_solved[4];
+static int epm_solved[4];
+
+static char move_string[NMOVES][7];
+static char edge_string[12][7];
+static char corner_string[8][7];
+static char center_string[6][7];
+
+static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+static bool commute[NMOVES][NMOVES];
+static bool possible_next[NMOVES][NMOVES][NMOVES];
+static Move inverse_move_aux[NMOVES];
+static Trans inverse_trans_aux[NTRANS];
+static int epos_dependent_aux[BINOM12ON4][BINOM12ON4];
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static Center what_center_at_aux[FACTORIAL6][6];
+static Corner what_corner_at_aux[FACTORIAL8][8];
+static int what_orientation_last_corner_aux[POW3TO7];
+static int what_orientation_last_edge_aux[POW2TO11];
+static Center where_is_center_aux[FACTORIAL6][6];
+static Corner where_is_corner_aux[FACTORIAL8][8];
+static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+static uint64_t me[12];
+
+static int edge_cycle[NMOVES][12];
+static int corner_cycle[NMOVES][8];
+static int center_cycle[NMOVES][6];
+static int eofb_flipped[NMOVES][12];
+static int eorl_flipped[NMOVES][12];
+static int eoud_flipped[NMOVES][12];
+static int coud_flipped[NMOVES][8];
+static int corl_flipped[NMOVES][8];
+static int cofb_flipped[NMOVES][8];
+static Alg * equiv_alg[NMOVES];
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+static int ep_mirror[12];
+static int cp_mirror[8];
+static int cpos_mirror[6];
+static Alg * rotation_algs[NROTATIONS];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+Trans
+trans_group_trivial[1] = { uf };
+
+Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 2;
+static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 };
+
+/* Coordinates and their implementation **************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430
+};
+
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+
+/* TODO: rename */
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ return admissible_ee_aux[ind];
+}
+
+/* TODO: rename */
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+/* TODO: admissible co for other orientations */
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+/* DONE: temporary fix, make it better */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ uint64_t epos, eofb;
+ Cube c;
+
+ eofb = ind % POW2TO11;
+ epos = ind / (POW2TO11 * POW3TO7);
+ c = admissible_ee_aux[eofb + POW2TO11 * epos];
+
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.corl = c.coud;
+ c.cofb = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+
+/* Checkers ******************************************************************/
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+
+/* Local functions implementation ********************************************/
+
+/* TODO: this should be an anti index (maybe?) */
+static Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+static void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+static Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+static void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+static int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+static Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+static void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+/*
+static int
+cphtr_cp(int cp)
+{
+ int i, a[8];
+
+ index_to_perm(cp, 8, a);
+
+ for (i = 0; i < 8; i++)
+ if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR)
+ a[i] = 0;
+ else
+ a[i] = 1;
+
+ swap(&a[1], &a[5]);
+ swap(&a[3], &a[7]);
+
+ return subset_to_index(a, 8, 4);
+}
+*/
+
+static void
+dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s.estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s.estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+static int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+static int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+static void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+static int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+static void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+static void
+genptable_bfs(PruneData *pd, int d, Move *ms)
+{
+ uint64_t i;
+
+ for (i = 0; i < pd->coord->max; i++)
+ if (ptableval_index(pd, i) == d)
+ genptable_branch(pd, i, d, ms);
+}
+
+static void
+genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms)
+{
+ int i, j;
+ Cube cc, c;
+
+
+ for (i = 0; i < pd->ntrans; i++) {
+ c = apply_trans(pd->trans[i], pd->coord->cube(ind));
+ for (j = 0; ms[j] != NULLMOVE; j++) {
+ cc = apply_move(ms[j], c);
+ if (ptableval(pd, cc) > d+1)
+ ptable_update(pd, cc, d+1);
+ }
+ }
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+static void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+static void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+static void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+static int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+static bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+static bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+static Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+static void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+static void
+moveset_to_list(Moveset ms, Estimator f, Move *r)
+{
+ CubeTarget ct = { .target = 1 };
+ int b[NMOVES];
+ int na = 0, nb = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++) {
+ if (ms(i)) {
+ ct.cube = apply_move(i, (Cube){0});
+ if (f != NULL && f(ct))
+ r[na++] = i;
+ else
+ b[nb++] = i;
+ }
+ }
+
+ memcpy(r + na, b, nb * sizeof(Move));
+ r[na+nb] = NULLMOVE;
+}
+
+static AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+static CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+static int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+static int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+static int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+static int
+ptableval_index(PruneData *pd, uint64_t ind)
+{
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
+static bool
+read_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+read_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fread(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fread(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]);
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+static void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+static void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+static bool
+write_mtables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_ttables_file()
+{
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(co_ttable[m], b, me[7], f) == me[7];
+ r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10];
+ r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+static void
+init_auxtables()
+{
+ Cube c1, c2;
+ CubeArray *arr;
+ uint64_t ui, uj;
+ int i, j, k, auxarr[12];
+ bool cij, p1, p2;
+
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++) {
+ what_center_at_aux[ui][i] = arr->cpos[i];
+ where_is_center_aux[ui][arr->cpos[i]] = i;
+ }
+ free_cubearray(arr, pf_cpos);
+ }
+
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++) {
+ what_corner_at_aux[ui][i] = arr->cp[i];
+ where_is_corner_aux[ui][arr->cp[i]] = i;
+ }
+ free_cubearray(arr, pf_cp);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ where_is_edge_aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ where_is_edge_aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ where_is_edge_aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ what_orientation_last_corner_aux[ui] = auxarr[7];
+ }
+
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ what_orientation_last_edge_aux[ui] = auxarr[11];
+ }
+
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj);
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ possible_next[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ for (i = 0; i < NMOVES; i++)
+ inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE;
+
+ /* Is there a more elegant way? */
+ inverse_trans_aux[uf] = uf;
+ inverse_trans_aux[ur] = ul;
+ inverse_trans_aux[ul] = ur;
+ inverse_trans_aux[ub] = ub;
+
+ inverse_trans_aux[df] = df;
+ inverse_trans_aux[dr] = dr;
+ inverse_trans_aux[dl] = dl;
+ inverse_trans_aux[db] = db;
+
+ inverse_trans_aux[rf] = lf;
+ inverse_trans_aux[rd] = bl;
+ inverse_trans_aux[rb] = rb;
+ inverse_trans_aux[ru] = fr;
+
+ inverse_trans_aux[lf] = rf;
+ inverse_trans_aux[ld] = br;
+ inverse_trans_aux[lb] = lb;
+ inverse_trans_aux[lu] = fl;
+
+ inverse_trans_aux[fu] = fu;
+ inverse_trans_aux[fr] = ru;
+ inverse_trans_aux[fd] = bu;
+ inverse_trans_aux[fl] = lu;
+
+ inverse_trans_aux[bu] = fd;
+ inverse_trans_aux[br] = ld;
+ inverse_trans_aux[bd] = bd;
+ inverse_trans_aux[bl] = rd;
+
+ inverse_trans_aux[uf_mirror] = uf_mirror;
+ inverse_trans_aux[ur_mirror] = ur_mirror;
+ inverse_trans_aux[ul_mirror] = ul_mirror;
+ inverse_trans_aux[ub_mirror] = ub_mirror;
+
+ inverse_trans_aux[df_mirror] = df_mirror;
+ inverse_trans_aux[dr_mirror] = dl_mirror;
+ inverse_trans_aux[dl_mirror] = dr_mirror;
+ inverse_trans_aux[db_mirror] = db_mirror;
+
+ inverse_trans_aux[rf_mirror] = rf_mirror;
+ inverse_trans_aux[rd_mirror] = br_mirror;
+ inverse_trans_aux[rb_mirror] = lb_mirror;
+ inverse_trans_aux[ru_mirror] = fl_mirror;
+
+ inverse_trans_aux[lf_mirror] = lf_mirror;
+ inverse_trans_aux[ld_mirror] = bl_mirror;
+ inverse_trans_aux[lb_mirror] = rb_mirror;
+ inverse_trans_aux[lu_mirror] = fr_mirror;
+
+ inverse_trans_aux[fu_mirror] = fu_mirror;
+ inverse_trans_aux[fr_mirror] = lu_mirror;
+ inverse_trans_aux[fd_mirror] = bu_mirror;
+ inverse_trans_aux[fl_mirror] = ru_mirror;
+
+ inverse_trans_aux[bu_mirror] = fd_mirror;
+ inverse_trans_aux[br_mirror] = rd_mirror;
+ inverse_trans_aux[bd_mirror] = bd_mirror;
+ inverse_trans_aux[bl_mirror] = ld_mirror;
+}
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ jj = cp_mtable[moves[k]][next[j]];
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_environment()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+}
+
+static void
+init_moves() {
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+}
+
+static void
+init_moves_aux()
+{
+ /* Some standard PieceFilters */
+ pf_all.epose = true;
+ pf_all.eposs = true;
+ pf_all.eposm = true;
+ pf_all.eofb = true;
+ pf_all.eorl = true;
+ pf_all.eoud = true;
+ pf_all.cp = true;
+ pf_all.cofb = true;
+ pf_all.corl = true;
+ pf_all.coud = true;
+ pf_all.cpos = true;
+
+ pf_4val.epose = true;
+ pf_4val.eposs = true;
+ pf_4val.eposm = true;
+ pf_4val.eofb = true;
+ pf_4val.coud = true;
+ pf_4val.cp = true;
+
+ pf_epcp.epose = true;
+ pf_epcp.eposs = true;
+ pf_epcp.eposm = true;
+ pf_epcp.cp = true;
+
+ pf_cpos.cpos = true;
+
+ pf_cp.cp = true;
+
+ pf_ep.epose = true;
+ pf_ep.eposs = true;
+ pf_ep.eposm = true;
+
+ pf_e.epose = true;
+ pf_s.eposs = true;
+ pf_m.eposm = true;
+
+ pf_eo.eofb = true;
+ pf_eo.eorl = true;
+ pf_eo.eoud = true;
+
+ pf_co.cofb = true;
+ pf_co.corl = true;
+ pf_co.coud = true;
+
+ /* Used to convert to and from CubeArray */
+ epe_solved[0] = FR;
+ epe_solved[1] = FL;
+ epe_solved[2] = BL;
+ epe_solved[3] = BR;
+
+ eps_solved[0] = UL;
+ eps_solved[1] = UR;
+ eps_solved[2] = DL;
+ eps_solved[3] = DR;
+
+ epm_solved[0] = UF;
+ epm_solved[1] = UB;
+ epm_solved[2] = DF;
+ epm_solved[3] = DB;
+
+ /* Table sizes, used for reading and writing files */
+ me[0] = FACTORIAL12/FACTORIAL8;
+ me[1] = FACTORIAL12/FACTORIAL8;
+ me[2] = FACTORIAL12/FACTORIAL8;
+ me[3] = POW2TO11;
+ me[4] = POW2TO11;
+ me[5] = POW2TO11;
+ me[6] = FACTORIAL8;
+ me[7] = POW3TO7;
+ me[8] = POW3TO7;
+ me[9] = POW3TO7;
+ me[10] = FACTORIAL6;
+ me[11] = NMOVES;
+
+ /* Cycles *********************/
+ edge_cycle[U][UF] = UR;
+ edge_cycle[U][UL] = UF;
+ edge_cycle[U][UB] = UL;
+ edge_cycle[U][UR] = UB;
+ edge_cycle[U][DF] = DF;
+ edge_cycle[U][DL] = DL;
+ edge_cycle[U][DB] = DB;
+ edge_cycle[U][DR] = DR;
+ edge_cycle[U][FR] = FR;
+ edge_cycle[U][FL] = FL;
+ edge_cycle[U][BL] = BL;
+ edge_cycle[U][BR] = BR;
+
+ edge_cycle[x][UF] = DF;
+ edge_cycle[x][UL] = FL;
+ edge_cycle[x][UB] = UF;
+ edge_cycle[x][UR] = FR;
+ edge_cycle[x][DF] = DB;
+ edge_cycle[x][DL] = BL;
+ edge_cycle[x][DB] = UB;
+ edge_cycle[x][DR] = BR;
+ edge_cycle[x][FR] = DR;
+ edge_cycle[x][FL] = DL;
+ edge_cycle[x][BL] = UL;
+ edge_cycle[x][BR] = UR;
+
+ edge_cycle[y][UF] = UR;
+ edge_cycle[y][UL] = UF;
+ edge_cycle[y][UB] = UL;
+ edge_cycle[y][UR] = UB;
+ edge_cycle[y][DF] = DR;
+ edge_cycle[y][DL] = DF;
+ edge_cycle[y][DB] = DL;
+ edge_cycle[y][DR] = DB;
+ edge_cycle[y][FR] = BR;
+ edge_cycle[y][FL] = FR;
+ edge_cycle[y][BL] = FL;
+ edge_cycle[y][BR] = BL;
+
+ corner_cycle[U][UFR] = UBR;
+ corner_cycle[U][UFL] = UFR;
+ corner_cycle[U][UBL] = UFL;
+ corner_cycle[U][UBR] = UBL;
+ corner_cycle[U][DFR] = DFR;
+ corner_cycle[U][DFL] = DFL;
+ corner_cycle[U][DBL] = DBL;
+ corner_cycle[U][DBR] = DBR;
+
+ corner_cycle[x][UFR] = DFR;
+ corner_cycle[x][UFL] = DFL;
+ corner_cycle[x][UBL] = UFL;
+ corner_cycle[x][UBR] = UFR;
+ corner_cycle[x][DFR] = DBR;
+ corner_cycle[x][DFL] = DBL;
+ corner_cycle[x][DBL] = UBL;
+ corner_cycle[x][DBR] = UBR;
+
+ corner_cycle[y][UFR] = UBR;
+ corner_cycle[y][UFL] = UFR;
+ corner_cycle[y][UBL] = UFL;
+ corner_cycle[y][UBR] = UBL;
+ corner_cycle[y][DFR] = DBR;
+ corner_cycle[y][DFL] = DFR;
+ corner_cycle[y][DBL] = DFL;
+ corner_cycle[y][DBR] = DBL;
+
+ center_cycle[U][U_center] = U_center;
+ center_cycle[U][D_center] = D_center;
+ center_cycle[U][R_center] = R_center;
+ center_cycle[U][L_center] = L_center;
+ center_cycle[U][F_center] = F_center;
+ center_cycle[U][B_center] = B_center;
+
+ center_cycle[x][U_center] = F_center;
+ center_cycle[x][D_center] = B_center;
+ center_cycle[x][R_center] = R_center;
+ center_cycle[x][L_center] = L_center;
+ center_cycle[x][F_center] = D_center;
+ center_cycle[x][B_center] = U_center;
+
+ center_cycle[y][U_center] = U_center;
+ center_cycle[y][D_center] = D_center;
+ center_cycle[y][R_center] = B_center;
+ center_cycle[y][L_center] = F_center;
+ center_cycle[y][F_center] = R_center;
+ center_cycle[y][B_center] = L_center;
+
+ /* Flipped pieces *************/
+ eofb_flipped[x][UF] = 1;
+ eofb_flipped[x][UB] = 1;
+ eofb_flipped[x][DF] = 1;
+ eofb_flipped[x][DB] = 1;
+
+ eofb_flipped[y][FR] = 1;
+ eofb_flipped[y][FL] = 1;
+ eofb_flipped[y][BL] = 1;
+ eofb_flipped[y][BR] = 1;
+
+ eorl_flipped[x][UF] = 1;
+ eorl_flipped[x][UL] = 1;
+ eorl_flipped[x][UB] = 1;
+ eorl_flipped[x][UR] = 1;
+ eorl_flipped[x][DF] = 1;
+ eorl_flipped[x][DL] = 1;
+ eorl_flipped[x][DB] = 1;
+ eorl_flipped[x][DR] = 1;
+ eorl_flipped[x][FR] = 1;
+ eorl_flipped[x][FL] = 1;
+ eorl_flipped[x][BL] = 1;
+ eorl_flipped[x][BR] = 1;
+
+ eorl_flipped[y][FR] = 1;
+ eorl_flipped[y][FL] = 1;
+ eorl_flipped[y][BL] = 1;
+ eorl_flipped[y][BR] = 1;
+
+ eoud_flipped[U][UF] = 1;
+ eoud_flipped[U][UL] = 1;
+ eoud_flipped[U][UB] = 1;
+ eoud_flipped[U][UR] = 1;
+
+ eoud_flipped[x][UF] = 1;
+ eoud_flipped[x][UB] = 1;
+ eoud_flipped[x][DF] = 1;
+ eoud_flipped[x][DB] = 1;
+
+ eoud_flipped[y][UF] = 1;
+ eoud_flipped[y][UL] = 1;
+ eoud_flipped[y][UB] = 1;
+ eoud_flipped[y][UR] = 1;
+ eoud_flipped[y][DF] = 1;
+ eoud_flipped[y][DL] = 1;
+ eoud_flipped[y][DB] = 1;
+ eoud_flipped[y][DR] = 1;
+ eoud_flipped[y][FR] = 1;
+ eoud_flipped[y][FL] = 1;
+ eoud_flipped[y][BL] = 1;
+ eoud_flipped[y][BR] = 1;
+
+ coud_flipped[x][UFR] = 2;
+ coud_flipped[x][UFL] = 1;
+ coud_flipped[x][UBR] = 1;
+ coud_flipped[x][UBL] = 2;
+ coud_flipped[x][DFR] = 1;
+ coud_flipped[x][DFL] = 2;
+ coud_flipped[x][DBR] = 2;
+ coud_flipped[x][DBL] = 1;
+
+ corl_flipped[U][UFR] = 1;
+ corl_flipped[U][UFL] = 2;
+ corl_flipped[U][UBL] = 1;
+ corl_flipped[U][UBR] = 2;
+
+ corl_flipped[y][UFR] = 1;
+ corl_flipped[y][UFL] = 2;
+ corl_flipped[y][UBL] = 1;
+ corl_flipped[y][UBR] = 2;
+ corl_flipped[y][DFR] = 2;
+ corl_flipped[y][DFL] = 1;
+ corl_flipped[y][DBL] = 2;
+ corl_flipped[y][DBR] = 1;
+
+ cofb_flipped[U][UFR] = 2;
+ cofb_flipped[U][UFL] = 1;
+ cofb_flipped[U][UBL] = 2;
+ cofb_flipped[U][UBR] = 1;
+
+ cofb_flipped[x][UFR] = 1;
+ cofb_flipped[x][UFL] = 2;
+ cofb_flipped[x][UBL] = 1;
+ cofb_flipped[x][UBR] = 2;
+ cofb_flipped[x][DFR] = 2;
+ cofb_flipped[x][DFL] = 1;
+ cofb_flipped[x][DBL] = 2;
+ cofb_flipped[x][DBR] = 1;
+
+ cofb_flipped[y][UFR] = 2;
+ cofb_flipped[y][UFL] = 1;
+ cofb_flipped[y][UBL] = 2;
+ cofb_flipped[y][UBR] = 1;
+ cofb_flipped[y][DFR] = 1;
+ cofb_flipped[y][DFL] = 2;
+ cofb_flipped[y][DBL] = 1;
+ cofb_flipped[y][DBR] = 2;
+
+ /* Equivalent moves ***********/
+ equiv_alg[NULLMOVE] = new_alg("");
+
+ equiv_alg[U] = new_alg(" U ");
+ equiv_alg[U2] = new_alg(" UU ");
+ equiv_alg[U3] = new_alg(" UUU ");
+ equiv_alg[D] = new_alg(" xx U xx ");
+ equiv_alg[D2] = new_alg(" xx UU xx ");
+ equiv_alg[D3] = new_alg(" xx UUU xx ");
+ equiv_alg[R] = new_alg(" yx U xxxyyy ");
+ equiv_alg[R2] = new_alg(" yx UU xxxyyy ");
+ equiv_alg[R3] = new_alg(" yx UUU xxxyyy ");
+ equiv_alg[L] = new_alg(" yyyx U xxxy ");
+ equiv_alg[L2] = new_alg(" yyyx UU xxxy ");
+ equiv_alg[L3] = new_alg(" yyyx UUU xxxy ");
+ equiv_alg[F] = new_alg(" x U xxx ");
+ equiv_alg[F2] = new_alg(" x UU xxx ");
+ equiv_alg[F3] = new_alg(" x UUU xxx ");
+ equiv_alg[B] = new_alg(" xxx U x ");
+ equiv_alg[B2] = new_alg(" xxx UU x ");
+ equiv_alg[B3] = new_alg(" xxx UUU x ");
+
+ equiv_alg[Uw] = new_alg(" xx U xx y ");
+ equiv_alg[Uw2] = new_alg(" xx UU xx yy ");
+ equiv_alg[Uw3] = new_alg(" xx UUU xx yyy ");
+ equiv_alg[Dw] = new_alg(" U yyy ");
+ equiv_alg[Dw2] = new_alg(" UU yy ");
+ equiv_alg[Dw3] = new_alg(" UUU y ");
+ equiv_alg[Rw] = new_alg(" yyyx U xxxy x ");
+ equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx ");
+ equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx ");
+ equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx ");
+ equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx ");
+ equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x ");
+ equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy ");
+ equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy ");
+ equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy ");
+ equiv_alg[Bw] = new_alg(" x U xxx yxyyy ");
+ equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy ");
+ equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy ");
+
+ equiv_alg[M] = new_alg(" yx U xx UUU yxyyy ");
+ equiv_alg[M2] = new_alg(" yx UU xx UU xxxy ");
+ equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy ");
+ equiv_alg[S] = new_alg(" x UUU xx U yyyx ");
+ equiv_alg[S2] = new_alg(" x UU xx UU yyx ");
+ equiv_alg[S3] = new_alg(" x U xx UUU yx ");
+ equiv_alg[E] = new_alg(" U xx UUU xxyyy ");
+ equiv_alg[E2] = new_alg(" UU xx UU xxyy ");
+ equiv_alg[E3] = new_alg(" UUU xx U xxy ");
+
+ equiv_alg[x] = new_alg(" x ");
+ equiv_alg[x2] = new_alg(" xx ");
+ equiv_alg[x3] = new_alg(" xxx ");
+ equiv_alg[y] = new_alg(" y ");
+ equiv_alg[y2] = new_alg(" yy ");
+ equiv_alg[y3] = new_alg(" yyy ");
+ equiv_alg[z] = new_alg(" yyy x y ");
+ equiv_alg[z2] = new_alg(" yy xx ");
+ equiv_alg[z3] = new_alg(" y x yyy ");
+}
+
+static void
+init_strings()
+{
+ strcpy(move_string [NULLMOVE], "-" );
+ strcpy(move_string [U], "U" );
+ strcpy(move_string [U2], "U2" );
+ strcpy(move_string [U3], "U\'" );
+ strcpy(move_string [D], "D" );
+ strcpy(move_string [D2], "D2" );
+ strcpy(move_string [D3], "D\'" );
+ strcpy(move_string [R], "R" );
+ strcpy(move_string [R2], "R2" );
+ strcpy(move_string [R3], "R\'" );
+ strcpy(move_string [L], "L" );
+ strcpy(move_string [L2], "L2" );
+ strcpy(move_string [L3], "L\'" );
+ strcpy(move_string [F], "F" );
+ strcpy(move_string [F2], "F2" );
+ strcpy(move_string [F3], "F\'" );
+ strcpy(move_string [B], "B" );
+ strcpy(move_string [B2], "B2" );
+ strcpy(move_string [B3], "B\'" );
+ strcpy(move_string [Uw], "Uw" );
+ strcpy(move_string [Uw2], "Uw2" );
+ strcpy(move_string [Uw3], "Uw\'" );
+ strcpy(move_string [Dw], "Dw" );
+ strcpy(move_string [Dw2], "Dw2" );
+ strcpy(move_string [Dw3], "Dw\'" );
+ strcpy(move_string [Rw], "Rw" );
+ strcpy(move_string [Rw2], "Rw2" );
+ strcpy(move_string [Rw3], "Rw\'" );
+ strcpy(move_string [Lw], "Lw" );
+ strcpy(move_string [Lw2], "Lw2" );
+ strcpy(move_string [Lw3], "Lw\'" );
+ strcpy(move_string [Fw], "Fw" );
+ strcpy(move_string [Fw2], "Fw2" );
+ strcpy(move_string [Fw3], "Fw\'" );
+ strcpy(move_string [Bw], "Bw" );
+ strcpy(move_string [Bw2], "Bw2" );
+ strcpy(move_string [Bw3], "Bw\'" );
+ strcpy(move_string [M], "M" );
+ strcpy(move_string [M2], "M2" );
+ strcpy(move_string [M3], "M\'" );
+ strcpy(move_string [S], "S" );
+ strcpy(move_string [S2], "S2" );
+ strcpy(move_string [S3], "S\'" );
+ strcpy(move_string [E], "E" );
+ strcpy(move_string [E2], "E2" );
+ strcpy(move_string [E3], "E\'" );
+ strcpy(move_string [x], "x" );
+ strcpy(move_string [x2], "x2" );
+ strcpy(move_string [x3], "x\'" );
+ strcpy(move_string [y], "y" );
+ strcpy(move_string [y2], "y2" );
+ strcpy(move_string [y3], "y\'" );
+ strcpy(move_string [z], "z" );
+ strcpy(move_string [z2], "z2" );
+ strcpy(move_string [z3], "z\'" );
+
+ strcpy(edge_string [UF], "UF" );
+ strcpy(edge_string [UL], "UL" );
+ strcpy(edge_string [UB], "UB" );
+ strcpy(edge_string [UR], "UR" );
+ strcpy(edge_string [DF], "DF" );
+ strcpy(edge_string [DL], "DL" );
+ strcpy(edge_string [DB], "DB" );
+ strcpy(edge_string [DR], "DR" );
+ strcpy(edge_string [FR], "FR" );
+ strcpy(edge_string [FL], "FL" );
+ strcpy(edge_string [BL], "BL" );
+ strcpy(edge_string [BR], "BR" );
+
+ strcpy(corner_string [UFR], "UFR" );
+ strcpy(corner_string [UFL], "UFL" );
+ strcpy(corner_string [UBL], "UBL" );
+ strcpy(corner_string [UBR], "UBR" );
+ strcpy(corner_string [DFR], "DFR" );
+ strcpy(corner_string [DFL], "DFL" );
+ strcpy(corner_string [DBL], "DBL" );
+ strcpy(corner_string [DBR], "DBR" );
+
+ strcpy(center_string [U_center], "U" );
+ strcpy(center_string [D_center], "D" );
+ strcpy(center_string [R_center], "R" );
+ strcpy(center_string [L_center], "L" );
+ strcpy(center_string [F_center], "F" );
+ strcpy(center_string [B_center], "B" );
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+static void
+init_trans() {
+ Cube aux, cube, mirr, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move_aux[move], aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube, false) ||
+ is_solved(mirr, false))
+ moves_ttable[m][mi] = move;
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static void
+init_trans_aux()
+{
+ ep_mirror[UF] = UF;
+ ep_mirror[UL] = UR;
+ ep_mirror[UB] = UB;
+ ep_mirror[UR] = UL;
+ ep_mirror[DF] = DF;
+ ep_mirror[DL] = DR;
+ ep_mirror[DB] = DB;
+ ep_mirror[DR] = DL;
+ ep_mirror[FR] = FL;
+ ep_mirror[FL] = FR;
+ ep_mirror[BR] = BL;
+ ep_mirror[BL] = BR;
+
+ cp_mirror[UFR] = UFL;
+ cp_mirror[UFL] = UFR;
+ cp_mirror[UBL] = UBR;
+ cp_mirror[UBR] = UBL;
+ cp_mirror[DFR] = DFL;
+ cp_mirror[DFL] = DFR;
+ cp_mirror[DBL] = DBR;
+ cp_mirror[DBR] = DBL;
+
+ cpos_mirror[U_center] = U_center;
+ cpos_mirror[D_center] = D_center;
+ cpos_mirror[R_center] = L_center;
+ cpos_mirror[L_center] = R_center;
+ cpos_mirror[F_center] = F_center;
+ cpos_mirror[B_center] = B_center;
+
+ /* Is there a more elegant way? */
+ rotation_algs[uf] = new_alg("");
+ rotation_algs[ur] = new_alg("y");
+ rotation_algs[ub] = new_alg("y2");
+ rotation_algs[ul] = new_alg("y3");
+
+ rotation_algs[df] = new_alg("z2");
+ rotation_algs[dr] = new_alg("y z2");
+ rotation_algs[db] = new_alg("x2");
+ rotation_algs[dl] = new_alg("y3 z2");
+
+ rotation_algs[rf] = new_alg("z3");
+ rotation_algs[rd] = new_alg("z3 y");
+ rotation_algs[rb] = new_alg("z3 y2");
+ rotation_algs[ru] = new_alg("z3 y3");
+
+ rotation_algs[lf] = new_alg("z");
+ rotation_algs[ld] = new_alg("z y3");
+ rotation_algs[lb] = new_alg("z y2");
+ rotation_algs[lu] = new_alg("z y");
+
+ rotation_algs[fu] = new_alg("x y2");
+ rotation_algs[fr] = new_alg("x y");
+ rotation_algs[fd] = new_alg("x");
+ rotation_algs[fl] = new_alg("x y3");
+
+ rotation_algs[bu] = new_alg("x3");
+ rotation_algs[br] = new_alg("x3 y");
+ rotation_algs[bd] = new_alg("x3 y2");
+ rotation_algs[bl] = new_alg("x3 y3");
+}
+
+
+/* Public functions implementation *******************************************/
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+void
+genptable(PruneData *pd)
+{
+ Move ms[NMOVES];
+ int d;
+ uint64_t j, oldn = 0;
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe crash gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ moveset_to_list(pd->moveset, NULL, ms);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update(pd, pd->coord->cube(j), 15);
+
+ /*TODO: change, set to 0 for every solved state (might be more than 1)*/
+ ptable_update(pd, (Cube){0}, 0);
+ pd->n = 1;
+
+ for (d = 0; d < 15 && pd->n < pd->coord->max; d++) {
+ genptable_bfs(pd, d, ms);
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ d, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+
+ pd->generated = true;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return inverse_move_aux[m];
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ return inverse_trans_aux[t];
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube, bool reorient)
+{
+ int i;
+
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+/*
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[arr->ep[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %c ", arr->eofb[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[arr->cp[i]]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %c ", arr->coud[i] + '0');
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[arr->cpos[i]]);
+ printf("\n");
+
+ free_cubearray(arr, pf_all);
+*/
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+/* TODO: clean pre_trans or put it back */
+AlgList *
+solve(Cube cube, Step step, SolveOptions *opts)
+{
+ /*AlgListNode *node;*/
+ AlgList *sols = new_alglist();
+ /*Cube c = apply_trans(opts->pre_trans, cube);*/
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step.ready != NULL && !step.ready(cube)) {
+ fprintf(stderr, "Cube not ready for solving step\n");
+ return sols;
+ }
+
+ moveset_to_list(step.moveset, step.estimate, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves && !(sols->len && opts->optimal_only);
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(cube, step, opts, &dd);
+ }
+
+/*
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(opts->pre_trans), node->alg);
+*/
+
+ free_alg(dd.current_alg);
+ return sols;
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string[j][0]) {
+ m = j;
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1]=='\'' || str[i+1]=='3') {
+ m += 2;
+ i++;
+ }
+ append_move(alg, m, niss);
+ break;
+ }
+ }
+ }
+
+ return alg;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string[alg->move[i]]);
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval(pd, pd->coord->cube(i))]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ return ptableval_index(pd, pd->coord->index(cube));
+}
+
+Alg *
+rotation_alg(Trans i)
+{
+ return rotation_algs[i % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ return what_center_at_aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ return what_corner_at_aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return what_orientation_last_corner_aux[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return what_orientation_last_edge_aux[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ return where_is_center_aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ return where_is_corner_aux[cube.cp][c];
+}
+
+
+void
+init()
+{
+ /* Order is important! */
+ init_environment();
+ init_strings();
+ init_moves_aux();
+ init_moves();
+ init_auxtables();
+ init_cphtr_cosets();
+ init_trans_aux();
+ init_trans();
+ init_symdata();
+}
+
diff --git a/old/2021-07-15-almostbeforerefactor/cube.h b/old/2021-07-15-almostbeforerefactor/cube.h
@@ -0,0 +1,79 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "macros.h"
+#include "cubetypes.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_drud;
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_khuge;
+
+extern Trans trans_group_udfix[16];
+extern Trans trans_group_trivial[1];
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_move(Move m, Cube cube);
+Cube apply_trans(Trans t, Cube cube);
+bool block_solved(Cube cube, Block);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */
+Cube anti_cphtr(uint64_t ind); /*TODO also this */
+uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+Move inverse_move(Move m);
+Trans inverse_trans(Trans t);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube, bool reorient);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void print_cube(Cube cube);
+Cube random_cube();
+AlgList * solve(Cube cube, Step step, SolveOptions *opts);
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+void init();
+
+#endif
+
diff --git a/old/2021-07-15-almostbeforerefactor/cubetypes.h b/old/2021-07-15-almostbeforerefactor/cubetypes.h
@@ -0,0 +1,252 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ Checker check;
+ uint64_t max;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+ int ntrans;
+ Trans * trans;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback;
+};
+
+struct
+step
+{
+ Estimator estimate;
+ Checker ready;
+ Moveset moveset;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/old/2021-07-15-almostbeforerefactor/macros.h b/old/2021-07-15-almostbeforerefactor/macros.h
@@ -0,0 +1,23 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define NMOVES (z3+1)
+#define NTRANS 48
+#define NROTATIONS 24
+
+#endif
diff --git a/old/2021-07-15-almostbeforerefactor/main.c b/old/2021-07-15-almostbeforerefactor/main.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include "cube.h"
+#include "steps.h"
+
+int main() {
+ Alg *algo;
+ AlgList *sols;
+ Cube cube;
+ SolveOptions opts;
+ char line[1000];
+ int i, ns = 1;
+/* Move m;*/
+/* int nrand = 10000, sum1, sum2;*/
+
+ Step *stps[20] = {&optimal_HTM};
+ char sss[30][30] = {"Optimal solve"};
+
+ opts = (SolveOptions) {
+ .min_moves = 0,
+ .max_moves = 20,
+ .optimal_only = true,
+ .max_solutions = 1,
+ .can_niss = false,
+ .feedback = true,
+ };
+
+ init();
+
+/*
+ srand(time(NULL));
+ sum1 = 0;
+ sum2 = 0;
+ for (i = 0; i < nrand; i++) {
+ cube = random_cube();
+ sum1 += drud_HTM.estimate((CubeTarget){.cube = cube, .target = 20});
+ sum2 += optimal_HTM.estimate((CubeTarget){.cube = cube, .target = 20});
+ }
+ printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand));
+ printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand));
+*/
+
+
+ printf("Welcome to nissy 2.0! Insert a scramble:\n");
+
+ if (fgets(line, 1000, stdin) != NULL) {
+ algo = new_alg(line);
+ cube = apply_alg(algo, (Cube){0});
+
+ for (i = 0; i < ns; i++) {
+ sols = solve(cube, *stps[i], &opts);
+ printf("%s: %d solutions found:\n", sss[i], sols->len);
+ print_alglist(sols, true);
+ free_alglist(sols);
+ }
+ free_alg(algo);
+ }
+
+ return 0;
+}
+
diff --git a/old/2021-07-15-almostbeforerefactor/nissy b/old/2021-07-15-almostbeforerefactor/nissy
Binary files differ.
diff --git a/old/2021-07-15-almostbeforerefactor/steps.c b/old/2021-07-15-almostbeforerefactor/steps.c
@@ -0,0 +1,284 @@
+#include "steps.h"
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .estimate = estimate_coud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .estimate = estimate_coud_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .estimate = estimate_corners_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_HTM = {
+ .estimate = estimate_cornershtr_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+cornershtr_URF = {
+ .estimate = estimate_cornershtr_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+corners_URF = {
+ .estimate = estimate_corners_URF,
+ .ready = check_nothing,
+ .moveset = moveset_URF
+};
+
+Step
+drud_HTM = {
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM,
+ .ntrans = 1,
+ .trans = trans_group_trivial
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM,
+ .ntrans = 1,
+ .trans = trans_group_trivial
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "ptable_cornershtr_withcosets_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM,
+ .ntrans = 1,
+ .trans = trans_group_trivial
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM,
+ .ntrans = 1,
+ .trans = trans_group_trivial
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .coord = &coord_drud,
+ .moveset = moveset_HTM,
+ .ntrans = 1,
+ .trans = trans_group_trivial
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "ptable_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "ptable_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ if (!pd_eofb_HTM.generated)
+ genptable(&pd_eofb_HTM);
+
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ if (!pd_coud_HTM.generated)
+ genptable(&pd_coud_HTM);
+
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ if (!pd_corners_HTM.generated)
+ genptable(&pd_corners_HTM);
+
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ if (!pd_cornershtr_HTM.generated)
+ genptable(&pd_cornershtr_HTM);
+
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+/*
+ if (!pd_drud_HTM.generated)
+ genptable(&pd_drud_HTM);
+
+ return ptableval(&pd_drud_HTM, ct.cube);
+*/
+
+ if (!pd_drud_sym16_HTM.generated)
+ genptable(&pd_drud_sym16_HTM);
+
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ if (!pd_khuge_HTM.generated)
+ genptable(&pd_khuge_HTM);
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(rf, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(fd, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ /* Michiel de Bondt's trick */
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ return MAX(ret, dr3);
+}
diff --git a/old/2021-07-15-almostbeforerefactor/steps.h b/old/2021-07-15-almostbeforerefactor/steps.h
@@ -0,0 +1,24 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+extern Step eofb_HTM;
+extern Step coud_HTM;
+extern Step coud_URF;
+extern Step corners_HTM;
+extern Step cornershtr_HTM;
+extern Step corners_URF;
+extern Step cornershtr_URF;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_cornershtreofb_HTM;
+extern PruneData pd_drud_HTM;
+extern PruneData pd_khuge_HTM;
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/alg.c b/old/2021-11-10-beforeremovingchecker/alg.c
@@ -0,0 +1,366 @@
+#include "alg.h"
+
+/* Local functions ***********************************************************/
+
+static void free_alglistnode(AlgListNode *aln);
+static void realloc_alg(Alg *alg, int n);
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+bool
+moveset_eofb(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == D || b == R || b == L ||
+ ((b == F || b == B) && m == b+1);
+}
+
+bool
+moveset_drud(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == D ||
+ ((b == R || b == L || b == F || b == B) && m == b + 1);
+}
+
+bool
+moveset_htr(Move m)
+{
+ Move b = base_move(m);
+
+ return moveset_HTM(m) && m == b + 1;
+}
+
+
+/* Functions *****************************************************************/
+
+void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+void
+compose_alg(Alg *alg1, Alg *alg2)
+{
+ int i;
+
+ for (i = 0; i < alg2->len; i++)
+ append_move(alg1, alg2->move[i], alg2->inv[i]);
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+static void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3);
+}
+
+char *
+move_string(Move m)
+{
+ static char move_string_aux[NMOVES][7] = {
+ [NULLMOVE] = "-",
+ [U] = "U", [U2] = "U2", [U3] = "U\'",
+ [D] = "D", [D2] = "D2", [D3] = "D\'",
+ [R] = "R", [R2] = "R2", [R3] = "R\'",
+ [L] = "L", [L2] = "L2", [L3] = "L\'",
+ [F] = "F", [F2] = "F2", [F3] = "F\'",
+ [B] = "B", [B2] = "B2", [B3] = "B\'",
+ [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'",
+ [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'",
+ [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'",
+ [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'",
+ [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'",
+ [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'",
+ [M] = "M", [M2] = "M2", [M3] = "M\'",
+ [E] = "E", [E2] = "E2", [E3] = "E\'",
+ [S] = "S", [S2] = "S2", [S3] = "S\'",
+ [x] = "x", [x2] = "x2", [x3] = "x\'",
+ [y] = "y", [y2] = "y2", [y3] = "y\'",
+ [z] = "z", [z2] = "z2", [z3] = "z\'",
+ };
+
+ return move_string_aux[m];
+}
+
+void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+void
+moveset_to_list(Moveset ms, Move *r)
+{
+ int n = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++)
+ if (ms(i))
+ r[n++] = i;
+
+ r[n] = NULLMOVE;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false, move_read;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ move_read = false;
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string(j)[0] ||
+ (str[i] >= 'a' && str[i] <= 'z' &&
+ str[i] == move_string(j)[0]-('A'-'a') && j<=B)) {
+ m = j;
+ if (str[i] >= 'a' && str[i] <= 'z' && j<=B) {
+ m += Uw - U;
+ }
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1] == '\'' ||
+ str[i+1] == '3' ||
+ str[i+1] == '`' ) {
+ m += 2;
+ i++;
+ } else if ((int)str[i+1] == -62 &&
+ (int)str[i+2] == -76) {
+ /* Weird apostrophe */
+ m += 2;
+ i += 2;
+ } else if ((int)str[i+1] == -30 &&
+ (int)str[i+2] == -128 &&
+ (int)str[i+3] == -103) {
+ /* MacOS apostrophe */
+ m += 2;
+ i += 3;
+ }
+ append_move(alg, m, niss);
+ move_read = true;
+ break;
+ }
+ }
+
+ if (!move_read) {
+ alg = new_alg("");
+ return alg;
+ }
+ }
+
+ return alg;
+}
+
+AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ /* TODO: make it possible to print to stdout or to string */
+ /* Maybe just return a string */
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string(alg->move[i]));
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
diff --git a/old/2021-11-10-beforeremovingchecker/alg.h b/old/2021-11-10-beforeremovingchecker/alg.h
@@ -0,0 +1,35 @@
+#ifndef ALG_H
+#define ALG_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cubetypes.h"
+#include "utils.h"
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+bool moveset_eofb(Move m);
+bool moveset_drud(Move m);
+bool moveset_htr(Move m);
+
+void append_alg(AlgList *l, Alg *alg);
+void append_move(Alg *alg, Move m, bool inverse);
+void compose_alg(Alg *alg1, Alg *alg2);
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Move inverse_move(Move m);
+char * move_string(Move m);
+void movelist_to_position(Move *ml, int *pos);
+void moveset_to_list(Moveset ms, Move *lst);
+Alg * new_alg(char *str);
+AlgList * new_alglist();
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+
+#endif
+
diff --git a/old/2021-11-10-beforeremovingchecker/commands.c b/old/2021-11-10-beforeremovingchecker/commands.c
@@ -0,0 +1,329 @@
+#include "commands.h"
+
+/* Arg parsing functions *****************************************************/
+
+CommandArgs * solvestep_parse_args(int c, char **v);
+CommandArgs * help_parse_args(int c, char **v);
+CommandArgs * print_parse_args(int c, char **v);
+CommandArgs * parse_no_arg(int c, char **v);
+
+/* Exec functions ************************************************************/
+
+static void solvestep_exec(CommandArgs *args);
+static void steps_exec(CommandArgs *args);
+static void commands_exec(CommandArgs *args);
+static void print_exec(CommandArgs *args);
+static void help_exec(CommandArgs *args);
+static void quit_exec(CommandArgs *args);
+
+/* Local functions ***********************************************************/
+
+static bool read_step(CommandArgs *args, char *str);
+static bool read_scramble(int c, char **v, CommandArgs *args);
+
+/* Commands ******************************************************************/
+
+Command
+solvestep_cmd = {
+ .name = "solve",
+ .usage = "solve STEP [OPTIONS] SCRAMBLE",
+ .description = "Solve a step",
+ .parse_args = solvestep_parse_args,
+ .exec = solvestep_exec
+};
+
+Command
+steps_cmd = {
+ .name = "steps",
+ .usage = "steps",
+ .description = "List available steps",
+ .parse_args = parse_no_arg,
+ .exec = steps_exec
+};
+
+Command
+commands_cmd = {
+ .name = "commands",
+ .usage = "commands",
+ .description = "List available commands",
+ .parse_args = parse_no_arg,
+ .exec = commands_exec
+};
+
+Command
+print_cmd = {
+ .name = "print",
+ .usage = "print SCRAMBLE",
+ .description = "Print written description of the cube",
+ .parse_args = print_parse_args,
+ .exec = print_exec,
+};
+
+Command
+help_cmd = {
+ .name = "help",
+ .usage = "help [COMMAND]",
+ .description = "Display nissy manual page or help on specific command",
+ .parse_args = help_parse_args,
+ .exec = help_exec,
+};
+
+Command
+quit_cmd = {
+ .name = "quit",
+ .usage = "quit",
+ .description = "Quit nissy",
+ .parse_args = parse_no_arg,
+ .exec = quit_exec,
+};
+
+Command *commands[NCOMMANDS] = {
+ &solvestep_cmd,
+ &steps_cmd,
+ &commands_cmd,
+ &help_cmd,
+ &print_cmd,
+ &quit_cmd
+};
+
+/* Arg parsing functions implementation **************************************/
+
+CommandArgs *
+solvestep_parse_args(int c, char **v)
+{
+ int i;
+ long val;
+
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->success = false;
+ a->opts = malloc(sizeof(SolveOptions));
+ a->step = steps[0];
+ a->command = NULL;
+ a->scramble = NULL;
+
+ a->opts->min_moves = 0;
+ a->opts->max_moves = 20;
+ a->opts->max_solutions = 1;
+ a->opts->optimal_only = false;
+ a->opts->can_niss = false;
+ a->opts->feedback = false;
+ a->opts->all = false;
+ a->opts->print_number = true;
+
+ for (i = 0; i < c; i++) {
+ if (!strcmp(v[i], "-m")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 0 || val > 100) {
+ fprintf(stderr,
+ "Invalid min number of moves.\n");
+ return a;
+ }
+ a->opts->min_moves = val;
+ } else if (!strcmp(v[i], "-M")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 0 || val > 100) {
+ fprintf(stderr,
+ "Invalid max number of moves.\n");
+ return a;
+ }
+ a->opts->max_moves = val;
+ } else if (!strcmp(v[i], "-s")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 1 || val > 1000000) {
+ fprintf(stderr,
+ "Invalid number of solutions.\n");
+ return a;
+ }
+ a->opts->max_solutions = val;
+ } else if (!strcmp(v[i], "-o")) {
+ a->opts->optimal_only = true;
+ } else if (!strcmp(v[i], "-n")) {
+ a->opts->can_niss = true;
+ } else if (!strcmp(v[i], "-v")) {
+ a->opts->feedback = true;
+ } else if (!strcmp(v[i], "-a")) {
+ a->opts->all = true;
+ } else if (!strcmp(v[i], "-p")) {
+ a->opts->print_number = false;
+ } else if (!read_step(a, v[i])) {
+ break;
+ }
+ }
+
+ a->success = read_scramble(c-i, &v[i], a);
+ return a;
+}
+
+CommandArgs *
+help_parse_args(int c, char **v)
+{
+ int i;
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->scramble = NULL;
+ a->opts = NULL;
+ a->step = NULL;
+ a->command = NULL;
+
+ if (c == 1) {
+ for (i = 0; i < NCOMMANDS; i++)
+ if (commands[i] != NULL &&
+ !strcmp(v[0], commands[i]->name))
+ a->command = commands[i];
+ if (a->command == NULL)
+ fprintf(stderr, "%s: command not found\n", v[0]);
+ }
+
+ a->success = c == 0 || (c == 1 && a->command != NULL);
+ return a;
+}
+
+CommandArgs *
+parse_no_arg(int c, char **v)
+{
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->scramble = NULL;
+ a->opts = NULL;
+ a->step = NULL;
+ a->command = NULL;
+
+ return a;
+}
+
+CommandArgs *
+print_parse_args(int c, char **v)
+{
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->opts = NULL;
+ a->step = NULL;
+ a->command = NULL;
+
+ a->success = read_scramble(c-1, &v[1], a);
+ return a;
+}
+
+/* Exec functions implementation *********************************************/
+
+static void
+solvestep_exec(CommandArgs *args)
+{
+ Cube c = apply_alg(args->scramble, (Cube){0});
+ AlgList *sols = solve(c, args->step, args->opts);
+ print_alglist(sols, args->opts->print_number);
+ free_alglist(sols);
+}
+
+static void
+steps_exec(CommandArgs *args)
+{
+ int i;
+
+ for (i = 0; i < NSTEPS && steps[i] != NULL; i++)
+ printf("%-15s %s\n", steps[i]->shortname, steps[i]->name);
+}
+
+static void
+commands_exec(CommandArgs *args)
+{
+ int i;
+
+ for (i = 0; i < NCOMMANDS && commands[i] != NULL; i++)
+ printf("%s\n", commands[i]->usage);
+
+}
+
+static void
+print_exec(CommandArgs *args)
+{
+ print_cube(apply_alg(args->scramble, (Cube){0}));
+}
+
+static void
+help_exec(CommandArgs *args)
+{
+ /* TODO: print full nissy manpage */
+ if (args->command == NULL) {
+ printf("Type help COMMAND for information on a ");
+ printf("specific command.\n");
+ printf("A more complete manual page is work in progress.\n");
+ } else {
+ printf("Command %s: %s\nusage: %s\n", args->command->name,
+ args->command->description, args->command->usage);
+ }
+}
+
+static void
+quit_exec(CommandArgs *args)
+{
+ exit(0);
+}
+
+/* Local functions implementation ********************************************/
+
+static bool
+read_step(CommandArgs *args, char *str)
+{
+ int i;
+
+ for (i = 0; i < NSTEPS; i++) {
+ if (steps[i] != NULL && !strcmp(steps[i]->shortname, str)) {
+ args->step = steps[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool
+read_scramble(int c, char **v, CommandArgs *args)
+{
+ int i, k, n;
+ unsigned int j;
+ char *algstr;
+
+ if (new_alg(v[0])->len == 0) {
+ fprintf(stderr, "%s: moves or option unrecognized\n", v[0]);
+ return false;
+ }
+
+ n = 0;
+ for(i = 0; i < c; i++)
+ n += strlen(v[i]);
+
+ algstr = malloc((n + 1) * sizeof(char));
+ k = 0;
+ for (i = 0; i < c; i++)
+ for (j = 0; j < strlen(v[i]); j++)
+ algstr[k++] = v[i][j];
+ algstr[k] = 0;
+
+ args->scramble = new_alg(algstr);
+ free(algstr);
+
+ if (args->scramble->len == 0)
+ fprintf(stderr, "Error reading scramble\n");
+
+ return args->scramble->len > 0;
+}
+
+/* Public functions implementation *******************************************/
+
+void
+free_args(CommandArgs *args)
+{
+ if (args == NULL)
+ return;
+
+ if (args->scramble != NULL)
+ free_alg(args->scramble);
+ if (args->opts != NULL)
+ free(args->opts);
+
+ /* step and command must not be freed, they are static! */
+
+ free(args);
+}
diff --git a/old/2021-11-10-beforeremovingchecker/commands.h b/old/2021-11-10-beforeremovingchecker/commands.h
@@ -0,0 +1,13 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+#include "solve.h"
+#include "steps.h"
+
+#define NCOMMANDS 10
+
+void free_args(CommandArgs *args);
+
+extern Command * commands[NCOMMANDS];
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/coord.c b/old/2021-11-10-beforeremovingchecker/coord.c
@@ -0,0 +1,629 @@
+#include "coord.h"
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_epud(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cp(uint64_t ind);
+static Cube antindex_cphtr(uint64_t);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_cornershtrfin(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_drud_eofb(uint64_t ind);
+static Cube antindex_htr_drud(uint64_t ind);
+static Cube antindex_htrfin(uint64_t ind);
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_epud(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cp(Cube cube);
+static uint64_t index_cphtr(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_cornershtrfin(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_drud_eofb(Cube cube);
+static uint64_t index_htr_drud(Cube cube);
+static uint64_t index_htrfin(Cube cube);
+
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_cornershtrfin();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static int cornershtrfin_ind[FACTORIAL8];
+static int cornershtrfin_ant[24*24/6];
+
+/* Coordinates and their implementation **************************************/
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cp = {
+ .index = index_cp,
+ .cube = antindex_cp,
+ .check = check_cp,
+ .max = FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cphtr = {
+ .index = index_cphtr,
+ .cube = antindex_cphtr,
+ .check = check_cphtr,
+ .max = BINOM8ON4 * 6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cornershtrfin = {
+ .index = index_cornershtrfin,
+ .cube = antindex_cornershtrfin,
+ .check = check_cp,
+ .max = 24*24/6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_epud = {
+ .index = index_epud,
+ .cube = antindex_epud,
+ .check = check_epud,
+ .max = FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_htr_drud = {
+ .index = index_htr_drud,
+ .cube = antindex_htr_drud,
+ .check = check_drud,
+ .max = BINOM8ON4 * 6 * BINOM8ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_htrfin = {
+ .index = index_htrfin,
+ .cube = antindex_htrfin,
+ .check = check_htr,
+ .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */
+ .ntrans = 1,
+};
+
+Coordinate
+coord_drud_eofb = {
+ .index = index_drud_eofb,
+ .cube = antindex_drud_eofb,
+ .check = check_drud,
+ .max = POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+/* Functions *****************************************************************/
+
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ Cube ret = {0};
+
+ ret.eofb = ind % POW2TO11;
+ ret.epose = (ind / POW2TO11) * 24;
+
+ return ret;
+}
+
+static Cube
+antindex_epud(uint64_t ind)
+{
+ static bool initialized = false;
+ static Cube epud_aux[FACTORIAL8];
+ int a[12];
+ uint64_t ui;
+ CubeArray arr;
+
+ if (!initialized) {
+ a[FR] = FR;
+ a[FL] = FL;
+ a[BL] = BL;
+ a[BR] = BR;
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ index_to_perm(ui, 8, a);
+ arr.ep = a;
+ epud_aux[ui] = arrays_to_cube(&arr, pf_ep);
+ }
+
+ initialized = true;
+ }
+
+ return epud_aux[ind];
+}
+
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+static Cube
+antindex_cp(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.cp = ind;
+
+ return c;
+}
+
+static Cube
+antindex_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = antindex_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+static Cube
+antindex_cornershtrfin(uint64_t ind)
+{
+ return (Cube){ .cp = cornershtrfin_ant[ind] };
+}
+
+static Cube
+antindex_drud(uint64_t ind)
+{
+ uint64_t epos, eofb;
+ Cube c;
+
+ eofb = ind % POW2TO11;
+ epos = ind / (POW2TO11 * POW3TO7);
+ c = antindex_eofbepos(eofb + POW2TO11 * epos);
+
+ c.coud = (ind / POW2TO11) % POW3TO7;
+
+ return c;
+}
+
+static Cube
+antindex_drud_eofb(uint64_t ind)
+{
+ return antindex_drud(ind * POW2TO11);
+}
+
+static Cube
+antindex_htr_drud(uint64_t ind)
+{
+ Cube ret;
+
+ ret = antindex_cphtr(ind / BINOM8ON4);
+ ret.eposs = (ind % BINOM8ON4) * FACTORIAL4;
+
+ return ret;
+}
+
+static Cube
+antindex_htrfin(uint64_t ind)
+{
+ Cube ret;
+
+ ret = antindex_cornershtrfin(ind/(24*24*24));
+
+ ret.eposm = ind % 24;
+ ind /= 24;
+ ret.eposs = ind % 24;
+ ind /= 24;
+ ret.epose = ind % 24;
+
+ return ret;
+}
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cp(Cube cube)
+{
+ return cube.cp == 0;
+}
+
+bool
+check_cphtr(Cube cube)
+{
+ return index_cphtr(cube) == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && index_cphtr(cube) == 0;
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_htr(Cube cube)
+{
+ return check_cornershtr(cube) &&
+ cube.eofb == 0 && cube.eorl == 0 && cube.eoud == 0;
+}
+
+bool
+check_drudfin_noE(Cube cube)
+{
+ return cube.eposs == 0 && cube.eposm == 0 && cube.cp == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_epud(Cube cube)
+{
+ return cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_epud(Cube cube)
+{
+ uint64_t ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = perm_to_index(arr->ep, 8);
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cp(Cube cube)
+{
+ return cube.cp;
+}
+
+static uint64_t
+index_cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube);
+}
+
+static uint64_t
+index_cornershtrfin(Cube cube)
+{
+ return cornershtrfin_ind[cube.cp];
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud_eofb(Cube cube)
+{
+ return index_drud(cube) / POW2TO11;
+}
+
+static uint64_t
+index_htr_drud(Cube cube)
+{
+ return index_cphtr(cube) * BINOM8ON4 +
+ (cube.eposs / FACTORIAL4) % BINOM8ON4;
+}
+
+static uint64_t
+index_htrfin(Cube cube)
+{
+ uint64_t epe, eps, epm, cp, ep;
+
+ epe = cube.epose % 24;
+ eps = cube.eposs % 24;
+ epm = cube.eposm % 24;
+ ep = (epe * 24 + eps) *24 + epm;
+ cp = index_cornershtrfin(cube);
+
+ return cp * 24 * 24 * 24 + ep;
+}
+
+/* Init functions implementation *********************************************/
+
+/*
+ * There is certainly a better way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ /*jj = cp_mtable[moves[k]][next[j]];*/
+ /* TODO fix formatting */
+ jj = apply_move(moves[k], (Cube){.cp=next[j]}).cp;
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_cornershtrfin()
+{
+ unsigned int i, j;
+ int n, c;
+ Move m;
+
+ for (i = 0; i < FACTORIAL8; i++)
+ cornershtrfin_ind[i] = -1;
+ cornershtrfin_ind[0] = 0;
+
+ /* 10-pass, I think 5 is enough, but just in case */
+ n = 1;
+ for (i = 0; i < 10; i++) {
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cornershtrfin_ind[j] == -1)
+ continue;
+ for (m = U; m < NMOVES; m++) {
+ if (moveset_htr(m)) {
+ c = apply_move(m, (Cube){.cp = j}).cp;
+ if (cornershtrfin_ind[c] == -1) {
+ cornershtrfin_ind[c] = n;
+ cornershtrfin_ant[n] = c;
+ n++;
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+init_coord()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ init_cphtr_cosets();
+ init_cornershtrfin();
+}
+
diff --git a/old/2021-11-10-beforeremovingchecker/coord.h b/old/2021-11-10-beforeremovingchecker/coord.h
@@ -0,0 +1,40 @@
+#ifndef COORD_H
+#define COORD_H
+
+#include "trans.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_cp;
+extern Coordinate coord_cphtr;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_cornershtrfin;
+extern Coordinate coord_epud;
+extern Coordinate coord_drud;
+extern Coordinate coord_drud_eofb;
+extern Coordinate coord_htr_drud;
+extern Coordinate coord_htrfin;
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cp(Cube cube);
+bool check_cphtr(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_htr(Cube cube);
+bool check_drudfin_noE(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_epud(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+void init_coord();
+
+#endif
+
diff --git a/old/2021-11-10-beforeremovingchecker/cube.c b/old/2021-11-10-beforeremovingchecker/cube.c
@@ -0,0 +1,716 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static int epos_from_arrays(int *epos, int *ep);
+
+/* Local functions implementation ********************************************/
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+/* Public functions implementation *******************************************/
+
+Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ static int epe_solved[4] = {FR, FL, BL, BR};
+ static int eps_solved[4] = {UL, UR, DL, DR};
+ static int epm_solved[4] = {UF, UB, DF, DB};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ static int epe_solved[4] = {FR, FL, BL, BR};
+ static int eps_solved[4] = {UL, UR, DL, DR};
+ static int epm_solved[4] = {UF, UB, DF, DB};
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+
+/* TODO: consider if this is good here or better in coord.c
+ in any case it is used in transformation init at the moment */
+Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* TODO: check that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube)
+{
+ /* TODO: move somewhere else, like in solve.c
+ int i;
+ if (reorient) {
+ for (i = 0; i < NROTATIONS; i++)
+ if (is_solved(apply_alg(rotation_algs[i], cube),false))
+ return true;
+ return false;
+ } else {
+ return equal(cube, (Cube){0});
+ }
+ */
+
+ return equal(cube, (Cube){0});
+}
+
+bool
+is_solved_block(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ static char edge_string[12][7] = {
+ [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR",
+ [DF] = "DF", [DL] = "DL", [DB] = "DB", [DR] = "DR",
+ [FR] = "FR", [FL] = "FL", [BL] = "BL", [BR] = "BR"
+ };
+
+ static char corner_string[8][7] = {
+ [UFR] = "UFR", [UFL] = "UFL", [UBL] = "UBL", [UBR] = "UBR",
+ [DFR] = "DFR", [DFL] = "DFL", [DBL] = "DBL", [DBR] = "DBR"
+ };
+
+ static char center_string[6][7] = {
+ [U_center] = "U", [D_center] = "D",
+ [R_center] = "R", [L_center] = "L",
+ [F_center] = "F", [B_center] = "B"
+ };
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ static bool initialized = false;
+ static Center aux[FACTORIAL6][6];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++)
+ aux[ui][i] = arr->cpos[i];
+ free_cubearray(arr, pf_cpos);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ static bool initialized = false;
+ static Corner aux[FACTORIAL8][8];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++)
+ aux[ui][i] = arr->cp[i];
+ free_cubearray(arr, pf_cp);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ static bool initialized = false;
+ static int auxlast[POW3TO7];
+ static int auxarr[8];
+ static unsigned int ui;
+
+ if (!initialized) {
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ auxlast[ui] = auxarr[7];
+ }
+
+ initialized = true;
+ }
+
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return auxlast[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ static bool initialized = false;
+ static int auxlast[POW2TO11];
+ static int auxarr[12];
+ static unsigned int ui;
+
+ if (!initialized) {
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ auxlast[ui] = auxarr[11];
+ }
+
+ initialized = true;
+ }
+
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return auxlast[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ static bool initialized = false;
+ static Center aux[FACTORIAL6][6];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++)
+ aux[ui][arr->cpos[i]] = i;
+ free_cubearray(arr, pf_cpos);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ static bool initialized = false;
+ static Corner aux[FACTORIAL8][8];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++)
+ aux[ui][arr->cp[i]] = i;
+ free_cubearray(arr, pf_cp);
+ }
+
+ initialized = true;
+ }
+ return aux[cube.cp][c];
+}
+
+Edge
+where_is_edge(Cube cube, Edge e)
+{
+ /* TODO: when I wrote this code I forgot to add the final
+ part, and now I can't remember how it was supposed to
+ work (i.e. how to recover the location of the edge
+ from these tables. I think it is either very easy or
+ wrong, in any case it is not a priority now.
+ Future Seba can deal with it.
+
+ static bool initialized = false;
+ static Edge aux[3][FACTORIAL12/FACTORIAL8][12];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ initialized = true;
+ }
+ */
+
+ int i;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ for (i = 0; i < 12; i++)
+ if ((Edge)arr->ep[i] == e)
+ return i;
+
+ return -1;
+}
diff --git a/old/2021-11-10-beforeremovingchecker/cube.h b/old/2021-11-10-beforeremovingchecker/cube.h
@@ -0,0 +1,40 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <time.h>
+
+#include "pf.h"
+#include "utils.h"
+
+Cube admissible_ep(Cube cube, PieceFilter f); /* TODO: move? */
+Cube arrays_to_cube(CubeArray *arr, PieceFilter f); /* TODO: remove */
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+int edge_slice(Edge e); /* E=0, S=1, M=2 */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube);
+bool block_solved(Cube cube, Block); /*TODO: rename to is_solved_block()*/
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void epos_to_partial_ep(int epos, int *ep, int *ss);
+void free_cubearray(CubeArray *arr, PieceFilter f); /* TODO: remove */
+Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+CubeArray * new_cubearray(Cube cube, PieceFilter f); /* TODO: remove */
+void print_cube(Cube cube);
+Cube random_cube();
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+#endif
+
diff --git a/old/2021-11-10-beforeremovingchecker/cubetypes.h b/old/2021-11-10-beforeremovingchecker/cubetypes.h
@@ -0,0 +1,298 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define NMOVES 55 /* Actually 55, but one is NULLMOVE */
+#define NTRANS 48
+#define NROTATIONS 24
+
+
+/* Typedefs ******************************************************************/
+
+typedef enum center Center;
+typedef enum corner Corner;
+typedef enum edge Edge;
+typedef enum move Move;
+typedef enum trans Trans;
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct command Command;
+typedef struct commandargs CommandArgs;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef bool (*Validator) (Alg *);
+typedef void (*Exec) (CommandArgs *);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+typedef CommandArgs * (*ArgParser) (int, char **);
+typedef Trans (*TransDetector) (Cube);
+
+
+/* Enums *********************************************************************/
+
+enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+};
+
+enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+};
+
+enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+};
+
+enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+};
+
+enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+};
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+command
+{
+ /* TODO: more stuff to add? maybe complete help? */
+ /* Maybe add list of options */
+ char * name;
+ char * usage;
+ char * description;
+ ArgParser parse_args;
+ Exec exec;
+};
+
+struct
+commandargs
+{
+ bool success;
+ Alg * scramble;
+ SolveOptions * opts;
+ Step * step;
+ Command * command; /* For help */
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ Checker check;
+ uint64_t max;
+ int ntrans;
+ Trans * trans;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ /* TODO: add option to list *all* solutions satisfying other
+ constraints (min/max moves and optimality) */
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool feedback; /* TODO: rename with "verbose" */
+ bool all;
+ bool print_number;
+};
+
+struct
+step
+{
+ char * shortname;
+ char * name;
+ Estimator estimate;
+ Checker ready;
+ char * ready_msg;
+ Validator is_valid;
+ Moveset moveset;
+ Trans pre_trans;
+ TransDetector detect;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/env.c b/old/2021-11-10-beforeremovingchecker/env.c
@@ -0,0 +1,45 @@
+#include "env.h"
+
+bool initialized_env = false;
+char *tabledir;
+
+void
+init_env()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (initialized_env)
+ return;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+
+ initialized_env = true;
+}
diff --git a/old/2021-11-10-beforeremovingchecker/env.h b/old/2021-11-10-beforeremovingchecker/env.h
@@ -0,0 +1,15 @@
+#ifndef ENV_H
+#define ENV_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+extern char *tabledir;
+
+void init_env();
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/moves.c b/old/2021-11-10-beforeremovingchecker/moves.c
@@ -0,0 +1,474 @@
+#include "moves.h"
+
+/* Local functions ***********************************************************/
+
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static bool read_mtables_file();
+static bool write_mtables_file();
+
+/* Tables and other data *****************************************************/
+
+/* Every move is translated to a an <U, x, y> alg before filling the
+ transition tables, see init_moves() */
+
+static int edge_cycle[NMOVES][12] =
+{
+ [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR },
+ [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR },
+ [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL }
+};
+
+static int corner_cycle[NMOVES][8] =
+{
+ [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR },
+ [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR },
+ [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL }
+};
+
+static int center_cycle[NMOVES][6] =
+{
+ [x] = { F_center, B_center, R_center, L_center, D_center, U_center },
+ [y] = { U_center, D_center, B_center, F_center, R_center, L_center }
+};
+
+static int eofb_flipped[NMOVES][12] = {
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
+
+static int eorl_flipped[NMOVES][12] = {
+ [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
+
+static int eoud_flipped[NMOVES][12] = {
+ [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+};
+
+static int coud_flipped[NMOVES][8] = {
+ [x] = {
+ [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2,
+ [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2
+ }
+};
+
+static int corl_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ }
+};
+
+static int cofb_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ },
+ [y] = {
+ [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1,
+ [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2
+ }
+};
+
+static char equiv_alg_string[100][NMOVES] = {
+ [NULLMOVE] = "",
+
+ [U] = " U ",
+ [U2] = " UU ",
+ [U3] = " UUU ",
+ [D] = " xx U xx ",
+ [D2] = " xx UU xx ",
+ [D3] = " xx UUU xx ",
+ [R] = " yx U xxxyyy ",
+ [R2] = " yx UU xxxyyy ",
+ [R3] = " yx UUU xxxyyy ",
+ [L] = " yyyx U xxxy ",
+ [L2] = " yyyx UU xxxy ",
+ [L3] = " yyyx UUU xxxy ",
+ [F] = " x U xxx ",
+ [F2] = " x UU xxx ",
+ [F3] = " x UUU xxx ",
+ [B] = " xxx U x ",
+ [B2] = " xxx UU x ",
+ [B3] = " xxx UUU x ",
+
+ [Uw] = " xx U xx y ",
+ [Uw2] = " xx UU xx yy ",
+ [Uw3] = " xx UUU xx yyy ",
+ [Dw] = " U yyy ",
+ [Dw2] = " UU yy ",
+ [Dw3] = " UUU y ",
+ [Rw] = " yyyx U xxxy x ",
+ [Rw2] = " yyyx UU xxxy xx ",
+ [Rw3] = " yyyx UUU xxxy xxx ",
+ [Lw] = " yx U xxxyyy xxx ",
+ [Lw2] = " yx UU xxxyyy xx ",
+ [Lw3] = " yx UUU xxxyyy x ",
+ [Fw] = " xxx U x yxxxyyy ",
+ [Fw2] = " xxx UU x yxxyyy ",
+ [Fw3] = " xxx UUU x yxyyy ",
+ [Bw] = " x U xxx yxyyy ",
+ [Bw2] = " x UU xxx yxxyyy ",
+ [Bw3] = " x UUU xxx yxxxyyy ",
+
+ [M] = " yx U xx UUU yxyyy ",
+ [M2] = " yx UU xx UU xxxy ",
+ [M3] = " yx UUU xx U yxxxy ",
+ [S] = " x UUU xx U yyyx ",
+ [S2] = " x UU xx UU yyx ",
+ [S3] = " x U xx UUU yx ",
+ [E] = " U xx UUU xxyyy ",
+ [E2] = " UU xx UU xxyy ",
+ [E3] = " UUU xx U xxy ",
+
+ [x] = " x ",
+ [x2] = " xx ",
+ [x3] = " xxx ",
+ [y] = " y ",
+ [y2] = " yy ",
+ [y3] = " yyy ",
+ [z] = " yyy x y ",
+ [z2] = " yy xx ",
+ [z3] = " y x yyy "
+};
+
+/* Transition tables, to be loaded up at the beginning */
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ /*init_moves();*/
+
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+/* Public functions **********************************************************/
+
+Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ /*init_moves();*/
+
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+void
+init_moves() {
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+ Alg *equiv_alg[NMOVES];
+
+ for (i = 0; i < NMOVES; i++)
+ equiv_alg[i] = new_alg(equiv_alg_string[i]);
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+
+ for (i = 0; i < NMOVES; i++)
+ free_alg(equiv_alg[i]);
+}
+
+static bool
+read_mtables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = POW2TO11,
+ [5] = POW2TO11,
+ [6] = FACTORIAL8,
+ [7] = POW3TO7,
+ [8] = POW3TO7,
+ [9] = POW3TO7,
+ [10] = FACTORIAL6
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_mtables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = POW2TO11,
+ [5] = POW2TO11,
+ [6] = FACTORIAL8,
+ [7] = POW3TO7,
+ [8] = POW3TO7,
+ [9] = POW3TO7,
+ [10] = FACTORIAL6
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+bool
+commute(Move m1, Move m2)
+{
+ static bool initialized = false;
+ static bool commute_aux[NMOVES][NMOVES];
+
+ if (!initialized) {
+ Cube c1, c2;
+ int i, j;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute_aux[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ initialized = true;
+ }
+
+ return commute_aux[m1][m2];
+}
+
+bool
+possible_next(Move m1, Move m2, Move m3)
+{
+ static bool initialized = false;
+ static bool paux[NMOVES][NMOVES][NMOVES];
+
+ if (!initialized) {
+ int i, j, k;
+ bool p, q, c;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p = j && base_move(j) == base_move(k);
+ q = i && base_move(i) == base_move(k);
+ c = commute(i, j);
+ paux[i][j][k] = !(p || (c && q));
+ }
+ }
+ }
+
+ initialized = true;
+ }
+
+ return paux[m1][m2][m3];
+}
diff --git a/old/2021-11-10-beforeremovingchecker/moves.h b/old/2021-11-10-beforeremovingchecker/moves.h
@@ -0,0 +1,16 @@
+#ifndef MOVES_H
+#define MOVES_H
+
+#include "alg.h"
+#include "cube.h"
+#include "env.h"
+
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+Cube apply_move(Move m, Cube cube);
+bool commute(Move m1, Move m2);
+bool possible_next(Move m1, Move m2, Move m3);
+
+void init_moves();
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/pf.c b/old/2021-11-10-beforeremovingchecker/pf.c
@@ -0,0 +1,80 @@
+#include "pf.h"
+
+PieceFilter
+pf_all = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .eofb = true,
+ .eorl = true,
+ .eoud = true,
+ .cp = true,
+ .cofb = true,
+ .corl = true,
+ .coud = true,
+ .cpos = true
+};
+
+PieceFilter
+pf_4val = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .eofb = true,
+ .coud = true,
+ .cp = true
+};
+
+PieceFilter
+pf_epcp = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .cp = true
+};
+
+PieceFilter
+pf_cpos = {
+ .cpos = true
+};
+
+PieceFilter
+pf_cp = {
+ .cp = true
+};
+
+PieceFilter
+pf_ep = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true
+};
+
+PieceFilter
+pf_e = {
+ .epose = true
+};
+
+PieceFilter
+pf_s = {
+ .eposs = true
+};
+
+PieceFilter
+pf_m = {
+ .eposm = true
+};
+
+PieceFilter
+pf_eo = {
+ .eofb = true,
+ .eorl = true,
+ .eoud = true
+};
+
+PieceFilter
+pf_co = {
+ .cofb = true,
+ .corl = true,
+ .coud = true
+};
diff --git a/old/2021-11-10-beforeremovingchecker/pf.h b/old/2021-11-10-beforeremovingchecker/pf.h
@@ -0,0 +1,18 @@
+#ifndef PF_H
+#define PF_H
+
+#include "cubetypes.h"
+
+extern PieceFilter pf_all;
+extern PieceFilter pf_4val;
+extern PieceFilter pf_epcp;
+extern PieceFilter pf_cpos;
+extern PieceFilter pf_cp;
+extern PieceFilter pf_ep;
+extern PieceFilter pf_e;
+extern PieceFilter pf_s;
+extern PieceFilter pf_m;
+extern PieceFilter pf_eo;
+extern PieceFilter pf_co;
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/pruning.c b/old/2021-11-10-beforeremovingchecker/pruning.c
@@ -0,0 +1,272 @@
+#include "pruning.h"
+
+static void genptable_bfs(PruneData *pd, int d, Move *ms);
+static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static void ptable_update_index(PruneData *pd, uint64_t ind, int m);
+static int ptableval_index(PruneData *pd, uint64_t ind);
+static bool read_ptable_file(PruneData *pd);
+static bool write_ptable_file(PruneData *pd);
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "pt_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "pt_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "pt_cornershtr_withcosets_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "pt_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "pt_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_drud_eofb = {
+ .filename = "pt_drud_eofb",
+ .coord = &coord_drud_eofb,
+ .moveset = moveset_eofb,
+};
+
+PruneData
+pd_drudfin_noE_sym16_drud = {
+ .filename = "pt_drudfin_noE_sym16_drud",
+ .coord = &coord_drudfin_noE_sym16,
+ .moveset = moveset_drud,
+};
+
+PruneData
+pd_htr_drud = {
+ .filename = "pt_htr_drud",
+ .coord = &coord_htr_drud,
+ .moveset = moveset_drud,
+};
+
+PruneData
+pd_htrfin_htr = {
+ .filename = "pt_htrfin_htr",
+ .coord = &coord_htrfin,
+ .moveset = moveset_htr,
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "pt_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM,
+};
+
+void
+genptable(PruneData *pd)
+{
+ Move ms[NMOVES];
+ int d;
+ uint64_t j, oldn;
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe exit gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+ pd->generated = true;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ moveset_to_list(pd->moveset, ms);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update_index(pd, j, 15);
+
+ for (j = 0; j < pd->coord->max; j++)
+ if (ptableval_index(pd, j) != 15) {
+ printf("Error, non-max value at index %lu!\n", j);
+ break;
+ }
+ printf("Table set, ready to start\n");
+
+ /*TODO: change, set to 0 for every solved state (might be more than 1)*/
+ ptable_update(pd, (Cube){0}, 0);
+ pd->n = 1;
+ oldn = 0;
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ 0, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = 1;
+ for (d = 0; d < 15 && pd->n < pd->coord->max; d++) {
+ genptable_bfs(pd, d, ms);
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ d+1, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+}
+
+static void
+genptable_bfs(PruneData *pd, int d, Move *ms)
+{
+ uint64_t i;
+
+ for (i = 0; i < pd->coord->max; i++)
+ if (ptableval_index(pd, i) == d)
+ genptable_branch(pd, i, d, ms);
+}
+
+static void
+genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms)
+{
+ int i, j;
+ Cube ci, cc, c;
+
+ /*
+ * This is the only line of the whole program where we REALLY need an
+ * anti-indexer function. We could get rid of it if only we could save
+ * a cube object for each index value as we go, but then we would need
+ * an incredible amount of memory to generate each ptable: assuming
+ * fields in struct cube are 32 bit ints that would take 88 times the
+ * memory of the table to be generated, more than 120Gb for
+ * ptable_khuge for example!
+ */
+ ci = pd->coord->cube(ind);
+
+ for (i = 0; i < pd->coord->ntrans; i++) {
+ /* For simplicity trans[] is NULL when ntrans = 1 */
+ c = i == 0 ? ci :
+ apply_trans(pd->coord->trans[i], ci);
+ for (j = 0; ms[j] != NULLMOVE; j++) {
+ cc = apply_move(ms[j], c);
+ if (ptableval(pd, cc) > d+1)
+ ptable_update(pd, cc, d+1);
+ }
+ }
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval_index(pd, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ ptable_update_index(pd, ind, n);
+}
+
+static void
+ptable_update_index(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ return ptableval_index(pd, pd->coord->index(cube));
+}
+
+static int
+ptableval_index(PruneData *pd, uint64_t ind)
+{
+ if (!pd->generated)
+ genptable(pd);
+
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
diff --git a/old/2021-11-10-beforeremovingchecker/pruning.h b/old/2021-11-10-beforeremovingchecker/pruning.h
@@ -0,0 +1,23 @@
+#ifndef PRUNING_H
+#define PRUNING_H
+
+#include "symcoord.h"
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_drud_sym16_HTM;
+extern PruneData pd_drud_eofb;
+extern PruneData pd_drudfin_noE_sym16_drud;
+extern PruneData pd_htr_drud;
+extern PruneData pd_htrfin_htr;
+extern PruneData pd_khuge_HTM;
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+#endif
+
diff --git a/old/2021-11-10-beforeremovingchecker/shell.c b/old/2021-11-10-beforeremovingchecker/shell.c
@@ -0,0 +1,99 @@
+#include "shell.h"
+
+static void cleanwhitespaces(char *line);
+static int parseline(char *line, char **v);
+
+static void
+cleanwhitespaces(char *line)
+{
+ char *i;
+
+ for (i = line; *i != 0; i++)
+ if (*i == '\t' || *i == '\n')
+ *i = ' ';
+}
+
+/* This function assumes that **v is large enough */
+static int
+parseline(char *line, char **v)
+{
+ char *t;
+ int n = 0;
+
+ cleanwhitespaces(line);
+
+ for (t = strtok(line, " "); t != NULL; t = strtok(NULL, " "))
+ strcpy(v[n++], t);
+
+ return n;
+}
+
+void
+exec_args(int c, char **v)
+{
+ int i;
+ Command *cmd = NULL;
+ CommandArgs *args;
+
+ for (i = 0; i < NCOMMANDS; i++)
+ if (commands[i] != NULL && !strcmp(v[0], commands[i]->name))
+ cmd = commands[i];
+
+ if (cmd == NULL) {
+ fprintf(stderr, "%s: command not found\n", v[0]);
+ return;
+ }
+
+ args = cmd->parse_args(c-1, &v[1]);
+ if (!args->success) {
+ fprintf(stderr, "usage: %s\n", cmd->usage);
+ return;
+ }
+
+ cmd->exec(args);
+ free_args(args);
+}
+
+void
+launch()
+{
+ int i, shell_argc;
+ char line[MAXLINELEN], **shell_argv;
+
+ shell_argv = malloc(MAXNTOKENS * sizeof(char *));
+ for (i = 0; i < MAXNTOKENS; i++)
+ shell_argv[i] = malloc((MAXTOKENLEN+1) * sizeof(char));
+
+ fprintf(stderr, "Welcome to Nissy 2.0 (demo version).\n");
+ fprintf(stderr, "Limited commands available. ");
+ fprintf(stderr, "Type 'help' for a list.\n");
+
+ while (true) {
+ fprintf(stderr, "nissy-# ");
+ if (fgets(line, MAXLINELEN, stdin) == NULL)
+ break;
+ shell_argc = parseline(line, shell_argv);
+ exec_args(shell_argc, shell_argv);
+ }
+
+ for (i = 0; i < MAXNTOKENS; i++)
+ free(shell_argv[i]);
+ free(shell_argv);
+}
+
+/* We will have our main() here, for now */
+int
+main(int argc, char *argv[])
+{
+ init_moves();
+ init_trans();
+ init_coord();
+ init_symcoord();
+
+ if (argc > 1)
+ exec_args(argc-1, &argv[1]);
+ else
+ launch();
+
+ return 0;
+}
diff --git a/old/2021-11-10-beforeremovingchecker/shell.h b/old/2021-11-10-beforeremovingchecker/shell.h
@@ -0,0 +1,13 @@
+#ifndef SHELL_H
+#define SHELL_H
+
+#include "commands.h"
+
+#define MAXLINELEN 10000
+#define MAXTOKENLEN 255
+#define MAXNTOKENS 255
+
+void exec_args(int c, char **v);
+void launch();
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/solve.c b/old/2021-11-10-beforeremovingchecker/solve.c
@@ -0,0 +1,209 @@
+#include "solve.h"
+
+/* Local functions ***********************************************************/
+
+static bool allowed_next(Move move, DfsData *dd);
+static void dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step *s, SolveOptions *os, DfsData *dd);
+static bool dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+
+/* Local functions ***********************************************************/
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+
+/* TODO: remove the commented part, was added to moves.c
+ static bool initialized = false;
+ static bool commute[NMOVES][NMOVES], pnext[NMOVES][NMOVES][NMOVES];
+
+ if (!initialized) {
+ Cube c1, c2;
+ int i, j, k;
+ bool p1, p2, cij;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute[i][j] = equal(c1, c2) && i && j;
+ for (k = 0; k < NMOVES; k++) {
+ p1 = j && base_move(j) == base_move(k);
+ p2 = i && base_move(i) == base_move(k);
+ cij = commute[i][j];
+ pnext[i][j][k] = !(p1 || (cij && p2));
+ }
+ }
+ }
+
+ initialized = true;
+ }
+
+ if (!pnext[dd->last2][dd->last1][move])
+ return false;
+
+ if (commute[dd->last1][move])
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+*/
+
+ if (!possible_next(dd->last2, dd->last1, move))
+ return false;
+
+ if (commute(dd->last1, move))
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(s, opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ if (s->is_valid(dd->current_alg) || opts->all)
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->feedback)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s->estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s->estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+/* Public functions **********************************************************/
+
+AlgList *
+solve(Cube cube, Step *step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c;
+
+ if (step->detect != NULL)
+ step->pre_trans = step->detect(cube);
+ c = apply_trans(step->pre_trans, cube);
+
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step->ready != NULL && !step->ready(c)) {
+ fprintf(stderr, "Cube not ready for solving step: ");
+ fprintf(stderr, "%s\n", step->ready_msg);
+ return sols;
+ }
+
+ moveset_to_list(step->moveset, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves &&
+ !(sols->len && opts->optimal_only) &&
+ sols->len < opts->max_solutions;
+ dd.d++) {
+ if (opts->feedback)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(c, step, opts, &dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(step->pre_trans), node->alg);
+
+ free_alg(dd.current_alg);
+ return sols;
+}
diff --git a/old/2021-11-10-beforeremovingchecker/solve.h b/old/2021-11-10-beforeremovingchecker/solve.h
@@ -0,0 +1,9 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include "moves.h"
+#include "trans.h"
+
+AlgList * solve(Cube cube, Step *step, SolveOptions *opts);
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/steps.c b/old/2021-11-10-beforeremovingchecker/steps.c
@@ -0,0 +1,916 @@
+#include "steps.h"
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int estimate_eoany_HTM(CubeTarget ct);
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coany_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coany_URF(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drany_HTM(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_drud_eofb(CubeTarget ct);
+static int estimate_dr_eofb(CubeTarget ct);
+static int estimate_drudfin_drud(CubeTarget ct);
+static int estimate_htr_drud(CubeTarget ct);
+static int estimate_htrfin_htr(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+/* Validators ****************************************************************/
+
+static bool always_valid(Alg *alg);
+static bool validate_singlecw_ending(Alg *alg);
+
+/* Pre-transformation detectors **********************************************/
+
+static Trans detect_pretrans_eofb(Cube cube);
+static Trans detect_pretrans_drud(Cube cube);
+
+/* Messages for when cube is not ready ***************************************/
+
+static char check_centers_msg[100] = "cube must be oriented (centers solved)";
+static char check_eo_msg[100] = "EO must be solved on given axis";
+static char check_dr_msg[100] = "DR must be solved on given axis";
+static char check_htr_msg[100] = "HTR must be solved";
+static char check_drany_msg[100] = "DR must be solved on at least one axis";
+
+/* Steps *********************************************************************/
+
+Step
+optimal_HTM = {
+ .shortname = "optimal",
+ .name = "Optimal solve (in HTM)",
+
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+/* EO steps **************************/
+Step
+eoany_HTM = {
+ .shortname = "eo",
+ .name = "EO on any axis",
+
+ .estimate = estimate_eoany_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+eofb_HTM = {
+ .shortname = "eofb",
+ .name = "EO on F/B",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+eorl_HTM = {
+ .shortname = "eorl",
+ .name = "EO on R/L",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = ur,
+};
+
+Step
+eoud_HTM = {
+ .shortname = "eoud",
+ .name = "EO on U/D",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+/* CO steps **************************/
+Step
+coany_HTM = {
+ .shortname = "co",
+ .name = "CO on any axis",
+
+ .estimate = estimate_coany_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+coud_HTM = {
+ .shortname = "coud",
+ .name = "CO on U/D",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+corl_HTM = {
+ .shortname = "corl",
+ .name = "CO on R/L",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = rf,
+};
+
+Step
+cofb_HTM = {
+ .shortname = "cofb",
+ .name = "CO on F/B",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+Step
+coany_URF = {
+ .shortname = "co-URF",
+ .name = "CO any axis (URF moveset)",
+
+ .estimate = estimate_coany_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+coud_URF = {
+ .shortname = "coud-URF",
+ .name = "CO on U/D (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+corl_URF = {
+ .shortname = "corl-URF",
+ .name = "CO on R/L (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = rf,
+};
+
+Step
+cofb_URF = {
+ .shortname = "cofb-URF",
+ .name = "CO on F/B (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = fd,
+};
+
+/* Misc corner steps *****************/
+Step
+cornershtr_HTM = {
+ .shortname = "chtr",
+ .name = "Solve corners to HTR state",
+
+ .estimate = estimate_cornershtr_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+cornershtr_URF = {
+ .shortname = "chtr-URF",
+ .name = "Solve corners to HTR state (URF moveset)",
+
+ .estimate = estimate_cornershtr_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+corners_HTM = {
+ .shortname = "corners",
+ .name = "Solve corners",
+
+ .estimate = estimate_corners_HTM,
+ .ready = NULL,
+ .is_valid = always_valid,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+corners_URF = {
+ .shortname = "corners-URF",
+ .name = "Solve corners (URF moveset)",
+
+ .estimate = estimate_corners_URF,
+ .ready = NULL,
+ .is_valid = always_valid,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+/* DR steps **************************/
+Step
+drany_HTM = {
+ .shortname = "dr",
+ .name = "DR on any axis",
+
+ .estimate = estimate_drany_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+drud_HTM = {
+ .shortname = "drud",
+ .name = "DR on U/D",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+drrl_HTM = {
+ .shortname = "drrl",
+ .name = "DR on R/L",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = rf,
+};
+
+Step
+drfb_HTM = {
+ .shortname = "drfb",
+ .name = "DR on F/B",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+/* DR from EO */
+Step
+dr_eo = {
+ .shortname = "dr-eo",
+ .name = "DR without breaking EO (automatically detected)",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .detect = detect_pretrans_eofb,
+};
+
+Step
+dr_eofb = {
+ .shortname = "dr-eofb",
+ .name = "DR on U/D or R/L without breaking EO on F/B",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = uf,
+};
+
+Step
+dr_eorl = {
+ .shortname = "dr-eorl",
+ .name = "DR on U/D or F/B without breaking EO on R/L",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = ur,
+};
+
+Step
+dr_eoud = {
+ .shortname = "dr-eoud",
+ .name = "DR on R/L or F/B without breaking EO on U/R",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fd,
+};
+
+Step
+drud_eofb = {
+ .shortname = "drud-eofb",
+ .name = "DR on U/D without breaking EO on F/B",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = uf,
+};
+
+Step
+drrl_eofb = {
+ .shortname = "drrl-eofb",
+ .name = "DR on R/L without breaking EO on F/B",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = rf,
+};
+
+Step
+drud_eorl = {
+ .shortname = "drud-eorl",
+ .name = "DR on U/D without breaking EO on R/L",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = ur,
+};
+
+Step
+drfb_eorl = {
+ .shortname = "drfb-eorl",
+ .name = "DR on F/B without breaking EO on R/L",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fr,
+};
+
+Step
+drfb_eoud = {
+ .shortname = "drfb-eoud",
+ .name = "DR on F/B without breaking EO on U/D",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fd,
+};
+
+Step
+drrl_eoud = {
+ .shortname = "drrl-eoud",
+ .name = "DR on R/L without breaking EO on U/D",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = rd,
+};
+
+/* DR finish steps */
+Step
+dranyfin_DR = {
+ .shortname = "drfin",
+ .name = "DR finish on any axis without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_drany_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .detect = detect_pretrans_drud,
+};
+
+Step
+drudfin_drud = {
+ .shortname = "drudfin",
+ .name = "DR finish on U/D without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = uf,
+};
+
+Step
+drrlfin_drrl = {
+ .shortname = "drrlfin",
+ .name = "DR finish on R/L without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = rf,
+};
+
+Step
+drfbfin_drfb = {
+ .shortname = "drfbfin",
+ .name = "DR finish on F/B without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = fd,
+};
+
+/* HTR from DR */
+Step
+htr_any = {
+ .shortname = "htr",
+ .name = "HTR from DR",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_drany_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .detect = detect_pretrans_drud,
+};
+
+Step
+htr_drud = {
+ .shortname = "htr-drud",
+ .name = "HTR from DR on U/D",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = uf,
+};
+
+Step
+htr_drrl = {
+ .shortname = "htr-drrl",
+ .name = "HTR from DR on R/L",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = rf,
+};
+
+Step
+htr_drfb = {
+ .shortname = "htr-drfb",
+ .name = "HTR from DR on F/B",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = fd,
+};
+
+/* HTR finish */
+Step
+htrfin_htr = {
+ .shortname = "htrfin",
+ .name = "HTR finish without breaking HTR",
+
+ .estimate = estimate_htrfin_htr,
+ .ready = check_htr,
+ .ready_msg = check_htr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_htr,
+
+ .pre_trans = uf,
+};
+
+Step *steps[NSTEPS] = {
+ &optimal_HTM, /* first is default */
+
+ &eoany_HTM,
+ &eofb_HTM,
+ &eorl_HTM,
+ &eoud_HTM,
+
+ &coany_HTM,
+ &coud_HTM,
+ &corl_HTM,
+ &cofb_HTM,
+
+ &coany_URF,
+ &coud_URF,
+ &corl_URF,
+ &cofb_URF,
+
+ &drany_HTM,
+ &drud_HTM,
+ &drrl_HTM,
+ &drfb_HTM,
+
+ &dr_eo,
+ &dr_eofb,
+ &dr_eorl,
+ &dr_eoud,
+ &drud_eofb,
+ &drrl_eofb,
+ &drud_eorl,
+ &drfb_eorl,
+ &drfb_eoud,
+ &drrl_eoud,
+
+ &dranyfin_DR,
+ &drudfin_drud,
+ &drrlfin_drrl,
+ &drfbfin_drfb,
+
+ &htr_any,
+ &htr_drud,
+ &htr_drrl,
+ &htr_drfb,
+
+ &htrfin_htr,
+
+ &cornershtr_HTM,
+ &cornershtr_URF,
+ &corners_HTM,
+ &corners_URF,
+};
+
+/* Standard checkers (return lower bound) ************************************/
+
+static int
+estimate_eoany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_eofb_HTM, ct.cube);
+ r2 = ptableval(&pd_eofb_HTM, apply_trans(ur, ct.cube));
+ r3 = ptableval(&pd_eofb_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_coud_HTM, ct.cube);
+ r2 = ptableval(&pd_coud_HTM, apply_trans(rf, ct.cube));
+ r3 = ptableval(&pd_coud_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coany_URF(CubeTarget ct)
+{
+ int r1, r2, r3;
+ CubeTarget ct2, ct3;
+
+ ct2.cube = apply_trans(rf, ct.cube);
+ ct2.target = ct.target;
+
+ ct3.cube = apply_trans(fd, ct.cube);
+ ct3.target = ct.target;
+
+ r1 = estimate_coud_URF(ct);
+ r2 = estimate_coud_URF(ct2);
+ r3 = estimate_coud_URF(ct3);
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_drud_sym16_HTM, ct.cube);
+ r2 = ptableval(&pd_drud_sym16_HTM, apply_trans(rf, ct.cube));
+ r3 = ptableval(&pd_drud_sym16_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+static int
+estimate_drud_eofb(CubeTarget ct)
+{
+ return ptableval(&pd_drud_eofb, ct.cube);
+}
+
+static int
+estimate_dr_eofb(CubeTarget ct)
+{
+ int r1, r2;
+
+ r1 = ptableval(&pd_drud_eofb, ct.cube);
+ r2 = ptableval(&pd_drud_eofb, apply_trans(rf, ct.cube));
+
+ return MIN(r1, r2);
+}
+
+static int
+estimate_drudfin_drud(CubeTarget ct)
+{
+ int val = ptableval(&pd_drudfin_noE_sym16_drud, ct.cube);
+
+ if (val != 0)
+ return val;
+
+ return ct.cube.epose % 24 == 0 ? 0 : 1;
+}
+
+static int
+estimate_htr_drud(CubeTarget ct)
+{
+ return ptableval(&pd_htr_drud, ct.cube);
+}
+
+static int
+estimate_htrfin_htr(CubeTarget ct)
+{
+ return ptableval(&pd_htrfin_htr, ct.cube);
+}
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(rf, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(fd, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ /* Michiel de Bondt's trick */
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ return MAX(ret, dr3);
+}
+
+/* Validators ****************************************************************/
+
+static bool
+always_valid(Alg *alg)
+{
+ return true;
+}
+
+static bool
+validate_singlecw_ending(Alg *alg)
+{
+ int i;
+ bool nor, inv;
+ Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE;
+
+ for (i = 0; i < alg->len; i++) {
+ if (alg->inv[i]) {
+ l2i = l1i;
+ l1i = alg->move[i];
+ } else {
+ l2 = l1;
+ l1 = alg->move[i];
+ }
+ }
+
+ nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2));
+ inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i));
+
+ return nor && inv;
+}
+
+/* Pre-transformation detectors **********************************************/
+
+static Trans
+detect_pretrans_eofb(Cube cube)
+{
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ if (check_eofb(apply_trans(i, cube)))
+ return i;
+
+ return 0;
+}
+
+static Trans
+detect_pretrans_drud(Cube cube)
+{
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ if (check_drud(apply_trans(i, cube)))
+ return i;
+
+ return 0;
+}
diff --git a/old/2021-11-10-beforeremovingchecker/steps.h b/old/2021-11-10-beforeremovingchecker/steps.h
@@ -0,0 +1,10 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "pruning.h"
+
+#define NSTEPS 50
+
+extern Step * steps[NSTEPS];
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/symcoord.c b/old/2021-11-10-beforeremovingchecker/symcoord.c
@@ -0,0 +1,359 @@
+#include "symcoord.h"
+
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_cp_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_drudfin_noE_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_cp_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_drudfin_noE_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static void gensym(SymData *sd);
+static bool read_symdata_file(SymData *sd);
+static bool write_symdata_file(SymData *sd);
+
+/* Transformation groups and symmetry data ***********************************/
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+static SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static SymData
+sd_cp_16 = {
+ .filename = "sd_cp_16",
+ .coord = &coord_cp,
+ .sym_coord = &coord_cp_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int nsymdata = 3;
+static SymData * all_sd[] = {
+ &sd_coud_16,
+ &sd_cp_16,
+ &sd_eofbepos_16,
+};
+
+
+/* Coordinates and their implementation **************************************/
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_cp_sym16 = {
+ .index = index_cp_sym16,
+ .cube = antindex_cp_sym16,
+ .check = check_cp,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drudfin_noE_sym16 = {
+ .index = index_drudfin_noE_sym16,
+ .cube = antindex_drudfin_noE_sym16,
+ .check = check_drudfin_noE,
+ .max = FACTORIAL8 * 2768,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+/* Functions *****************************************************************/
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_cp_sym16(uint64_t ind)
+{
+ return sd_cp_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = antindex_eofbepos_sym16(ind/POW3TO7);
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_drudfin_noE_sym16(uint64_t ind)
+{
+ Cube c1, c2;
+
+ c1 = coord_epud.cube(ind % FACTORIAL8);
+ c2 = antindex_cp_sym16(ind/FACTORIAL8);
+ c1.cp = c2.cp;
+
+ return c1;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = antindex_eofbepos_sym16(ind/(FACTORIAL4*POW3TO7));
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[coord_coud.index(cube)];
+}
+
+static uint64_t
+index_cp_sym16(Cube cube)
+{
+ return sd_cp_16.class[coord_cp.index(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_drudfin_noE_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_cp_16.transtorep[coord_cp.index(cube)];
+ c = apply_trans(t, cube);
+
+ return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c);
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[coord_eofbepos.index(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+/* Other functions ***********************************************************/
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+void
+init_symcoord()
+{
+ int i;
+
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ for (i = 0; i < nsymdata; i++)
+ gensym(all_sd[i]);
+}
+
diff --git a/old/2021-11-10-beforeremovingchecker/symcoord.h b/old/2021-11-10-beforeremovingchecker/symcoord.h
@@ -0,0 +1,15 @@
+#ifndef SYMCOORD_H
+#define SYMCOORD_H
+
+#include "coord.h"
+
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_cp_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_drudfin_noE_sym16;
+extern Coordinate coord_khuge;
+
+void init_symcoord();
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/trans.c b/old/2021-11-10-beforeremovingchecker/trans.c
@@ -0,0 +1,372 @@
+#include "trans.h"
+
+/* Local functions ***********************************************************/
+
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static bool write_ttables_file();
+
+/* Tables and other data *****************************************************/
+
+static int ep_mirror[12] = {
+ [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL,
+ [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL,
+ [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL
+};
+
+static int cp_mirror[8] = {
+ [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL,
+ [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL
+};
+
+static int cpos_mirror[6] = {
+ [U_center] = U_center, [D_center] = D_center,
+ [R_center] = L_center, [L_center] = R_center,
+ [F_center] = F_center, [B_center] = B_center
+};
+
+/* TODO Is there a more elegant way? */
+static char rotation_alg_string[100][NROTATIONS] = {
+ [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3",
+ [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2",
+ [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3",
+ [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y",
+ [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3",
+ [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3",
+};
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+/* Local functions implementation ********************************************/
+
+void
+init_trans() {
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_alg(i), (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_alg(m), (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ /* Old version:
+ *
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move(move), aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube) || is_solved(mirr))
+ moves_ttable[m][mi] = move;
+ }
+ */
+
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move(move), aux);
+ if (is_solved(cube)) {
+ moves_ttable[m][mi] = move;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
+static bool
+read_ttables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = FACTORIAL8,
+ [5] = POW3TO7,
+ [6] = FACTORIAL6,
+ [7] = NMOVES
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[4], f) == me[4];
+ r = r && fread(co_ttable[m], b, me[5], f) == me[5];
+ r = r && fread(cpos_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(moves_ttable[m], b, me[7], f) == me[7];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_alg(r));
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_alg(r), ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static bool
+write_ttables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = FACTORIAL8,
+ [5] = POW3TO7,
+ [6] = FACTORIAL6,
+ [7] = NMOVES
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4];
+ r = r && fwrite(co_ttable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Public functions **********************************************************/
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ /*init_trans();*/
+
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ /* TODO is there a more elegant way? */
+ static Trans inverse_trans_aux[NTRANS] = {
+ [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub,
+ [df] = df, [dr] = dr, [dl] = dl, [db] = db,
+ [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr,
+ [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl,
+ [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu,
+ [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd,
+
+ [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror,
+ [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror,
+ [df_mirror] = df_mirror, [dr_mirror] = dl_mirror,
+ [dl_mirror] = dr_mirror, [db_mirror] = db_mirror,
+ [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror,
+ [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror,
+ [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror,
+ [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror,
+ [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror,
+ [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror,
+ [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror,
+ [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror
+ };
+
+ return inverse_trans_aux[t];
+}
+
+Alg *
+rotation_alg(Trans t)
+{
+ int i;
+
+ static Alg *rotation_alg_arr[NROTATIONS];
+ static bool initialized = false;
+
+ if (!initialized) {
+ for (i = 0; i < NROTATIONS; i++)
+ rotation_alg_arr[i] = new_alg(rotation_alg_string[i]);
+
+ initialized = true;
+ }
+
+ return rotation_alg_arr[t % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ /*init_trans();*/
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
diff --git a/old/2021-11-10-beforeremovingchecker/trans.h b/old/2021-11-10-beforeremovingchecker/trans.h
@@ -0,0 +1,13 @@
+#ifndef TRANS_H
+#define TRANS_H
+
+#include "moves.h"
+
+Cube apply_trans(Trans t, Cube cube);
+Trans inverse_trans(Trans t);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans i, Alg *alg);
+
+void init_trans();
+
+#endif
diff --git a/old/2021-11-10-beforeremovingchecker/utils.c b/old/2021-11-10-beforeremovingchecker/utils.c
@@ -0,0 +1,274 @@
+#include "utils.h"
+
+void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
diff --git a/old/2021-11-10-beforeremovingchecker/utils.h b/old/2021-11-10-beforeremovingchecker/utils.h
@@ -0,0 +1,41 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+void apply_permutation(int *perm, int *set, int n);
+int binomial(int n, int k);
+int digit_array_to_int(int *a, int n, int b);
+int factorial(int n);
+void index_to_perm(int p, int n, int *r);
+void index_to_subset(int s, int n, int k, int *r);
+void int_to_digit_array(int a, int b, int n, int *r);
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+int invert_digits(int a, int b, int n);
+bool is_perm(int *a, int n);
+bool is_subset(int *a, int n, int k);
+int perm_sign(int *a, int n);
+int perm_to_index(int *a, int n);
+int powint(int a, int b);
+int subset_to_index(int *a, int n, int k);
+void sum_arrays_mod(int *src, int *dst, int n, int m);
+void swap(int *a, int *b);
+
+#endif
diff --git a/old/backup-manysteps-pretrans/steps.c b/old/backup-manysteps-pretrans/steps.c
@@ -0,0 +1,369 @@
+#include "steps.h"
+
+/* Check functions ***********************************************************/
+
+static int check_nothing(Cube cube);
+static int check_eofb_HTM(Cube cube);
+static int check_coud_HTM(Cube cube);
+static int check_coud_URF(Cube cube);
+static int check_corners_HTM(Cube cube);
+static int check_corners_URF(Cube cube);
+static int check_edges_HTM(Cube cube);
+static int check_drud_HTM(Cube cube);
+static int check_optimal_HTM(Cube cube);
+
+
+/* Index functions ***********************************************************/
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_ep(Cube cube);
+static uint64_t index_drud(Cube cube);
+
+
+/* Steps *********************************************************************/
+
+Step
+eofb_HTM = {
+ .check = check_eofb_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+Step
+eorl_HTM = {
+ .check = check_eofb_HTM,
+ .ready = check_nothing,
+ .pre_trans = ur,
+ .moveset = moveset_HTM
+};
+
+Step
+eoud_HTM = {
+ .check = check_eofb_HTM,
+ .ready = check_nothing,
+ .pre_trans = bu,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_HTM = {
+ .check = check_coud_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+Step
+corl_HTM = {
+ .check = check_coud_HTM,
+ .ready = check_nothing,
+ .pre_trans = rf,
+ .moveset = moveset_HTM
+};
+
+Step
+cofb_HTM = {
+ .check = check_coud_HTM,
+ .ready = check_nothing,
+ .pre_trans = fd,
+ .moveset = moveset_HTM
+};
+
+Step
+coud_URF = {
+ .check = check_coud_URF,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_URF
+};
+
+Step
+corl_URF = {
+ .check = check_coud_URF,
+ .ready = check_nothing,
+ .pre_trans = rf,
+ .moveset = moveset_URF
+};
+
+Step
+cofb_URF = {
+ .check = check_coud_URF,
+ .ready = check_nothing,
+ .pre_trans = fd,
+ .moveset = moveset_URF
+};
+
+Step
+corners_HTM = {
+ .check = check_corners_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+Step
+corners_URF = {
+ .check = check_corners_URF,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_URF
+};
+
+Step
+edges_HTM = {
+ .check = check_edges_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+Step
+drud_HTM = {
+ .check = check_drud_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+Step
+optimal_HTM = {
+ .check = check_optimal_HTM,
+ .ready = check_nothing,
+ .pre_trans = uf,
+ .moveset = moveset_HTM
+};
+
+
+/* Pruning tables ************************************************************/
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "ptable_eofb_HTM",
+ .size = POW2TO11,
+ .index = index_eofb,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "ptable_coud_HTM",
+ .size = POW3TO7,
+ .index = index_coud,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "ptable_corners_HTM",
+ .size = POW3TO7 * FACTORIAL8,
+ .index = index_corners,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_ep_HTM = {
+ .filename = "ptable_ep_HTM",
+ .size = FACTORIAL12,
+ .index = index_ep,
+ .moveset = moveset_HTM
+};
+
+PruneData
+pd_drud_HTM = {
+ .filename = "ptable_drud_HTM",
+ .size = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .index = index_drud,
+ .moveset = moveset_HTM
+};
+
+
+/* Check functions implementation ********************************************/
+
+static int
+check_nothing(Cube cube)
+{
+ /* At least we check that it is admissible */
+ return is_admissible(cube);
+}
+
+static int
+check_eofb_HTM(Cube cube)
+{
+ if (!pd_eofb_HTM.generated)
+ generate_ptable(&pd_eofb_HTM);
+
+ return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb);
+}
+
+static int
+check_coud_HTM(Cube cube)
+{
+ if (!pd_coud_HTM.generated)
+ generate_ptable(&pd_coud_HTM);
+
+ return PTABLEVAL(pd_coud_HTM.ptable, cube.coud);
+}
+
+static int
+check_coud_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ int ud = check_coud_HTM(cube);
+ int rl = check_coud_HTM(apply_move(z, cube));
+ int fb = check_coud_HTM(apply_move(x, cube));
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+check_corners_HTM(Cube cube)
+{
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+
+ return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+}
+
+static int
+check_corners_URF(Cube cube)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ ret = MIN(ret,check_corners_HTM(apply_alg(trans_alg(i),cube)));
+
+ return ret;
+}
+
+static int
+check_edges_HTM(Cube cube)
+{
+ int ret = 0;
+
+ if (!pd_ep_HTM.generated)
+ generate_ptable(&pd_ep_HTM);
+
+ ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)));
+ ret = MAX(ret, check_eofb_HTM(cube));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube)));
+ ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube)));
+
+ return ret;
+}
+
+static int
+check_drud_HTM(Cube cube)
+{
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+
+ return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+}
+
+static int
+check_optimal_HTM(Cube cube)
+{
+ int dr1, dr2, dr3, drmax, cor; /*ep;*/
+
+ if (!pd_drud_HTM.generated)
+ generate_ptable(&pd_drud_HTM);
+ if (!pd_corners_HTM.generated)
+ generate_ptable(&pd_corners_HTM);
+ /*
+ *if (!pd_ep_HTM.generated)
+ * generate_ptable(&pd_ep_HTM);
+ */
+
+ dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube));
+ dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube)));
+ dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube)));
+
+ drmax = MAX(dr1, MAX(dr2, dr3));
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ drmax++;
+
+ cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube));
+ /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */
+
+ /*return MAX(drmax, MAX(ep, cor));*/
+ if (drmax == 0 && cor == 0)
+ return is_solved(cube, false) ? 0 : 1;
+ return MAX(drmax, cor);
+}
+
+
+/* Index functions implementation ********************************************/
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_ep(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eposs;
+ b = (cube.epose % FACTORIAL4) + epos_dependent_cube(cube)*FACTORIAL4;
+ c = cube.eposm % FACTORIAL4;
+
+ b *= FACTORIAL4 * BINOM12ON4;
+ c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
diff --git a/old/backup-manysteps-pretrans/steps.h b/old/backup-manysteps-pretrans/steps.h
@@ -0,0 +1,36 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "cube.h"
+
+/* Steps *********************************************************************/
+
+extern Step eofb_HTM;
+extern Step eorl_HTM;
+extern Step eoud_HTM;
+extern Step coud_HTM;
+extern Step corl_HTM;
+extern Step cofb_HTM;
+extern Step coud_URF;
+extern Step corl_URF;
+extern Step cofb_URF;
+extern Step corners_HTM;
+extern Step corners_URF;
+extern Step edges_HTM;
+extern Step drud_HTM;
+extern Step optimal_HTM;
+
+/* Pruning tables ************************************************************/
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_ep_HTM;
+extern PruneData pd_drud_HTM;
+
+/* Movesets ******************************************************************/
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+
+#endif
diff --git a/old/coord.c b/old/coord.c
@@ -0,0 +1,1014 @@
+#include "coord.h"
+
+static Cube admissible_eos_from_eofbepos(Cube cube);
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_epud(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cp(uint64_t ind);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_cp_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_drud_eofb(uint64_t ind);
+static Cube antindex_drudfin_noE_sym16(uint64_t ind);
+static Cube antindex_htrfin(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+static int epos_dependent_pos(int pos1, int pos2);
+static void gensym(SymData *sd);
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_epud(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cp(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_cp_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_drud_eofb(Cube cube);
+static uint64_t index_drudfin_noE_sym16(Cube cube);
+static uint64_t index_htrfin(Cube cube);
+static uint64_t index_khuge(Cube cube);
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_symdata();
+static bool read_symdata_file(SymData *sd);
+static bool write_symdata_file(SymData *sd);
+
+/* All sorts of useful costants and tables **********************************/
+
+/* TODO: Can I move inside functions that use them?
+ Maybe I need to pass them as argument to some
+ secondary function */
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+
+
+/* Symmetry data for some coordinates ****************************************/
+
+static Trans
+trans_group_trivial[1] = { uf };
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_cp_16 = {
+ .filename = "sd_cp_16",
+ .coord = &coord_cp,
+ .sym_coord = &coord_cp_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int n_all_symdata = 3;
+static SymData * all_sd[3] = {
+ &sd_coud_16,
+ &sd_cp_16,
+ &sd_eofbepos_16,
+};
+
+/* Coordinates and their implementation **************************************/
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .check = check_eofb,
+ .max = POW2TO11,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .check = check_eofbepos,
+ .max = POW2TO11 * BINOM12ON4,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .check = check_coud,
+ .max = POW3TO7,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .check = check_corners,
+ .max = POW3TO7 * FACTORIAL8,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_cp = {
+ .index = index_cp,
+ .cube = antindex_cp,
+ .check = check_cp,
+ .max = FACTORIAL8,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .check = check_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .check = check_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_htrfin = {
+ .index = index_htrfin,
+ .cube = antindex_htrfin,
+ .check = check_htrfin,
+ .max = 24 * 24 * 24 *24 * 24,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_drud_eofb = {
+ .index = index_drud_eofb,
+ .cube = antindex_drud_eofb,
+ .check = check_drud,
+ .max = POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+ .trans = trans_group_trivial,
+};
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .check = check_eofbepos,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .check = check_coud,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_cp_sym16 = {
+ .index = index_cp_sym16,
+ .cube = antindex_cp_sym16,
+ .check = check_cp,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .check = check_drud,
+ .max = POW3TO7 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drudfin_noE_sym16 = {
+ .index = index_drudfin_noE_sym16,
+ .cube = antindex_drudfin_noE_sym16,
+ .check = check_drudfin_noE,
+ .max = FACTORIAL8 * 2768,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .check = check_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+/* Functions *****************************************************************/
+
+static Cube
+admissible_eos_from_eofbepos(Cube cube)
+{
+ Edge e;
+ Cube ret;
+ CubeArray *arr = new_cubearray(cube, pf_all);
+
+ memcpy(arr->eorl, arr->eofb, 12 * sizeof(int));
+ memcpy(arr->eoud, arr->eofb, 12 * sizeof(int));
+
+ for (e = 0; e < 12; e++) {
+ if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) ||
+ (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0))
+ arr->eorl[e] = 1 - arr->eorl[e];
+ if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) ||
+ (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2))
+ arr->eoud[e] = 1 - arr->eoud[e];
+ }
+
+ ret = arrays_to_cube(arr, pf_all);
+ free_cubearray(arr, pf_all);
+
+ return ret;
+}
+
+
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ static bool initialized = false;
+ static Cube admissible_ee_aux[POW2TO11*BINOM12ON4];
+ static Cube c1;
+ static int k;
+ static uint64_t ui;
+
+ if (!initialized) {
+ for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) {
+ k = (ui / POW2TO11) * 24;
+ c1 = admissible_ep((Cube){ .epose = k }, pf_e);
+ c1.eofb = ui % POW2TO11;
+ c1 = admissible_eos_from_eofbepos(c1);
+ admissible_ee_aux[ui] = c1;
+ }
+
+ initialized = true;
+ }
+
+ return admissible_ee_aux[ind];
+}
+
+static Cube
+antindex_epud(uint64_t ind)
+{
+ static bool initialized = false;
+ static Cube epud_aux[FACTORIAL8];
+ int a[12];
+ uint64_t ui;
+ CubeArray arr;
+
+ if (!initialized) {
+ a[FR] = FR;
+ a[FL] = FL;
+ a[BL] = BL;
+ a[BR] = BR;
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ index_to_perm(ui, 8, a);
+ arr.ep = a;
+ epud_aux[ui] = arrays_to_cube(&arr, pf_ep);
+ }
+
+ initialized = true;
+ }
+
+ return epud_aux[ind];
+}
+
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+static Cube
+antindex_cp(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.cp = ind;
+
+ return c;
+}
+
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = anti_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+/* TODO: admissible eos and cos */
+/* DONE: temporary fix, make it better */
+/* Or maybe it's ok like this? */
+static Cube
+antindex_drud(uint64_t ind)
+{
+ uint64_t epos, eofb;
+ Cube c;
+
+ eofb = ind % POW2TO11;
+ epos = ind / (POW2TO11 * POW3TO7);
+ c = antindex_eofbepos(eofb + POW2TO11 * epos);
+
+ c.coud = (ind / POW2TO11) % POW3TO7;
+ c.corl = c.coud;
+ c.cofb = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_drud_eofb(uint64_t ind)
+{
+ return antindex_drud(ind * POW2TO11);
+}
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_cp_sym16(uint64_t ind)
+{
+ return sd_cp_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/POW3TO7];
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_drudfin_noE_sym16(uint64_t ind)
+{
+ Cube c1, c2;
+
+ c1 = antindex_epud(ind % FACTORIAL8);
+ c2 = sd_cp_16.rep[ind/FACTORIAL8];
+ c1.cp = c2.cp;
+
+ return c1;
+}
+
+static Cube
+antindex_htrfin(uint64_t ind)
+{
+ Cube ret = {0};
+ uint64_t cp1, cp2;
+
+ static bool initialized = false;
+ static int i, j, k, c[8], c1[4], c2[4], cp[24][24];
+ static int c1solved[4] = {UFR, UBL, DFL, DBR};
+ static int c2solved[4] = {UFL, UBR, DFR, DBL};
+
+ if (!initialized) {
+ for (i = 0; i < 24; i++) {
+ for (j = 0; j < 24; j++) {
+ index_to_perm(i, 4, c1);
+ index_to_perm(j, 4, c2);
+ for (k = 0; k < 8; k++)
+ if (k == UFR || k == UBL ||
+ k == DFL || k == DBR)
+ c[k] = c1[c1solved[k/2]];
+ else
+ c[k] = c2[c2solved[k/2]];
+
+ cp[i][j] = perm_to_index(c, 8);
+ }
+ }
+
+ initialized = true;
+ }
+
+ cp2 = ind % 24;
+ ind /= 24;
+ cp1 = ind % 24;
+ ret.cp = cp[cp1][cp2];
+
+ ind /= 24;
+ ret.eposm = ind % 24;
+ ind /= 24;
+ ret.eposs = ind % 24;
+ ret.epose = ind / 24;
+
+ return ret;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)];
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+bool
+check_corners(Cube cube)
+{
+ return cube.cp == 0 && cube.coud == 0;
+}
+
+bool
+check_cp(Cube cube)
+{
+ return cube.cp == 0;
+}
+
+bool
+check_cornershtr(Cube cube)
+{
+ return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/
+}
+
+bool
+check_coud(Cube cube)
+{
+ return cube.coud == 0;
+}
+
+bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+bool
+check_htr(Cube cube)
+{
+ return check_cornershtr(cube) &&
+ cube.eofb == 0 && cube.eorl == 0 && cube.eoud == 0;
+}
+
+bool
+check_htrfin(Cube cube)
+{
+ return cube.cp == 0 &&
+ cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_drudfin_noE(Cube cube)
+{
+ return cube.eposs == 0 && cube.eposm == 0 && cube.cp == 0;
+}
+
+bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+bool
+check_eofbepos(Cube cube)
+{
+ return cube.eofb == 0 && cube.epose / 24 == 0;
+}
+
+bool
+check_epose(Cube cube)
+{
+ return cube.epose == 0;
+}
+
+bool
+check_ep(Cube cube)
+{
+ return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0;
+}
+
+bool
+check_khuge(Cube cube)
+{
+ return check_drud(cube) && cube.epose % 24 == 0;
+}
+
+bool
+check_nothing(Cube cube)
+{
+ return is_admissible(cube); /*TODO: maybe change?*/
+}
+
+static int
+epos_dependent_pos(int poss, int pose)
+{
+ static int epe_solved[4] = {FR, FL, BL, BR};
+ static int eps_solved[4] = {UL, UR, DL, DR};
+ int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved);
+ epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved);
+
+ for (i = 0, j = 0; i < 12; i++)
+ if (edge_slice(ep[i]) != 0)
+ ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0;
+
+ swap(&ep8[1], &ep8[4]);
+ swap(&ep8[3], &ep8[6]);
+
+ return subset_to_index(ep8, 8, 4);
+}
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_epud(Cube cube)
+{
+ uint64_t ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = perm_to_index(arr->ep, 8);
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cp(Cube cube)
+{
+ return cube.cp;
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + cphtr(cube);
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud_eofb(Cube cube)
+{
+ return index_drud(cube) / POW2TO11;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[index_coud(cube)];
+}
+
+static uint64_t
+index_cp_sym16(Cube cube)
+{
+ return sd_cp_16.class[index_cp(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_drudfin_noE_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_cp_16.transtorep[index_cp(cube)];
+ c = apply_trans(t, cube);
+
+ return index_cp_sym16(c) * FACTORIAL8 + index_epud(c);
+}
+
+static uint64_t
+index_htrfin(Cube cube)
+{
+ uint64_t epe, eps, epm, cp, ep;
+
+ static bool initialized = false;
+ static uint64_t cp1[FACTORIAL8], cp2[FACTORIAL8];
+ static unsigned int i;
+ static int j, n1, n2, c[8], c1[4], c2[4];
+
+ if (!initialized) {
+ for (i = 0; i < FACTORIAL8; i++) {
+ index_to_perm(i, 8, c);
+ n1 = 0;
+ n2 = 0;
+ for (j = 0; j < 8; j++)
+ if (c[j] == UFR || c[j] == UBL ||
+ c[j] == DFL || c[j] == DBR)
+ c1[n1++] = c[j] / 2;
+ else
+ c2[n2++] = c[j] / 2;
+
+ cp1[i] = perm_to_index(c1, 4);
+ cp2[i] = perm_to_index(c2, 4);
+ }
+
+ initialized = true;
+ }
+
+ epe = cube.epose % 24;
+ eps = cube.eposs % 24;
+ epm = cube.eposm % 24;
+
+ cp = cp1[cube.cp] * 24 + cp2[cube.cp];
+ ep = (epe * 24 + eps) *24 + epm;
+
+ return ep * 24 * 24 + cp;
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[index_eofbepos(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[index_eofbepos(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+/* Init functions implementation *********************************************/
+
+/*
+ * There is certainly a bette way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+ Move moves[6] = {U2, D2, R2, L2, F2, B2};
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = 0; k < 6; k++) {
+ /*jj = cp_mtable[moves[k]][next[j]];*/
+ /* TODO fix formatting */
+ jj = apply_move(moves[k], (Cube){.cp=next[j]}).cp;
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ /* TODO: use antindexer, it's nicer */
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_symdata()
+{
+ int i;
+
+ for (i = 0; i < n_all_symdata; i++)
+ gensym(all_sd[i]);
+}
+
+/*TODO maybe move the next two */
+uint64_t
+cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+Cube
+anti_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+uint64_t
+epos_dependent(Cube c)
+{
+ static int initialized = false;
+ static int aux[BINOM12ON4][BINOM12ON4];
+ static uint64_t ui, uj;
+
+ if (!initialized) {
+ for (ui = 0; ui < BINOM12ON4; ui++)
+ for (uj = 0; uj < BINOM12ON4; uj++)
+ aux[ui][uj] = epos_dependent_pos(ui, uj);
+
+ initialized = true;
+ }
+
+ return aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4];
+}
+
+void
+init_coord()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ init_cphtr_cosets();
+ init_symdata();
+}
+
diff --git a/old/coord.h b/old/coord.h
@@ -0,0 +1,45 @@
+#ifndef COORD_H
+#define COORD_H
+
+#include "trans.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_cp;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_drud;
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_drud_eofb;
+extern Coordinate coord_cp_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_drudfin_noE_sym16;
+extern Coordinate coord_htrfin;
+extern Coordinate coord_khuge;
+
+uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */
+Cube anti_cphtr(uint64_t ind); /*TODO also this */
+uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */
+
+bool check_centers(Cube cube);
+bool check_corners(Cube cube);
+bool check_cp(Cube cube);
+bool check_cornershtr(Cube cube);
+bool check_coud(Cube cube);
+bool check_drud(Cube cube);
+bool check_htr(Cube cube);
+bool check_htrfin(Cube cube);
+bool check_drudfin_noE(Cube cube);
+bool check_eofb(Cube cube);
+bool check_eofbepos(Cube cube);
+bool check_epose(Cube cube);
+bool check_ep(Cube cube);
+bool check_khuge(Cube cube);
+bool check_nothing(Cube cube);
+
+void init_coord();
+
+#endif
+
diff --git a/old/utils.c b/old/utils.c
@@ -0,0 +1,294 @@
+#include "utils.h"
+
+/* Generic utility functions *************************************************/
+
+void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ intarrcopy(aux, set, n);
+ free(aux);
+}
+
+void
+intarrcopy(int *src, int *dst, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ dst[i] = src[i];
+}
+
+bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+int
+sum(int *a, int n)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < n; i++)
+ ret += a[i];
+
+ return ret;
+}
+
+void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
+}
+
+/* Standard mathematical functions *******************************************/
+
+int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0; /* Truncate */
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+/* Conversions to and from int (base b digits, permutations...) **************/
+
+int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+int
+invert_digits(int a, int b, int n)
+{
+ int i, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ return digit_array_to_int(r, n, b);
+}
+
+void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+int subset_to_index(int *a, int n, int k) {
+ int i, ret = 0;
+
+ /* TODO: better checks */
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
diff --git a/old/utils.h b/old/utils.h
@@ -0,0 +1,59 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* Constants and macros *****************************************************/
+
+#define pow2to11 2048
+#define pow2to12 4096
+#define pow3to7 2187
+#define pow3to8 6561
+#define pow12to4 20736
+#define factorial4 24
+#define factorial6 720
+#define factorial8 40320
+#define factorial12 479001600
+#define binom12on4 495
+#define binom8on4 70
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Generic utility functions *************************************************/
+
+void apply_permutation(int *perm, int *set, int n);
+void intarrcopy(int *src, int *dst, int n);
+bool is_perm(int *a, int n);
+bool is_subset(int *a, int n, int k);
+int sum(int *a, int n);
+void sum_arrays_mod(int *src, int *dst, int n, int m);
+void swap(int *a, int *b);
+
+/* Standard mathematical functions *******************************************/
+
+int binomial(int n, int k);
+int factorial(int n);
+int perm_sign(int a[], int n);
+int powint(int a, int b);
+
+/* Conversions to and from int (base b digits, permutations...) **************/
+
+int digit_array_to_int(int *a, int n, int b);
+void int_to_digit_array(int a, int b, int n, int *r);
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+int invert_digits(int a, int b, int n);
+
+void index_to_perm(int p, int n, int *r);
+int perm_to_index(int *a, int n);
+
+void index_to_subset(int s, int n, int k, int *r);
+int subset_to_index(int *a, int n, int k);
+
+/* Am I not using these two?
+void index_to_ordered_subset(int s, int n, int k, int *r);
+int ordered_subset_to_index(int *a, int n, int k);
+*/
+
+#endif
diff --git a/src/alg.c b/src/alg.c
@@ -0,0 +1,364 @@
+#include "alg.h"
+
+/* Local functions ***********************************************************/
+
+static void free_alglistnode(AlgListNode *aln);
+static void realloc_alg(Alg *alg, int n);
+
+/* Movesets ******************************************************************/
+
+bool
+moveset_HTM(Move m)
+{
+ return m >= U && m <= B3;
+}
+
+bool
+moveset_URF(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == R || b == F;
+}
+
+bool
+moveset_eofb(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == D || b == R || b == L ||
+ ((b == F || b == B) && m == b+1);
+}
+
+bool
+moveset_drud(Move m)
+{
+ Move b = base_move(m);
+
+ return b == U || b == D ||
+ ((b == R || b == L || b == F || b == B) && m == b + 1);
+}
+
+bool
+moveset_htr(Move m)
+{
+ Move b = base_move(m);
+
+ return moveset_HTM(m) && m == b + 1;
+}
+
+
+/* Functions *****************************************************************/
+
+void
+append_alg(AlgList *l, Alg *alg)
+{
+ AlgListNode *node = malloc(sizeof(AlgListNode));
+ int i;
+
+ node->alg = new_alg("");
+ for (i = 0; i < alg->len; i++)
+ append_move(node->alg, alg->move[i], alg->inv[i]);
+ node->next = NULL;
+
+ if (++l->len == 1)
+ l->first = node;
+ else
+ l->last->next = node;
+ l->last = node;
+}
+
+void
+append_move(Alg *alg, Move m, bool inverse)
+{
+ if (alg->len == alg->allocated)
+ realloc_alg(alg, 2*alg->len);
+
+ alg->move[alg->len] = m;
+ alg->inv [alg->len] = inverse;
+ alg->len++;
+}
+
+Move
+base_move(Move m)
+{
+ if (m == NULLMOVE)
+ return NULLMOVE;
+ else
+ return m - (m-1)%3;
+}
+
+void
+compose_alg(Alg *alg1, Alg *alg2)
+{
+ int i;
+
+ for (i = 0; i < alg2->len; i++)
+ append_move(alg1, alg2->move[i], alg2->inv[i]);
+}
+
+void
+free_alg(Alg *alg)
+{
+ free(alg->move);
+ free(alg->inv);
+ free(alg);
+}
+
+void
+free_alglist(AlgList *l)
+{
+ AlgListNode *aux, *i = l->first;
+
+ while (i != NULL) {
+ aux = i->next;
+ free_alglistnode(i);
+ i = aux;
+ }
+ free(l);
+}
+
+static void
+free_alglistnode(AlgListNode *aln)
+{
+ free_alg(aln->alg);
+ free(aln);
+}
+
+Alg *
+inverse_alg(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = alg->len-1; i >= 0; i--)
+ append_move(ret, inverse_move(alg->move[i]), alg->inv[i]);
+
+ return ret;
+}
+
+Move
+inverse_move(Move m)
+{
+ return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3);
+}
+
+char *
+move_string(Move m)
+{
+ static char move_string_aux[NMOVES][7] = {
+ [NULLMOVE] = "-",
+ [U] = "U", [U2] = "U2", [U3] = "U\'",
+ [D] = "D", [D2] = "D2", [D3] = "D\'",
+ [R] = "R", [R2] = "R2", [R3] = "R\'",
+ [L] = "L", [L2] = "L2", [L3] = "L\'",
+ [F] = "F", [F2] = "F2", [F3] = "F\'",
+ [B] = "B", [B2] = "B2", [B3] = "B\'",
+ [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'",
+ [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'",
+ [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'",
+ [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'",
+ [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'",
+ [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'",
+ [M] = "M", [M2] = "M2", [M3] = "M\'",
+ [E] = "E", [E2] = "E2", [E3] = "E\'",
+ [S] = "S", [S2] = "S2", [S3] = "S\'",
+ [x] = "x", [x2] = "x2", [x3] = "x\'",
+ [y] = "y", [y2] = "y2", [y3] = "y\'",
+ [z] = "z", [z2] = "z2", [z3] = "z\'",
+ };
+
+ return move_string_aux[m];
+}
+
+void
+movelist_to_position(Move *movelist, int *position)
+{
+ Move m;
+
+ for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++)
+ position[movelist[m]] = m;
+}
+
+void
+moveset_to_list(Moveset ms, Move *r)
+{
+ int n = 0;
+ Move i;
+
+ if (ms == NULL) {
+ fprintf(stderr, "Error: no moveset given\n");
+ return;
+ }
+
+ for (i = U; i < NMOVES; i++)
+ if (ms(i))
+ r[n++] = i;
+
+ r[n] = NULLMOVE;
+}
+
+Alg *
+new_alg(char *str)
+{
+ Alg *alg = malloc(sizeof(Alg));
+ int i;
+ bool niss = false, move_read;
+ Move j, m;
+
+ alg->move = malloc(30 * sizeof(Move));
+ alg->inv = malloc(30 * sizeof(bool));
+ alg->allocated = 30;
+ alg->len = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
+ continue;
+
+ if (str[i] == '(' && niss) {
+ fprintf(stderr, "Error reading moves: nested ( )\n");
+ return alg;
+ }
+
+ if (str[i] == ')' && !niss) {
+ fprintf(stderr, "Error reading moves: unmatched )\n");
+ return alg;
+ }
+
+ if (str[i] == '(' || str[i] == ')') {
+ niss = !niss;
+ continue;
+ }
+
+ move_read = false;
+ for (j = 0; j < NMOVES; j++) {
+ if (str[i] == move_string(j)[0] ||
+ (str[i] >= 'a' && str[i] <= 'z' &&
+ str[i] == move_string(j)[0]-('A'-'a') && j<=B)) {
+ m = j;
+ if (str[i] >= 'a' && str[i] <= 'z' && j<=B) {
+ m += Uw - U;
+ }
+ if (m <= B && str[i+1]=='w') {
+ m += Uw - U;
+ i++;
+ }
+ if (str[i+1]=='2') {
+ m += 1;
+ i++;
+ } else if (str[i+1] == '\'' ||
+ str[i+1] == '3' ||
+ str[i+1] == '`' ) {
+ m += 2;
+ i++;
+ } else if ((int)str[i+1] == -62 &&
+ (int)str[i+2] == -76) {
+ /* Weird apostrophe */
+ m += 2;
+ i += 2;
+ } else if ((int)str[i+1] == -30 &&
+ (int)str[i+2] == -128 &&
+ (int)str[i+3] == -103) {
+ /* MacOS apostrophe */
+ m += 2;
+ i += 3;
+ }
+ append_move(alg, m, niss);
+ move_read = true;
+ break;
+ }
+ }
+
+ if (!move_read) {
+ alg = new_alg("");
+ return alg;
+ }
+ }
+
+ return alg;
+}
+
+AlgList *
+new_alglist()
+{
+ AlgList *ret = malloc(sizeof(AlgList));
+
+ ret->len = 0;
+ ret->first = NULL;
+ ret->last = NULL;
+
+ return ret;
+}
+
+Alg *
+on_inverse(Alg *alg)
+{
+ Alg *ret = new_alg("");
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ append_move(ret, alg->move[i], !alg->inv[i]);
+
+ return ret;
+}
+
+void
+print_alg(Alg *alg, bool l)
+{
+ char fill[4];
+ int i;
+ bool niss = false;
+
+ for (i = 0; i < alg->len; i++) {
+ if (!niss && alg->inv[i])
+ strcpy(fill, i == 0 ? "(" : " (");
+ if (niss && !alg->inv[i])
+ strcpy(fill, ") ");
+ if (niss == alg->inv[i])
+ strcpy(fill, i == 0 ? "" : " ");
+
+ printf("%s%s", fill, move_string(alg->move[i]));
+ niss = alg->inv[i];
+ }
+
+ if (niss)
+ printf(")");
+ if (l)
+ printf(" (%d)", alg->len);
+
+ printf("\n");
+}
+
+void
+print_alglist(AlgList *al, bool l)
+{
+ AlgListNode *i;
+
+ for (i = al->first; i != NULL; i = i->next)
+ print_alg(i->alg, l);
+}
+
+static void
+realloc_alg(Alg *alg, int n)
+{
+ if (alg == NULL) {
+ fprintf(stderr, "Error: trying to reallocate NULL alg.\n");
+ return;
+ }
+
+ if (n < alg->len) {
+ fprintf(stderr, "Error: alg too long for reallocation ");
+ fprintf(stderr, "(%d vs %d)\n", alg->len, n);
+ return;
+ }
+
+ if (n > 1000000) {
+ fprintf(stderr, "Warning: very long alg,");
+ fprintf(stderr, "something might go wrong.\n");
+ }
+
+ alg->move = realloc(alg->move, n * sizeof(int));
+ alg->inv = realloc(alg->inv, n * sizeof(int));
+ alg->allocated = n;
+}
+
diff --git a/src/alg.h b/src/alg.h
@@ -0,0 +1,35 @@
+#ifndef ALG_H
+#define ALG_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cubetypes.h"
+#include "utils.h"
+
+bool moveset_HTM(Move m);
+bool moveset_URF(Move m);
+bool moveset_eofb(Move m);
+bool moveset_drud(Move m);
+bool moveset_htr(Move m);
+
+void append_alg(AlgList *l, Alg *alg);
+void append_move(Alg *alg, Move m, bool inverse);
+void compose_alg(Alg *alg1, Alg *alg2);
+Move base_move(Move m);
+void free_alg(Alg *alg);
+void free_alglist(AlgList *l);
+Alg * inverse_alg(Alg *alg);
+Move inverse_move(Move m);
+char * move_string(Move m);
+void movelist_to_position(Move *ml, int *pos);
+void moveset_to_list(Moveset ms, Move *lst);
+Alg * new_alg(char *str);
+AlgList * new_alglist();
+Alg * on_inverse(Alg *alg);
+void print_alg(Alg *alg, bool l);
+void print_alglist(AlgList *al, bool l);
+
+#endif
+
diff --git a/src/commands.c b/src/commands.c
@@ -0,0 +1,346 @@
+#include "commands.h"
+
+/* Arg parsing functions *****************************************************/
+
+CommandArgs * solve_parse_args(int c, char **v);
+CommandArgs * help_parse_args(int c, char **v);
+CommandArgs * print_parse_args(int c, char **v);
+CommandArgs * parse_no_arg(int c, char **v);
+
+/* Exec functions ************************************************************/
+
+static void solve_exec(CommandArgs *args);
+static void steps_exec(CommandArgs *args);
+static void commands_exec(CommandArgs *args);
+static void print_exec(CommandArgs *args);
+static void help_exec(CommandArgs *args);
+static void quit_exec(CommandArgs *args);
+static void version_exec(CommandArgs *args);
+
+/* Local functions ***********************************************************/
+
+static bool read_step(CommandArgs *args, char *str);
+static bool read_scramble(int c, char **v, CommandArgs *args);
+
+/* Commands ******************************************************************/
+
+Command
+solve_cmd = {
+ .name = "solve",
+ .usage = "solve STEP [OPTIONS] SCRAMBLE",
+ .description = "Solve a step",
+ .parse_args = solve_parse_args,
+ .exec = solve_exec
+};
+
+Command
+steps_cmd = {
+ .name = "steps",
+ .usage = "steps",
+ .description = "List available steps",
+ .parse_args = parse_no_arg,
+ .exec = steps_exec
+};
+
+Command
+commands_cmd = {
+ .name = "commands",
+ .usage = "commands",
+ .description = "List available commands",
+ .parse_args = parse_no_arg,
+ .exec = commands_exec
+};
+
+Command
+print_cmd = {
+ .name = "print",
+ .usage = "print SCRAMBLE",
+ .description = "Print written description of the cube",
+ .parse_args = print_parse_args,
+ .exec = print_exec,
+};
+
+Command
+help_cmd = {
+ .name = "help",
+ .usage = "help [COMMAND]",
+ .description = "Display nissy manual page or help on specific command",
+ .parse_args = help_parse_args,
+ .exec = help_exec,
+};
+
+Command
+quit_cmd = {
+ .name = "quit",
+ .usage = "quit",
+ .description = "Quit nissy",
+ .parse_args = parse_no_arg,
+ .exec = quit_exec,
+};
+
+Command
+version_cmd = {
+ .name = "version",
+ .usage = "version",
+ .description = "print nissy version",
+ .parse_args = parse_no_arg,
+ .exec = version_exec,
+};
+
+Command *commands[NCOMMANDS] = {
+ &commands_cmd,
+ &help_cmd,
+ &print_cmd,
+ &quit_cmd,
+ &solve_cmd,
+ &steps_cmd,
+ &version_cmd,
+};
+
+/* Arg parsing functions implementation **************************************/
+
+CommandArgs *
+solve_parse_args(int c, char **v)
+{
+ int i;
+ long val;
+
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->success = false;
+ a->opts = malloc(sizeof(SolveOptions));
+ a->step = steps[0];
+ a->command = NULL;
+ a->scramble = NULL;
+
+ a->opts->min_moves = 0;
+ a->opts->max_moves = 20;
+ a->opts->max_solutions = 1;
+ a->opts->optimal_only = false;
+ a->opts->can_niss = false;
+ a->opts->verbose = false;
+ a->opts->all = false;
+ a->opts->print_number = true;
+
+ for (i = 0; i < c; i++) {
+ if (!strcmp(v[i], "-m")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 0 || val > 100) {
+ fprintf(stderr,
+ "Invalid min number of moves.\n");
+ return a;
+ }
+ a->opts->min_moves = val;
+ } else if (!strcmp(v[i], "-M")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 0 || val > 100) {
+ fprintf(stderr,
+ "Invalid max number of moves.\n");
+ return a;
+ }
+ a->opts->max_moves = val;
+ } else if (!strcmp(v[i], "-s")) {
+ val = strtol(v[++i], NULL, 10);
+ if (val < 1 || val > 1000000) {
+ fprintf(stderr,
+ "Invalid number of solutions.\n");
+ return a;
+ }
+ a->opts->max_solutions = val;
+ } else if (!strcmp(v[i], "-o")) {
+ a->opts->optimal_only = true;
+ } else if (!strcmp(v[i], "-n")) {
+ a->opts->can_niss = true;
+ } else if (!strcmp(v[i], "-v")) {
+ a->opts->verbose = true;
+ } else if (!strcmp(v[i], "-a")) {
+ a->opts->all = true;
+ } else if (!strcmp(v[i], "-p")) {
+ a->opts->print_number = false;
+ } else if (!read_step(a, v[i])) {
+ break;
+ }
+ }
+
+ a->success = read_scramble(c-i, &v[i], a);
+ return a;
+}
+
+CommandArgs *
+help_parse_args(int c, char **v)
+{
+ int i;
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->scramble = NULL;
+ a->opts = NULL;
+ a->step = NULL;
+ a->command = NULL;
+
+ if (c == 1) {
+ for (i = 0; i < NCOMMANDS; i++)
+ if (commands[i] != NULL &&
+ !strcmp(v[0], commands[i]->name))
+ a->command = commands[i];
+ if (a->command == NULL)
+ fprintf(stderr, "%s: command not found\n", v[0]);
+ }
+
+ a->success = c == 0 || (c == 1 && a->command != NULL);
+ return a;
+}
+
+CommandArgs *
+parse_no_arg(int c, char **v)
+{
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->success = true;
+
+ return a;
+}
+
+CommandArgs *
+print_parse_args(int c, char **v)
+{
+ CommandArgs *a = malloc(sizeof(CommandArgs));
+
+ a->success = read_scramble(c, v, a);
+ return a;
+}
+
+/* Exec functions implementation *********************************************/
+
+static void
+solve_exec(CommandArgs *args)
+{
+ Cube c;
+ AlgList *sols;
+
+ init_symcoord();
+
+ c = apply_alg(args->scramble, (Cube){0});
+ sols = solve(c, args->step, args->opts);
+
+ print_alglist(sols, args->opts->print_number);
+ free_alglist(sols);
+}
+
+static void
+steps_exec(CommandArgs *args)
+{
+ int i;
+
+ for (i = 0; i < NSTEPS && steps[i] != NULL; i++)
+ printf("%-15s %s\n", steps[i]->shortname, steps[i]->name);
+}
+
+static void
+commands_exec(CommandArgs *args)
+{
+ int i;
+
+ for (i = 0; i < NCOMMANDS && commands[i] != NULL; i++)
+ printf("%s\n", commands[i]->usage);
+
+}
+
+static void
+print_exec(CommandArgs *args)
+{
+ init_moves();
+ print_cube(apply_alg(args->scramble, (Cube){0}));
+}
+
+static void
+help_exec(CommandArgs *args)
+{
+ /* TODO: print full nissy manpage */
+ if (args->command == NULL) {
+ printf("Type help COMMAND for information on a ");
+ printf("specific command.\n");
+ printf("A more complete manual page is work in progress.\n");
+ } else {
+ printf("Command %s: %s\nusage: %s\n", args->command->name,
+ args->command->description, args->command->usage);
+ }
+}
+
+static void
+quit_exec(CommandArgs *args)
+{
+ exit(0);
+}
+
+static void
+version_exec(CommandArgs *args)
+{
+ printf(VERSION"\n");
+}
+
+/* Local functions implementation ********************************************/
+
+static bool
+read_step(CommandArgs *args, char *str)
+{
+ int i;
+
+ for (i = 0; i < NSTEPS; i++) {
+ if (steps[i] != NULL && !strcmp(steps[i]->shortname, str)) {
+ args->step = steps[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool
+read_scramble(int c, char **v, CommandArgs *args)
+{
+ int i, k, n;
+ unsigned int j;
+ char *algstr;
+
+ if (new_alg(v[0])->len == 0) {
+ fprintf(stderr, "%s: moves or option unrecognized\n", v[0]);
+ return false;
+ }
+
+ n = 0;
+ for(i = 0; i < c; i++)
+ n += strlen(v[i]);
+
+ algstr = malloc((n + 1) * sizeof(char));
+ k = 0;
+ for (i = 0; i < c; i++)
+ for (j = 0; j < strlen(v[i]); j++)
+ algstr[k++] = v[i][j];
+ algstr[k] = 0;
+
+ args->scramble = new_alg(algstr);
+ free(algstr);
+
+ if (args->scramble->len == 0)
+ fprintf(stderr, "Error reading scramble\n");
+
+ return args->scramble->len > 0;
+}
+
+/* Public functions implementation *******************************************/
+
+void
+free_args(CommandArgs *args)
+{
+ if (args == NULL)
+ return;
+
+ if (args->scramble != NULL)
+ free_alg(args->scramble);
+ if (args->opts != NULL)
+ free(args->opts);
+
+ /* step and command must not be freed, they are static! */
+
+ free(args);
+}
diff --git a/src/commands.h b/src/commands.h
@@ -0,0 +1,13 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+#include "solve.h"
+#include "steps.h"
+
+#define NCOMMANDS 10
+
+void free_args(CommandArgs *args);
+
+extern Command * commands[NCOMMANDS];
+
+#endif
diff --git a/src/coord.c b/src/coord.c
@@ -0,0 +1,522 @@
+#include "coord.h"
+
+static Cube antindex_eofb(uint64_t ind);
+static Cube antindex_eofbepos(uint64_t ind);
+static Cube antindex_epud(uint64_t ind);
+static Cube antindex_coud(uint64_t ind);
+static Cube antindex_corners(uint64_t ind);
+static Cube antindex_cp(uint64_t ind);
+static Cube antindex_cphtr(uint64_t);
+static Cube antindex_cornershtr(uint64_t ind);
+static Cube antindex_cornershtrfin(uint64_t ind);
+static Cube antindex_drud(uint64_t ind);
+static Cube antindex_drud_eofb(uint64_t ind);
+static Cube antindex_htr_drud(uint64_t ind);
+static Cube antindex_htrfin(uint64_t ind);
+
+static uint64_t index_eofb(Cube cube);
+static uint64_t index_eofbepos(Cube cube);
+static uint64_t index_epud(Cube cube);
+static uint64_t index_coud(Cube cube);
+static uint64_t index_corners(Cube cube);
+static uint64_t index_cp(Cube cube);
+static uint64_t index_cphtr(Cube cube);
+static uint64_t index_cornershtr(Cube cube);
+static uint64_t index_cornershtrfin(Cube cube);
+static uint64_t index_drud(Cube cube);
+static uint64_t index_drud_eofb(Cube cube);
+static uint64_t index_htr_drud(Cube cube);
+static uint64_t index_htrfin(Cube cube);
+
+static void init_cphtr_cosets();
+static void init_cphtr_left_cosets_bfs(int i, int c);
+static void init_cphtr_right_cosets_color(int i, int c);
+static void init_cornershtrfin();
+
+
+/* All sorts of useful costants and tables **********************************/
+
+static int cphtr_left_cosets[FACTORIAL8];
+static int cphtr_right_cosets[FACTORIAL8];
+static int cphtr_right_rep[BINOM8ON4*6];
+static int cornershtrfin_ind[FACTORIAL8];
+static int cornershtrfin_ant[24*24/6];
+
+/* Coordinates and their implementation **************************************/
+
+Coordinate
+coord_eofb = {
+ .index = index_eofb,
+ .cube = antindex_eofb,
+ .max = POW2TO11,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_eofbepos = {
+ .index = index_eofbepos,
+ .cube = antindex_eofbepos,
+ .max = POW2TO11 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_coud = {
+ .index = index_coud,
+ .cube = antindex_coud,
+ .max = POW3TO7,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_corners = {
+ .index = index_corners,
+ .cube = antindex_corners,
+ .max = POW3TO7 * FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cp = {
+ .index = index_cp,
+ .cube = antindex_cp,
+ .max = FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cphtr = {
+ .index = index_cphtr,
+ .cube = antindex_cphtr,
+ .max = BINOM8ON4 * 6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cornershtr = {
+ .index = index_cornershtr,
+ .cube = antindex_cornershtr,
+ .max = POW3TO7 * BINOM8ON4 * 6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_cornershtrfin = {
+ .index = index_cornershtrfin,
+ .cube = antindex_cornershtrfin,
+ .max = 24*24/6,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_epud = {
+ .index = index_epud,
+ .cube = antindex_epud,
+ .max = FACTORIAL8,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_drud = {
+ .index = index_drud,
+ .cube = antindex_drud,
+ .max = POW2TO11 * POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_htr_drud = {
+ .index = index_htr_drud,
+ .cube = antindex_htr_drud,
+ .max = BINOM8ON4 * 6 * BINOM8ON4,
+ .ntrans = 1,
+};
+
+Coordinate
+coord_htrfin = {
+ .index = index_htrfin,
+ .cube = antindex_htrfin,
+ .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */
+ .ntrans = 1,
+};
+
+Coordinate
+coord_drud_eofb = {
+ .index = index_drud_eofb,
+ .cube = antindex_drud_eofb,
+ .max = POW3TO7 * BINOM12ON4,
+ .ntrans = 1,
+};
+
+/* Functions *****************************************************************/
+
+static Cube
+antindex_eofb(uint64_t ind)
+{
+ return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind };
+}
+
+static Cube
+antindex_eofbepos(uint64_t ind)
+{
+ Cube ret = {0};
+
+ ret.eofb = ind % POW2TO11;
+ ret.epose = (ind / POW2TO11) * 24;
+
+ return ret;
+}
+
+static Cube
+antindex_epud(uint64_t ind)
+{
+ static bool initialized = false;
+ static Cube epud_aux[FACTORIAL8];
+ int a[12];
+ uint64_t ui;
+ CubeArray arr;
+
+ if (!initialized) {
+ a[FR] = FR;
+ a[FL] = FL;
+ a[BL] = BL;
+ a[BR] = BR;
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ index_to_perm(ui, 8, a);
+ arr.ep = a;
+ epud_aux[ui] = arrays_to_cube(&arr, pf_ep);
+ }
+
+ initialized = true;
+ }
+
+ return epud_aux[ind];
+}
+
+static Cube
+antindex_coud(uint64_t ind)
+{
+ return (Cube){ .coud = ind, .corl = ind, .cofb = ind };
+}
+
+static Cube
+antindex_corners(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.coud = ind / FACTORIAL8;
+ c.cp = ind % FACTORIAL8;
+
+ return c;
+}
+
+static Cube
+antindex_cp(uint64_t ind)
+{
+ Cube c = {0};
+
+ c.cp = ind;
+
+ return c;
+}
+
+static Cube
+antindex_cphtr(uint64_t ind)
+{
+ return (Cube) { .cp = cphtr_right_rep[ind] };
+}
+
+static Cube
+antindex_cornershtr(uint64_t ind)
+{
+ Cube c = antindex_cphtr(ind % (BINOM8ON4 * 6));
+
+ c.coud = ind / (BINOM8ON4 * 6);
+
+ return c;
+}
+
+static Cube
+antindex_cornershtrfin(uint64_t ind)
+{
+ return (Cube){ .cp = cornershtrfin_ant[ind] };
+}
+
+static Cube
+antindex_drud(uint64_t ind)
+{
+ uint64_t epos, eofb;
+ Cube c;
+
+ eofb = ind % POW2TO11;
+ epos = ind / (POW2TO11 * POW3TO7);
+ c = antindex_eofbepos(eofb + POW2TO11 * epos);
+
+ c.coud = (ind / POW2TO11) % POW3TO7;
+
+ return c;
+}
+
+static Cube
+antindex_drud_eofb(uint64_t ind)
+{
+ return antindex_drud(ind * POW2TO11);
+}
+
+static Cube
+antindex_htr_drud(uint64_t ind)
+{
+ Cube ret;
+
+ ret = antindex_cphtr(ind / BINOM8ON4);
+ ret.eposs = (ind % BINOM8ON4) * FACTORIAL4;
+
+ return ret;
+}
+
+static Cube
+antindex_htrfin(uint64_t ind)
+{
+ Cube ret;
+
+ ret = antindex_cornershtrfin(ind/(24*24*24));
+
+ ret.eposm = ind % 24;
+ ind /= 24;
+ ret.eposs = ind % 24;
+ ind /= 24;
+ ret.epose = ind % 24;
+
+ return ret;
+}
+
+static uint64_t
+index_eofb(Cube cube)
+{
+ return cube.eofb;
+}
+
+static uint64_t
+index_eofbepos(Cube cube)
+{
+ return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb;
+}
+
+static uint64_t
+index_epud(Cube cube)
+{
+ uint64_t ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = perm_to_index(arr->ep, 8);
+ free_cubearray(arr, pf_ep);
+
+ return ret;
+}
+
+static uint64_t
+index_coud(Cube cube)
+{
+ return cube.coud;
+}
+
+static uint64_t
+index_corners(Cube cube)
+{
+ return cube.coud * FACTORIAL8 + cube.cp;
+}
+
+static uint64_t
+index_cp(Cube cube)
+{
+ return cube.cp;
+}
+
+static uint64_t
+index_cphtr(Cube cube)
+{
+ return cphtr_right_cosets[cube.cp];
+}
+
+static uint64_t
+index_cornershtr(Cube cube)
+{
+ return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube);
+}
+
+static uint64_t
+index_cornershtrfin(Cube cube)
+{
+ return cornershtrfin_ind[cube.cp];
+}
+
+static uint64_t
+index_drud(Cube cube)
+{
+ uint64_t a, b, c;
+
+ a = cube.eofb;
+ b = cube.coud;
+ c = cube.epose / FACTORIAL4;
+
+ b *= POW2TO11;
+ c *= POW2TO11 * POW3TO7;
+
+ return a + b + c;
+}
+
+static uint64_t
+index_drud_eofb(Cube cube)
+{
+ return index_drud(cube) / POW2TO11;
+}
+
+static uint64_t
+index_htr_drud(Cube cube)
+{
+ return index_cphtr(cube) * BINOM8ON4 +
+ (cube.eposs / FACTORIAL4) % BINOM8ON4;
+}
+
+static uint64_t
+index_htrfin(Cube cube)
+{
+ uint64_t epe, eps, epm, cp, ep;
+
+ epe = cube.epose % 24;
+ eps = cube.eposs % 24;
+ epm = cube.eposm % 24;
+ ep = (epe * 24 + eps) *24 + epm;
+ cp = index_cornershtrfin(cube);
+
+ return cp * 24 * 24 * 24 + ep;
+}
+
+/* Init functions implementation *********************************************/
+
+/*
+ * There is certainly a better way to do this, but for now I just use
+ * a "graph coloring" algorithm to compute the left cosets, and I compose
+ * with every possible cp to get the right cosets (it is possible that I am
+ * mixing up left and right).
+ *
+ * For doing it better "Mathematically", we need 3 things:
+ * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other
+ * This is easy and it is done in the commented function cphtr_cp().
+ * - Check that there is no ep/cp parity
+ * - Check that we are not in the "3c" case; this is the part I don't
+ * know how to do.
+ */
+static void
+init_cphtr_cosets()
+{
+ unsigned int i;
+ int c = 0, d = 0;
+
+ for (i = 0; i < FACTORIAL8; i++) {
+ cphtr_left_cosets[i] = -1;
+ cphtr_right_cosets[i] = -1;
+ }
+
+ /* First we compute left cosets with a bfs */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_left_cosets[i] == -1)
+ init_cphtr_left_cosets_bfs(i, c++);
+
+ /* Then we compute right cosets using compose() */
+ for (i = 0; i < FACTORIAL8; i++)
+ if (cphtr_right_cosets[i] == -1)
+ init_cphtr_right_cosets_color(i, d++);
+}
+
+static void
+init_cphtr_left_cosets_bfs(int i, int c)
+{
+ int j, jj, next[FACTORIAL8], next2[FACTORIAL8], n, n2;
+
+ Move k;
+
+ n = 1;
+ next[0] = i;
+ cphtr_left_cosets[i] = c;
+
+ while (n != 0) {
+ for (j = 0, n2 = 0; j < n; j++) {
+ for (k = U2; k < B3; k++) {
+ if (!moveset_htr(k))
+ continue;
+ jj = apply_move(k, (Cube){ .cp = next[j] }).cp;
+
+ if (cphtr_left_cosets[jj] == -1) {
+ cphtr_left_cosets[jj] = c;
+ next2[n2++] = jj;
+ }
+ }
+ }
+
+ for (j = 0; j < n2; j++)
+ next[j] = next2[j];
+ n = n2;
+ }
+}
+
+static void
+init_cphtr_right_cosets_color(int i, int d)
+{
+ int cp;
+ unsigned int j;
+
+ cphtr_right_rep[d] = i;
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cphtr_left_cosets[j] == 0) {
+ cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp;
+ cphtr_right_cosets[cp] = d;
+ }
+ }
+}
+
+static void
+init_cornershtrfin()
+{
+ unsigned int i, j;
+ int n, c;
+ Move m;
+
+ for (i = 0; i < FACTORIAL8; i++)
+ cornershtrfin_ind[i] = -1;
+ cornershtrfin_ind[0] = 0;
+
+ /* 10-pass, I think 5 is enough, but just in case */
+ n = 1;
+ for (i = 0; i < 10; i++) {
+ for (j = 0; j < FACTORIAL8; j++) {
+ if (cornershtrfin_ind[j] == -1)
+ continue;
+ for (m = U; m < NMOVES; m++) {
+ if (moveset_htr(m)) {
+ c = apply_move(m, (Cube){.cp = j}).cp;
+ if (cornershtrfin_ind[c] == -1) {
+ cornershtrfin_ind[c] = n;
+ cornershtrfin_ant[n] = c;
+ n++;
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+init_coord()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ init_trans();
+
+ init_cphtr_cosets();
+ init_cornershtrfin();
+}
+
diff --git a/src/coord.h b/src/coord.h
@@ -0,0 +1,23 @@
+#ifndef COORD_H
+#define COORD_H
+
+#include "trans.h"
+
+extern Coordinate coord_eofb;
+extern Coordinate coord_eofbepos;
+extern Coordinate coord_coud;
+extern Coordinate coord_cp;
+extern Coordinate coord_cphtr;
+extern Coordinate coord_corners;
+extern Coordinate coord_cornershtr;
+extern Coordinate coord_cornershtrfin;
+extern Coordinate coord_epud;
+extern Coordinate coord_drud;
+extern Coordinate coord_drud_eofb;
+extern Coordinate coord_htr_drud;
+extern Coordinate coord_htrfin;
+
+void init_coord();
+
+#endif
+
diff --git a/src/coordinates.c b/src/coordinates.c
@@ -1,286 +0,0 @@
-#include <stdio.h>
-
-#include "utils.h"
-#include "coordinates.h"
-
-/* Names of pieces and moves. */
-char edge_string_list[12][5] = {
- "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR"
-};
-
-char corner_string_list[8][5] = {
- "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR"
-};
-
-char move_string_list[19][5] = {
- "-",
- "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'",
- "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'"
-};
-
-int inverse_move[19] = {
- -1, U3, U2, U, D3, D2, D, R3, R2, R, L3, L2, L, F3, F2, F, B3, B2, B
-};
-
-/* Convert piece representation from integer to array.
- * Come convertions are not "perfect": for example, and epud type of piece
- * is represented by a permutation index in 8! elements, but it as an array
- * it is converted to the first 8 elements of a 12 elements ep array (with
- * meaningless values for the other 4 elements). */
-
-void ep_int_to_array(int ep, int a[12]) {
- index_to_perm(ep, 12, a);
-}
-
-void epud_int_to_array(int epud, int a[12]) {
- index_to_perm(epud, 8, a); /* Last 4 elements are left untouched. */
-}
-
-void epfb_int_to_array(int epfb, int a[12]) {
- int edges[] = {UF, UB, DF, DB, FR, FL, BL, BR};
- int b[8];
- index_to_perm(epfb, 8, b);
- for (int i = 0; i < 8; i++)
- a[edges[i]] = edges[b[i]];
-}
-
-void eprl_int_to_array(int eprl, int a[12]) {
- int edges[] = {UL, UR, DL, DR, FR, FL, BL, BR};
- int b[8];
- index_to_perm(eprl, 8, b);
- for (int i = 0; i < 8; i++)
- a[edges[i]] = edges[b[i]];
-}
-
-void epose_int_to_array(int epos, int a[12]) {
- int edges[] = {FR, FL, BL, BR};
- index_to_subset(epos, 12, 4, a);
- for (int i = 0, j = 0; i < 12; i++)
- a[i] = (a[i] == 1) ? edges[j++] : -1;
-}
-
-void eposs_int_to_array(int epos, int a[12]) {
- int edges[] = {UL, UR, DL, DR};
- index_to_subset(epos, 12, 4, a);
- for (int i = 0, j = 0; i < 12; i++)
- a[i] = (a[i] == 1) ? edges[j++] : -1;
- /* Swap with last 4, so 0 is alway solved state */
- for (int i = 0; i < 4; i++)
- swap(&a[edges[i]], &a[i+8]);
-}
-
-void eposm_int_to_array(int epos, int a[12]) {
- int edges[] = {UF, UB, DF, DB};
- index_to_subset(epos, 12, 4, a);
- for (int i = 0, j = 0; i < 12; i++)
- a[i] = (a[i] == 1) ? edges[j++] : -1;
- /* Swap with last 4, so 0 is alway solved state */
- for (int i = 0; i < 4; i++)
- swap(&a[edges[i]], &a[i+8]);
-}
-
-void epe_int_to_array(int epe, int a[12]) {
- index_to_perm(epe, 4, a+8);
- for (int i = 0; i < 4; i++)
- a[i+8] += 8;
-}
-
-void eps_int_to_array(int eps, int a[12]) {
- int edges[] = {UL, UR, DL, DR};
- int b[4];
- index_to_perm(eps, 4, b);
- for (int i = 0; i < 4; i++)
- a[edges[i]] = edges[b[i]];
-}
-
-void epm_int_to_array(int epm, int a[12]) {
- int edges[] = {UF, UB, DF, DB};
- int b[4];
- index_to_perm(epm, 4, b);
- for (int i = 0; i < 4; i++)
- a[edges[i]] = edges[b[i]];
-}
-
-void emslices_int_to_array(int emslices, int a[12]) {
- int b[] = {0,0,0,0,0,0,0,0};
- int eslice[] = {FR, FL, BL, BR};
- int mslice[] = {UF, UB, DF, DB};
-
- index_to_subset(emslices % binom12on4, 12, 4, a);
- index_to_subset(emslices / binom12on4, 8, 4, b);
-
- if (emslices % binom12on4 == 0) {
- swap(&b[UF], &b[DL]);
- swap(&b[UB], &b[DR]);
- /*for (int i = 0; i < 4; i++)
- swap(&b[mslice[i]], &b[i+4]);*/
- }
-
- for (int i = 0, j = 0; j < 8; i++, j++) {
- while (a[i])
- i++;
- a[i] = b[j] ? 2 : -1;
- }
- for (int i = 0, j1 = 0, j2 = 0; i < 12; i++) {
- if (a[i] == 1)
- a[i] = eslice[j1++];
- if (a[i] == 2)
- a[i] = mslice[j2++];
- }
-}
-
-void cp_int_to_array(int cp, int a[8]) {
- index_to_perm(cp, 8, a);
-}
-
-void eo_11bits_to_array(int eo, int a[12]) {
- int_to_sum_zero_array(eo, 2, 12, a);
-}
-
-void co_7trits_to_array(int co, int a[8]) {
- int_to_sum_zero_array(co, 3, 8, a);
-}
-
-
-
-
-
-int ep_array_to_int(int ep[12]) {
- return perm_to_index(ep, 12);
-}
-
-int epud_array_to_int(int ep[12]) {
- return perm_to_index(ep, 8); /* Last 4 elements are ignored */
-}
-
-int epfb_array_to_int(int ep[12]) {
- int index[] = {0, -1, 1, -1, 2, -1, 3, -1, 4, 5, 6, 7};
- int b[8];
- for (int i = 0; i < 12; i++)
- if (index[i] != -1)
- b[index[i]] = index[ep[i]];
- return perm_to_index(b, 8);
-}
-
-int eprl_array_to_int(int ep[12]) {
- int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, 4, 5, 6, 7};
- int b[8];
- for (int i = 0; i < 12; i++)
- if (index[i] != -1)
- b[index[i]] = index[ep[i]];
- return perm_to_index(b, 8);
-}
-
-int epose_array_to_int(int ep[12]) {
- int a[12];
- for (int i = 0; i < 12; i++)
- a[i] = (ep[i] >= FR);
- return subset_to_index(a, 12, 4);
-}
-
-int eposs_array_to_int(int ep[12]) {
- int a[12];
- int edges[] = {UL, UR, DL, DR};
- for (int i = 0; i < 12; i++)
- a[i] = (ep[i] == UL || ep[i] == UR || ep[i] == DL || ep[i] == DR);
- /* Swap with last 4, so 0 is alway solved state */
- for (int i = 0; i < 4; i++)
- swap(&a[edges[i]], &a[i+8]);
- return subset_to_index(a, 12, 4);
-}
-
-int eposm_array_to_int(int ep[12]) {
- int a[12];
- int edges[] = {UF, UB, DF, DB};
- for (int i = 0; i < 12; i++)
- a[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB);
- /* Swap with last 4, so 0 is alway solved state */
- for (int i = 0; i < 4; i++)
- swap(&a[edges[i]], &a[i+8]);
- return subset_to_index(a, 12, 4);
-}
-
-int epe_array_to_int(int ep[12]) {
- int b[4];
- for (int i = 0; i < 4; i++)
- b[i] = ep[i+8] - 8;
- return perm_to_index(b, 4);
-}
-
-int eps_array_to_int(int ep[12]) {
- int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1};
- int b[4];
- for (int i = 0; i < 12; i++)
- if (index[i] != -1)
- b[index[i]] = index[ep[i]];
- return perm_to_index(b, 4);
-}
-
-int epm_array_to_int(int ep[12]) {
- int index[] = {0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1, -1};
- int b[4];
- for (int i = 0; i < 12; i++)
- if (index[i] != -1)
- b[index[i]] = index[ep[i]];
- return perm_to_index(b, 4);
-}
-
-int emslices_array_to_int(int ep[12]) {
- int a[12], b[12], c[8] = {0, 0, 0, 0, 0, 0, 0, 0};
- /*int edges[] = {UF, UB, DF, DB};*/
- for (int i = 0; i < 12; i++) {
- a[i] = (ep[i] >= FR) ? 1 : 0;
- b[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB) ? 1 : 0;
- }
-
- /*for ( int i = 0; i < 12; i++)
- printf("%d ", ep[i]);
- printf("\n");*/
-
- for (int i = 0, j = 0; i < 12; i++, j++) {
- if (a[i])
- j--;
- if (b[i])
- c[j] = 1;
- }
-
- int epose = subset_to_index(a, 12, 4);
-
- /*if (epose == 0) {
- printf("Before: ");
- for (int i = 0; i < 8; i++)
- printf("%d ", c[i]);
- printf("\n");
- for (int i = 0; i < 4; i++)
- swap(&c[edges[i]], &c[i+4]);
- printf("After: ");
- for (int i = 0; i < 8; i++)
- printf("%d ", c[i]);
- printf("\n");
- }*/
- if (epose == 0) {
- swap(&c[UF], &c[DL]);
- swap(&c[UB], &c[DR]);
- }
-
- /*for ( int i = 0; i < 8; i++)
- printf("%d ", c[i]);
- printf("\n");*/
- int eposm = subset_to_index(c, 8, 4);
-
- return epose + 495*eposm;
-}
-
-
-int cp_array_to_int(int cp[8]) {
- return perm_to_index(cp, 8);
-}
-
-int eo_array_to_11bits(int a[12]) {
- return digit_array_to_int(a, 11, 2);
-}
-
-int co_array_to_7trits(int a[8]) {
- return digit_array_to_int(a, 7, 3);
-}
-
diff --git a/src/coordinates.h b/src/coordinates.h
@@ -1,92 +0,0 @@
-/* General rule for piece numbering (visually nicer):
- *
- * 0 1 2 3 4 5 6 7 8 9 10 11
- * UF UL UB UR DF DL DB DR FR FL BL BR
- * UFR UFL UBL UBR DFR DFL DBL DBR
- *
- * The order of moves is
- * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
- * - U U2 U' D D2 D' R R2 R' L L2 L' F F2 F' B B2 B'
- * (0 is reserved for no move) */
-
-#define UF 0
-#define UL 1
-#define UB 2
-#define UR 3
-#define DF 4
-#define DL 5
-#define DB 6
-#define DR 7
-#define FR 8
-#define FL 9
-#define BL 10
-#define BR 11
-
-#define UFR 0
-#define UFL 1
-#define UBL 2
-#define UBR 3
-#define DFR 4
-#define DFL 5
-#define DBL 6
-#define DBR 7
-
-#define U 1
-#define U2 2
-#define U3 3
-#define D 4
-#define D2 5
-#define D3 6
-#define R 7
-#define R2 8
-#define R3 9
-#define L 10
-#define L2 11
-#define L3 12
-#define F 13
-#define F2 14
-#define F3 15
-#define B 16
-#define B2 17
-#define B3 18
-
-extern char edge_string_list[12][5];
-extern char corner_string_list[8][5];
-extern char move_string_list[19][5];
-extern int inverse_move[19];
-
-/* Convert piece representation from integer to array.
- * Come convertions are not "perfect": for example, and epud type of piece
- * is represented by a permutation index in 8! elements, but it as an array
- * it is converted to the first 8 elements of a 12 elements ep array (with
- * meaningless values for the other 4 elements). */
-
-void ep_int_to_array(int ep, int a[12]);
-void epud_int_to_array(int epud, int a[12]);
-void epfb_int_to_array(int epfb, int a[12]);
-void eprl_int_to_array(int eprl, int a[12]);
-void epose_int_to_array(int epos, int a[12]);
-void eposs_int_to_array(int epos, int a[12]);
-void eposm_int_to_array(int epos, int a[12]);
-void epe_int_to_array(int epe, int a[12]);
-void epm_int_to_array(int epe, int a[12]);
-void eps_int_to_array(int epe, int a[12]);
-void emslices_int_to_array(int emslices, int a[12]);
-void cp_int_to_array(int cp, int a[8]);
-void eo_11bits_to_array(int eo, int a[12]);
-void co_7trits_to_array(int co, int a[8]);
-
-int ep_array_to_int(int ep[12]);
-int epud_array_to_int(int ep[12]);
-int epfb_array_to_int(int ep[12]);
-int eprl_array_to_int(int ep[12]);
-int epose_array_to_int(int ep[12]);
-int eposs_array_to_int(int ep[12]);
-int eposm_array_to_int(int ep[12]);
-int epe_array_to_int(int epe[12]);
-int epm_array_to_int(int epe[12]);
-int eps_array_to_int(int epe[12]);
-int emslices_array_to_int(int ep[12]);
-int cp_array_to_int(int cp[8]);
-int eo_array_to_11bits(int a[12]);
-int co_array_to_7trits(int a[8]);
diff --git a/src/cube.c b/src/cube.c
@@ -0,0 +1,701 @@
+#include "cube.h"
+
+/* Local functions **********************************************************/
+
+static int array_ep_to_epos(int *ep, int *eps_solved);
+static int epos_from_arrays(int *epos, int *ep);
+
+/* Local functions implementation ********************************************/
+
+static int
+array_ep_to_epos(int *ep, int *ss)
+{
+ int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int eps[4];
+ int i, j, is;
+
+ for (i = 0, is = 0; i < 12; i++) {
+ for (j = 0; j < 4; j++) {
+ if (ep[i] == ss[j]) {
+ eps[is++] = j;
+ epos[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ swap(&epos[ss[i]], &epos[i+8]);
+
+ return epos_from_arrays(epos, eps);
+}
+
+static int
+epos_from_arrays(int *epos, int *ep)
+{
+ return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4);
+}
+
+/* Public functions implementation *******************************************/
+
+Cube
+arrays_to_cube(CubeArray *arr, PieceFilter f)
+{
+ Cube ret = {0};
+
+ static int epe_solved[4] = {FR, FL, BL, BR};
+ static int eps_solved[4] = {UL, UR, DL, DR};
+ static int epm_solved[4] = {UF, UB, DF, DB};
+
+ if (f.epose)
+ ret.epose = array_ep_to_epos(arr->ep, epe_solved);
+ if (f.eposs)
+ ret.eposs = array_ep_to_epos(arr->ep, eps_solved);
+ if (f.eposm)
+ ret.eposm = array_ep_to_epos(arr->ep, epm_solved);
+ if (f.eofb)
+ ret.eofb = digit_array_to_int(arr->eofb, 11, 2);
+ if (f.eorl)
+ ret.eorl = digit_array_to_int(arr->eorl, 11, 2);
+ if (f.eoud)
+ ret.eoud = digit_array_to_int(arr->eoud, 11, 2);
+ if (f.cp)
+ ret.cp = perm_to_index(arr->cp, 8);
+ if (f.coud)
+ ret.coud = digit_array_to_int(arr->coud, 7, 3);
+ if (f.corl)
+ ret.corl = digit_array_to_int(arr->corl, 7, 3);
+ if (f.cofb)
+ ret.cofb = digit_array_to_int(arr->cofb, 7, 3);
+ if (f.cpos)
+ ret.cpos = perm_to_index(arr->cpos, 6);
+
+ return ret;
+}
+
+Cube
+compose_filtered(Cube c2, Cube c1, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(c2, f);
+ Cube ret;
+
+ ret = move_via_arrays(arr, c1, f);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+void
+cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f)
+{
+ int i;
+
+ static int epe_solved[4] = {FR, FL, BL, BR};
+ static int eps_solved[4] = {UL, UR, DL, DR};
+ static int epm_solved[4] = {UF, UB, DF, DB};
+
+ if (f.epose || f.eposs || f.eposm)
+ for (i = 0; i < 12; i++)
+ arr->ep[i] = -1;
+
+ if (f.epose)
+ epos_to_partial_ep(cube.epose, arr->ep, epe_solved);
+ if (f.eposs)
+ epos_to_partial_ep(cube.eposs, arr->ep, eps_solved);
+ if (f.eposm)
+ epos_to_partial_ep(cube.eposm, arr->ep, epm_solved);
+ if (f.eofb)
+ int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb);
+ if (f.eorl)
+ int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl);
+ if (f.eoud)
+ int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud);
+ if (f.cp)
+ index_to_perm(cube.cp, 8, arr->cp);
+ if (f.coud)
+ int_to_sum_zero_array(cube.coud, 3, 8, arr->coud);
+ if (f.corl)
+ int_to_sum_zero_array(cube.corl, 3, 8, arr->corl);
+ if (f.cofb)
+ int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb);
+ if (f.cpos)
+ index_to_perm(cube.cpos, 6, arr->cpos);
+}
+
+void
+epos_to_partial_ep(int epos, int *ep, int *ss)
+{
+ int i, is, eposs[12], eps[4];
+
+ index_to_perm(epos % FACTORIAL4, 4, eps);
+ index_to_subset(epos / FACTORIAL4, 12, 4, eposs);
+
+ for (i = 0; i < 4; i++)
+ swap(&eposs[ss[i]], &eposs[i+8]);
+
+ for (i = 0, is = 0; i < 12; i++)
+ if (eposs[i])
+ ep[i] = ss[eps[is++]];
+}
+
+void
+free_cubearray(CubeArray *arr, PieceFilter f)
+{
+ if (f.epose || f.eposs || f.eposm)
+ free(arr->ep);
+ if (f.eofb)
+ free(arr->eofb);
+ if (f.eorl)
+ free(arr->eorl);
+ if (f.eoud)
+ free(arr->eoud);
+ if (f.cp)
+ free(arr->cp);
+ if (f.coud)
+ free(arr->coud);
+ if (f.corl)
+ free(arr->corl);
+ if (f.cofb)
+ free(arr->cofb);
+ if (f.cpos)
+ free(arr->cpos);
+
+ free(arr);
+}
+
+Cube
+move_via_arrays(CubeArray *arr, Cube c, PieceFilter f)
+{
+ CubeArray *arrc = new_cubearray(c, f);
+ Cube ret;
+
+ if (f.epose || f.eposs || f.eposm)
+ apply_permutation(arr->ep, arrc->ep, 12);
+
+ if (f.eofb) {
+ apply_permutation(arr->ep, arrc->eofb, 12);
+ sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2);
+ }
+
+ if (f.eorl) {
+ apply_permutation(arr->ep, arrc->eorl, 12);
+ sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2);
+ }
+
+ if (f.eoud) {
+ apply_permutation(arr->ep, arrc->eoud, 12);
+ sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2);
+ }
+
+ if (f.cp)
+ apply_permutation(arr->cp, arrc->cp, 8);
+
+ if (f.coud) {
+ apply_permutation(arr->cp, arrc->coud, 8);
+ sum_arrays_mod(arr->coud, arrc->coud, 8, 3);
+ }
+
+ if (f.corl) {
+ apply_permutation(arr->cp, arrc->corl, 8);
+ sum_arrays_mod(arr->corl, arrc->corl, 8, 3);
+ }
+
+ if (f.cofb) {
+ apply_permutation(arr->cp, arrc->cofb, 8);
+ sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3);
+ }
+
+ if (f.cpos)
+ apply_permutation(arr->cpos, arrc->cpos, 6);
+
+ ret = arrays_to_cube(arrc, f);
+ free_cubearray(arrc, f);
+
+ return ret;
+}
+
+CubeArray *
+new_cubearray(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = malloc(sizeof(CubeArray));
+
+ if (f.epose || f.eposs || f.eposm)
+ arr->ep = malloc(12 * sizeof(int));
+ if (f.eofb)
+ arr->eofb = malloc(12 * sizeof(int));
+ if (f.eorl)
+ arr->eorl = malloc(12 * sizeof(int));
+ if (f.eoud)
+ arr->eoud = malloc(12 * sizeof(int));
+ if (f.cp)
+ arr->cp = malloc(8 * sizeof(int));
+ if (f.coud)
+ arr->coud = malloc(8 * sizeof(int));
+ if (f.corl)
+ arr->corl = malloc(8 * sizeof(int));
+ if (f.cofb)
+ arr->cofb = malloc(8 * sizeof(int));
+ if (f.cpos)
+ arr->cpos = malloc(6 * sizeof(int));
+
+ cube_to_arrays(cube, arr, f);
+
+ return arr;
+}
+
+Cube
+admissible_ep(Cube cube, PieceFilter f)
+{
+ CubeArray *arr = new_cubearray(cube, f);
+ Cube ret;
+ bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i, j;
+
+ for (i = 0; i < 12; i++)
+ if (arr->ep[i] != -1)
+ used[arr->ep[i]] = true;
+
+ for (i = 0, j = 0; i < 12; i++) {
+ for ( ; j < 11 && used[j]; j++);
+ if (arr->ep[i] == -1)
+ arr->ep[i] = j++;
+ }
+
+ ret = arrays_to_cube(arr, pf_ep);
+ free_cubearray(arr, f);
+
+ return ret;
+}
+
+Cube
+compose(Cube c2, Cube c1)
+{
+ return compose_filtered(c2, c1, pf_all);
+}
+
+int
+edge_slice(Edge e) {
+ if (e < 0 || e > 11)
+ return -1;
+
+ if (e == FR || e == FL || e == BL || e == BR)
+ return 0;
+ if (e == UR || e == UL || e == DR || e == DL)
+ return 1;
+
+ return 2;
+}
+
+bool
+equal(Cube c1, Cube c2)
+{
+ return c1.eofb == c2.eofb &&
+ c1.epose == c2.epose &&
+ c1.eposs == c2.eposs &&
+ c1.eposm == c2.eposm &&
+ c1.coud == c2.coud &&
+ c1.cp == c2.cp &&
+ c1.cpos == c2.cpos;
+}
+
+Cube
+inverse_cube(Cube cube)
+{
+ CubeArray *arr = new_cubearray(cube, pf_all);
+ CubeArray *inv = new_cubearray((Cube){0}, pf_all);
+ Cube ret;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ inv->ep[arr->ep[i]] = i;
+ inv->eofb[arr->ep[i]] = arr->eofb[i];
+ inv->eorl[arr->ep[i]] = arr->eorl[i];
+ inv->eoud[arr->ep[i]] = arr->eoud[i];
+ }
+
+ for (i = 0; i < 8; i++) {
+ inv->cp[arr->cp[i]] = i;
+ inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3;
+ inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3;
+ inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3;
+ }
+
+ for (int i = 0; i < 6; i++)
+ inv->cpos[arr->cpos[i]] = i;
+
+ ret = arrays_to_cube(inv, pf_all);
+ free_cubearray(arr, pf_all);
+ free_cubearray(inv, pf_all);
+
+ return ret;
+}
+
+bool
+is_admissible(Cube cube)
+{
+ /* TODO: this should check consistency of different orientations */
+ /* check also that centers are opposite and admissible */
+
+ CubeArray *a = new_cubearray(cube, pf_all);
+ int parity;
+ bool perm;
+
+ perm = is_perm(a->ep, 12) &&
+ is_perm(a->cp, 8) &&
+ is_perm(a->cpos, 6);
+ parity = perm_sign(a->ep, 12) +
+ perm_sign(a->cp, 8) +
+ perm_sign(a->cpos, 6);
+
+ return perm && parity % 2 == 0;
+}
+
+bool
+is_solved(Cube cube)
+{
+ return equal(cube, (Cube){0});
+}
+
+bool
+is_block_solved(Cube cube, Block block)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (block.edge[i] && !is_solved_edge(cube, i))
+ return false;
+ for (i = 0; i < 8; i++)
+ if (block.corner[i] && !is_solved_corner(cube, i))
+ return false;
+ for (i = 0; i < 6; i++)
+ if (block.center[i] && !is_solved_center(cube, i))
+ return false;
+
+ return true;
+}
+
+bool
+is_solved_center(Cube cube, Center c)
+{
+ return what_center_at(cube, c) == c;
+}
+
+bool
+is_solved_corner(Cube cube, Corner c)
+{
+ return what_corner_at(cube, c) == c &&
+ what_orientation_corner(cube.coud, c);
+}
+
+bool
+is_solved_edge(Cube cube, Edge e)
+{
+ return what_edge_at(cube, e) == e &&
+ what_orientation_edge(cube.eofb, e);
+}
+
+int
+piece_orientation(Cube cube, int piece, char *orientation)
+{
+ int arr[12], n, b, x;
+
+ if (!strcmp(orientation, "eofb")) {
+ x = cube.eofb;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eorl")) {
+ x = cube.eorl;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "eoud")) {
+ x = cube.eoud;
+ n = 12;
+ b = 2;
+ } else if (!strcmp(orientation, "coud")) {
+ x = cube.coud;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "corl")) {
+ x = cube.corl;
+ n = 8;
+ b = 3;
+ } else if (!strcmp(orientation, "cofb")) {
+ x = cube.cofb;
+ n = 8;
+ b = 3;
+ } else {
+ return -1;
+ }
+
+ int_to_sum_zero_array(x, b, n, arr);
+ if (piece < n)
+ return arr[piece];
+
+ return -1;
+}
+
+void
+print_cube(Cube cube)
+{
+ static char edge_string[12][7] = {
+ [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR",
+ [DF] = "DF", [DL] = "DL", [DB] = "DB", [DR] = "DR",
+ [FR] = "FR", [FL] = "FL", [BL] = "BL", [BR] = "BR"
+ };
+
+ static char corner_string[8][7] = {
+ [UFR] = "UFR", [UFL] = "UFL", [UBL] = "UBL", [UBR] = "UBR",
+ [DFR] = "DFR", [DFL] = "DFL", [DBL] = "DBL", [DBR] = "DBR"
+ };
+
+ static char center_string[6][7] = {
+ [U_center] = "U", [D_center] = "D",
+ [R_center] = "R", [L_center] = "L",
+ [F_center] = "F", [B_center] = "B"
+ };
+
+ for (int i = 0; i < 12; i++)
+ printf(" %s ", edge_string[what_edge_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 12; i++)
+ printf(" %d ", what_orientation_edge(cube.eofb, i));
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf("%s ", corner_string[what_corner_at(cube, i)]);
+ printf("\n");
+
+ for (int i = 0; i < 8; i++)
+ printf(" %d ", what_orientation_corner(cube.coud, i));
+ printf("\n");
+
+ for (int i = 0; i < 6; i++)
+ printf(" %s ", center_string[what_center_at(cube, i)]);
+ printf("\n");
+}
+
+Cube
+random_cube()
+{
+ CubeArray *arr = new_cubearray((Cube){0}, pf_4val);
+ Cube ret;
+ int ep, cp, eo, co;
+
+ ep = rand() % FACTORIAL12;
+ cp = rand() % FACTORIAL8;
+ eo = rand() % POW2TO11;
+ co = rand() % POW3TO7;
+
+ index_to_perm(ep, 12, arr->ep);
+ index_to_perm(cp, 8, arr->cp);
+ int_to_sum_zero_array(eo, 2, 12, arr->eofb);
+ int_to_sum_zero_array(co, 3, 8, arr->coud);
+
+ if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8))
+ swap(&(arr->ep[0]), &(arr->ep[1]));
+
+ ret = arrays_to_cube(arr, pf_4val);
+ free_cubearray(arr, pf_4val);
+
+ return ret;
+}
+
+Center
+what_center_at(Cube cube, Center c)
+{
+ static bool initialized = false;
+ static Center aux[FACTORIAL6][6];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++)
+ aux[ui][i] = arr->cpos[i];
+ free_cubearray(arr, pf_cpos);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cpos][c];
+}
+
+Corner
+what_corner_at(Cube cube, Corner c)
+{
+ static bool initialized = false;
+ static Corner aux[FACTORIAL8][8];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++)
+ aux[ui][i] = arr->cp[i];
+ free_cubearray(arr, pf_cp);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cp][c];
+}
+
+Edge
+what_edge_at(Cube cube, Edge e)
+{
+ Edge ret;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ ret = arr->ep[e];
+
+ free_cubearray(arr, pf_ep);
+ return ret;
+}
+
+int
+what_orientation_corner(int co, Corner c)
+{
+ static bool initialized = false;
+ static int auxlast[POW3TO7];
+ static int auxarr[8];
+ static unsigned int ui;
+
+ if (!initialized) {
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, auxarr);
+ auxlast[ui] = auxarr[7];
+ }
+
+ initialized = true;
+ }
+
+ if (c < 7)
+ return (co / powint(3, c)) % 3;
+ else
+ return auxlast[co];
+}
+
+int
+what_orientation_edge(int eo, Edge e)
+{
+ static bool initialized = false;
+ static int auxlast[POW2TO11];
+ static int auxarr[12];
+ static unsigned int ui;
+
+ if (!initialized) {
+ for (ui = 0; ui < POW2TO11; ui++) {
+ int_to_sum_zero_array(ui, 2, 12, auxarr);
+ auxlast[ui] = auxarr[11];
+ }
+
+ initialized = true;
+ }
+
+ if (e < 11)
+ return (eo & (1 << e)) ? 1 : 0;
+ else
+ return auxlast[eo];
+}
+
+Center
+where_is_center(Cube cube, Center c)
+{
+ static bool initialized = false;
+ static Center aux[FACTORIAL6][6];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ arr = new_cubearray((Cube){.cpos = ui}, pf_cpos);
+ for (i = 0; i < 6; i++)
+ aux[ui][arr->cpos[i]] = i;
+ free_cubearray(arr, pf_cpos);
+ }
+
+ initialized = true;
+ }
+
+ return aux[cube.cpos][c];
+}
+
+Corner
+where_is_corner(Cube cube, Corner c)
+{
+ static bool initialized = false;
+ static Corner aux[FACTORIAL8][8];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.cp = ui}, pf_cp);
+ for (i = 0; i < 8; i++)
+ aux[ui][arr->cp[i]] = i;
+ free_cubearray(arr, pf_cp);
+ }
+
+ initialized = true;
+ }
+ return aux[cube.cp][c];
+}
+
+Edge
+where_is_edge(Cube cube, Edge e)
+{
+ /* TODO: when I wrote this code I forgot to add the final
+ part, and now I can't remember how it was supposed to
+ work (i.e. how to recover the location of the edge
+ from these tables. I think it is either very easy or
+ wrong, in any case it is not a priority now.
+ Future Seba can deal with it.
+
+ static bool initialized = false;
+ static Edge aux[3][FACTORIAL12/FACTORIAL8][12];
+ static int i;
+ static unsigned int ui;
+ static CubeArray *arr;
+
+ if (!initialized) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ arr = new_cubearray((Cube){.epose = ui}, pf_e);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 0)
+ aux[0][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_e);
+
+ arr = new_cubearray((Cube){.eposs = ui}, pf_s);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 1)
+ aux[1][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_s);
+
+ arr = new_cubearray((Cube){.eposm = ui}, pf_m);
+ for (i = 0; i < 12; i++)
+ if (edge_slice(arr->ep[i]) == 2)
+ aux[2][ui][arr->ep[i]] = i;
+ free_cubearray(arr, pf_m);
+ }
+
+ initialized = true;
+ }
+ */
+
+ int i;
+ CubeArray *arr = new_cubearray(cube, pf_ep);
+
+ for (i = 0; i < 12; i++)
+ if ((Edge)arr->ep[i] == e)
+ return i;
+
+ return -1;
+}
diff --git a/src/cube.h b/src/cube.h
@@ -0,0 +1,40 @@
+#ifndef CUBE_H
+#define CUBE_H
+
+#include <stdio.h>
+#include <time.h>
+
+#include "pf.h"
+#include "utils.h"
+
+Cube admissible_ep(Cube cube, PieceFilter f);
+Cube arrays_to_cube(CubeArray *arr, PieceFilter f);
+Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */
+Cube compose_filtered(Cube c2, Cube c1, PieceFilter f);
+void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f);
+int edge_slice(Edge e); /* E=0, S=1, M=2 */
+bool equal(Cube c1, Cube c2);
+Cube inverse_cube(Cube cube);
+bool is_admissible(Cube cube);
+bool is_solved(Cube cube);
+bool is_block_solved(Cube cube, Block);
+bool is_solved_center(Cube cube, Center c);
+bool is_solved_corner(Cube cube, Corner c);
+bool is_solved_edge(Cube cube, Edge e);
+void epos_to_partial_ep(int epos, int *ep, int *ss);
+void free_cubearray(CubeArray *arr, PieceFilter f);
+Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf);
+CubeArray * new_cubearray(Cube cube, PieceFilter f);
+void print_cube(Cube cube);
+Cube random_cube();
+Center what_center_at(Cube cube, Center c);
+Corner what_corner_at(Cube cube, Corner c);
+Edge what_edge_at(Cube cube, Edge e);
+int what_orientation_corner(int co, Corner c);
+int what_orientation_edge(int eo, Edge e);
+Center where_is_center(Cube cube, Center c);
+Corner where_is_corner(Cube cube, Corner c);
+Edge where_is_edge(Cube cube, Edge e);
+
+#endif
+
diff --git a/src/cubetypes.h b/src/cubetypes.h
@@ -0,0 +1,286 @@
+#ifndef CUBETYPES_H
+#define CUBETYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define NMOVES 55 /* Actually 55, but one is NULLMOVE */
+#define NTRANS 48
+#define NROTATIONS 24
+
+/* Enums *********************************************************************/
+
+typedef enum
+center
+{
+ U_center, D_center,
+ R_center, L_center,
+ F_center, B_center
+} Center;
+
+typedef enum
+corner
+{
+ UFR, UFL, UBL, UBR,
+ DFR, DFL, DBL, DBR
+} Corner;
+
+typedef enum
+edge
+{
+ UF, UL, UB, UR,
+ DF, DL, DB, DR,
+ FR, FL, BL, BR
+} Edge;
+
+typedef enum
+move
+{
+ NULLMOVE,
+ U, U2, U3, D, D2, D3,
+ R, R2, R3, L, L2, L3,
+ F, F2, F3, B, B2, B3,
+ Uw, Uw2, Uw3, Dw, Dw2, Dw3,
+ Rw, Rw2, Rw3, Lw, Lw2, Lw3,
+ Fw, Fw2, Fw3, Bw, Bw2, Bw3,
+ M, M2, M3,
+ S, S2, S3,
+ E, E2, E3,
+ x, x2, x3,
+ y, y2, y3,
+ z, z2, z3,
+} Move;
+
+typedef enum
+trans
+{
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ rf, rd, rb, ru,
+ lf, ld, lb, lu,
+ fu, fr, fd, fl,
+ bu, br, bd, bl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+ rf_mirror, rd_mirror, rb_mirror, ru_mirror,
+ lf_mirror, ld_mirror, lb_mirror, lu_mirror,
+ fu_mirror, fr_mirror, fd_mirror, fl_mirror,
+ bu_mirror, br_mirror, bd_mirror, bl_mirror,
+} Trans;
+
+
+/* Typedefs ******************************************************************/
+
+typedef struct alg Alg;
+typedef struct alglist AlgList;
+typedef struct alglistnode AlgListNode;
+typedef struct block Block;
+typedef struct command Command;
+typedef struct commandargs CommandArgs;
+typedef struct coordinate Coordinate;
+typedef struct cube Cube;
+typedef struct cubearray CubeArray;
+typedef struct cubetarget CubeTarget;
+typedef struct dfsdata DfsData;
+typedef struct piecefilter PieceFilter;
+typedef struct prunedata PruneData;
+typedef struct solveoptions SolveOptions;
+typedef struct step Step;
+typedef struct symdata SymData;
+
+typedef Cube (*AntiIndexer) (uint64_t);
+typedef bool (*Checker) (Cube);
+typedef int (*Estimator) (CubeTarget);
+typedef bool (*Validator) (Alg *);
+typedef void (*Exec) (CommandArgs *);
+typedef uint64_t (*Indexer) (Cube);
+typedef bool (*Moveset) (Move);
+typedef CommandArgs * (*ArgParser) (int, char **);
+typedef Trans (*TransDetector) (Cube);
+
+
+/* Structs *******************************************************************/
+
+struct
+alg
+{
+ Move * move;
+ bool * inv;
+ int len;
+ int allocated;
+};
+
+struct
+alglist
+{
+ AlgListNode * first;
+ AlgListNode * last;
+ int len;
+};
+
+struct
+alglistnode
+{
+ Alg * alg;
+ AlgListNode * next;
+};
+
+struct
+block
+{
+ bool edge[12];
+ bool corner[8];
+ bool center[6];
+};
+
+struct
+command
+{
+ char * name;
+ char * usage;
+ char * description;
+ ArgParser parse_args;
+ Exec exec;
+};
+
+struct
+commandargs
+{
+ bool success;
+ Alg * scramble;
+ SolveOptions * opts;
+ Step * step;
+ Command * command; /* For help */
+};
+
+struct
+coordinate
+{
+ Indexer index;
+ AntiIndexer cube;
+ uint64_t max;
+ int ntrans;
+ Trans * trans;
+};
+
+struct
+cube
+{
+ int epose;
+ int eposs;
+ int eposm;
+ int eofb;
+ int eorl;
+ int eoud;
+ int cp;
+ int coud;
+ int cofb;
+ int corl;
+ int cpos;
+};
+
+struct
+cubearray
+{
+ int * ep;
+ int * eofb;
+ int * eorl;
+ int * eoud;
+ int * cp;
+ int * coud;
+ int * corl;
+ int * cofb;
+ int * cpos;
+};
+
+struct
+cubetarget
+{
+ Cube cube;
+ int target;
+};
+
+struct
+dfsdata
+{
+ int d;
+ int m;
+ int lb;
+ bool niss;
+ Move last1;
+ Move last2;
+ AlgList * sols;
+ Alg * current_alg;
+ Move sorted_moves[NMOVES];
+ int move_position[NMOVES];
+};
+
+struct
+piecefilter
+{
+ bool epose;
+ bool eposs;
+ bool eposm;
+ bool eofb;
+ bool eorl;
+ bool eoud;
+ bool cp;
+ bool coud;
+ bool cofb;
+ bool corl;
+ bool cpos;
+};
+
+struct
+prunedata
+{
+ char * filename;
+ uint8_t * ptable;
+ bool generated;
+ uint64_t n;
+ Coordinate * coord;
+ Moveset moveset;
+};
+
+struct
+solveoptions
+{
+ int min_moves;
+ int max_moves;
+ int max_solutions;
+ bool optimal_only;
+ bool can_niss;
+ bool verbose;
+ bool all;
+ bool print_number;
+};
+
+struct
+step
+{
+ char * shortname;
+ char * name;
+ Estimator estimate;
+ Checker ready;
+ char * ready_msg;
+ Validator is_valid;
+ Moveset moveset;
+ Trans pre_trans;
+ TransDetector detect;
+};
+
+struct
+symdata
+{
+ char * filename;
+ bool generated;
+ Coordinate * coord;
+ Coordinate * sym_coord;
+ int ntrans;
+ Trans * trans;
+ uint64_t * class;
+ Cube * rep;
+ Trans * transtorep;
+};
+
+#endif
diff --git a/src/env.c b/src/env.c
@@ -0,0 +1,45 @@
+#include "env.h"
+
+bool initialized_env = false;
+char *tabledir;
+
+void
+init_env()
+{
+ char *nissydata = getenv("NISSYDATA");
+ char *localdata = getenv("XDG_DATA_HOME");
+ char *home = getenv("HOME");
+ bool read, write;
+
+ if (initialized_env)
+ return;
+
+ if (nissydata != NULL) {
+ tabledir = malloc(strlen(nissydata) * sizeof(char) + 20);
+ strcpy(tabledir, nissydata);
+ } else if (localdata != NULL) {
+ tabledir = malloc(strlen(localdata) * sizeof(char) + 20);
+ strcpy(tabledir, localdata);
+ strcat(tabledir, "/nissy");
+ } else if (home != NULL) {
+ tabledir = malloc(strlen(home) * sizeof(char) + 20);
+ strcpy(tabledir, home);
+ strcat(tabledir, "/.nissy");
+ }
+
+ mkdir(tabledir, 0777);
+ strcat(tabledir, "/tables");
+ mkdir(tabledir, 0777);
+
+ read = !access(tabledir, R_OK);
+ write = !access(tabledir, W_OK);
+
+ if (!read) {
+ fprintf(stderr, "Table files cannot be read.\n");
+ } else if (!write) {
+ fprintf(stderr, "Data directory not writable: ");
+ fprintf(stderr, "tables can be loaded, but not saved.\n");
+ }
+
+ initialized_env = true;
+}
diff --git a/src/env.h b/src/env.h
@@ -0,0 +1,15 @@
+#ifndef ENV_H
+#define ENV_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+extern char *tabledir;
+
+void init_env();
+
+#endif
diff --git a/src/helppages.h b/src/helppages.h
@@ -1,689 +0,0 @@
-/* To generate this help page, use the script makedoc.sh */
-
-int Npages = 22;
-
-char *helppages[][10] = {
-
-{ "add",
-"\
-\n\
-HELP PAGE FOR COMMAND add\n\
-\n\
-SYNTAX\n\
-add [MOVES|$ID1|@ID1] $ID2\n\
-\n\
-DESCRIPTION\n\
-Appends either MOVES, the scramble memorized under $ID1 or the output sequence\n\
-memorized under @ID1 at the end of the scramble memorized under $ID2. If none\n\
-of MOVES, $ID1 or @ID1 is specified, the user will be asked to type the moves.\n\
-Menmonic: \"add x to y\" or just \"add to y\".\n\
-\n\
-EXAMPLES\n\
-add $1\n\
- The user is required to type the moves that will be appended to $1.\n\
-add F R B $1\n\
- Appends the moves F R B to scramble $1. Now scramble $1 ends with F R B.\n\
-\n\
-"
-},
-
-{ "change",
-"\
-\n\
-HELP PAGE FOR COMMAND change\n\
-\n\
-SYNTAX\n\
-change $ID1 [MOVES|$ID2|@ID2]\n\
-\n\
-DESCRIPTION\n\
-Changes the scramble $ID1 to either MOVES, the scramble $ID2, the output @ID2\n\
-or, if none is specified, the moves entered by the user. The scramble that was\n\
-memorized under $ID1 is then lost.\n\
-Mnemonic: \"change x to y\", or just \"change x\".\n\
-\n\
-EXAMPLES\n\
-change $1\n\
- The user is required to type the moves that will replace $1.\n\
-change $2 $3\n\
- Saves the scramble that was saved under $3 in $2. Now $2 and $3 are the same\n\
- scrambles, and the old $2 is lost.\n\
-change $1 U R\n\
- Saves U R as scramble $1.\n\
-\n\
-"
-},
-
-{ "clear",
-"\
-\n\
-HELP PAGE FOR COMMAND clear\n\
-\n\
-SYNTAX\n\
-clear\n\
-\n\
-DESCRIPTION\n\
-Resets all saved scrambles and output sequences.\n\
-\n\
-"
-},
-
-{ "co",
-"\
-\n\
-HELP PAGE FOR COMMAND co\n\
-\n\
-SYNTAX\n\
-co [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Solves CO for a given scramble. A scramble can be given as last argument of the\n\
-command, or an ID of a saved scramble can be provided. If none of the two is\n\
-given, a prompt will ask the user to input a new scramble.\n\
-\n\
-OPTIONS\n\
-axis={fb,rl,ud} Specify the axis for the CO. One to three axes can be given,\n\
- comma separated, no spaces.\n\
- Default: CO on any of the three axis (omitting the option is\n\
- the same as specifying axis=fb,rl,ud).\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-h Show hidden COs.\n\
- Default, if an CO ending in e.g. F is shown, the equivalent\n\
- one ending in F' is hidden.\n\
-i Ignore centers. By default the CO is aligned with centers.\n\
-niss Use NISS.\n\
- Default: does not use NISS.\n\
-n=N Specify a maximum number of COs to be output. N must be a\n\
- number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-co axis=fb $1\n\
- Finds one optimal CO on fb for the first saved scramble.\n\
-\n\
-co n=5 b=4 U R F \n\
- Finds up to 5 COs of length at most 4 for scramble U R F.\n\
-\n\
-co n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\
- Finds up to 100 COs of lenth at most 4, possibly using NISS, including\n\
- \"hidden\" COs, excluding the rl axis.\n\
-\n\
-"
-},
-
-{ "dr",
-"\
-\n\
-HELP PAGE FOR COMMAND dr\n\
-\n\
-SYNTAX\n\
-dr [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Solves DR for a given scramble. A scramble can be given as last argument of the\n\
-command, or an ID of a saved scramble can be provided. If none of the two is\n\
-given, a prompt will ask the user to input a new scramble.\n\
-If the option \"from\" is given (see below), it solves DR from an EO (if edges\n\
-are oriented) without breaking that EO.\n\
-The first time this command is called without the option from (and, to some\n\
-extent, also the first time it is called with the option from), nissy loads\n\
-some pruning tables that were not loaded on startup, causing a small but\n\
-noticeable delay.\n\
-\n\
-OPTIONS\n\
-axis={fb,rl,ud} Specify the axis for the DR. One to three axes can be given,\n\
- comma separated, no spaces.\n\
- Default: DR on any of the three axis (omitting the option is\n\
- the same as specifying axis=fb,rl,ud).\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-h Show hidden DRs.\n\
- Default, if an DR ending in e.g. R is shown, the equivalent\n\
- one ending in R' is hidden.\n\
-from {fb|rl|ud} Solve DR from the specified EO, which must be solved,\n\
- without breaking the EO.\n\
-niss Use NISS. It works only if solving DR from EO.\n\
- Default: does not use NISS.\n\
-n=N Specify a maximum number of EOs to be output. N must be a\n\
- number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-dr from rl axis=ud $1\n\
- Finds optimal DR on ud, starting from EO on rl, for the first saved scramble.\n\
-\n\
-dr niss from fb n=10 m=6 F2 R L B' F D U' R2 L' F D B\n\
- Finds up to 10 DRs of length at most 6 from EO on fb, possibly using NISS.\n\
-\n\
-dr n=100 axis=ud h\n\
- Finds 100 DRs on ud, including \"hidden\" DRs.\n\
-\n\
-"
-},
-
-{ "drcorners",
-"\
-\n\
-HELP PAGE FOR COMMAND drcorners\n\
-\n\
-SYNTAX\n\
-drcorners [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Similar to drfinish, but only solves corners. CO must be solved. The scramble\n\
-can be given as the last argument of the command, or it may be given as an $ID\n\
-or @ID, or it can be typed out on the following line.\n\
-\n\
-OPTIONS\n\
-from {ud|fb|rl} Allows to specify on which axis the CO is solved. It is\n\
- usually not necessary, since nissy will find a CO on any\n\
- axis.\n\
-i Ignores E-layer centers. By default cornersare solved\n\
- relatively to centers; this options allows for solutions\n\
- which solve corners relatively to each other and to the\n\
- U and D sides, but not to the E layer (or any equivalent\n\
- if the CO is not on U/D).\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-n=N Specify a maximum number of solutions to be output. N must\n\
- be a number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-drcorners n=3 R' D R2 D' R' U2 R D R' U2 R' D' R\n\
- Produces the following output:\n\
-Found 3 results.\n\
-@1: U' F2 U R2 U2 F2 U F2 U R2 (10)\n\
-@2: U' F2 U R2 U2 B2 U R2 D R2 (10)\n\
-@3: U' F2 U R2 U2 B2 U L2 U L2 (10)\n\
-\n\
-"
-},
-
-{ "drfinish",
-"\
-\n\
-HELP PAGE FOR COMMAND drfinish\n\
-\n\
-SYNTAX\n\
-drfinish [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Solves the given scramble using the DR moveset. DR must be solved. The scramble\n\
-can be given as the last argument of the command, or it may be given as an $ID\n\
-or @ID, or it can be typed out on the following line.\n\
-\n\
-OPTIONS\n\
-from {ud|fb|rl} Allows to specify on which axis the DR is solved. It is\n\
- usually not necessary, since nissy will find a DR on any\n\
- axis, but it can be useful if one want to e.g. solve an HTR\n\
- state allowing quarter-turns from a specific DR.\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-n=N Specify a maximum number of solutions to be output. N must\n\
- be a number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-dr from ud R L' U2 R' L F2\n\
- Solves the given scramble using the moveset <U,D,R2,L2,F2,B2>. In this case:\n\
-@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\
-drfinish b=7 n=10 $1\n\
- Finds (at most) 10 solutions of length at most 7 for the scramble $1.\n\
-\n\
-"
-},
-
-{ "eo",
-"\
-\n\
-HELP PAGE FOR COMMAND eo\n\
-\n\
-SYNTAX\n\
-eo [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Solves EO for a given scramble. A scramble can be given as last argument of the\n\
-command, or an ID of a saved scramble can be provided. If none of the two is\n\
-given, a prompt will ask the user to input a new scramble.\n\
-\n\
-OPTIONS\n\
-axis={fb,rl,ud} Specify the axis for the EO. One to three axes can be given,\n\
- comma separated, no spaces.\n\
- Default: EO on any of the three axis (omitting the option is\n\
- the same as specifying axis=fb,rl,ud).\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-h Show hidden EOs.\n\
- Default, if an EO ending in e.g. F is shown, the equivalent\n\
- one ending in F' is hidden.\n\
-niss Use NISS.\n\
- Default: does not use NISS.\n\
-n=N Specify a maximum number of EOs to be output. N must be a\n\
- number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-eo axis=fb $1\n\
- Finds one optimal EO on fb for the first saved scramble.\n\
-\n\
-eo n=5 b=4 U R F \n\
- Finds up to 5 EOs of length at most 4 for scramble U R F.\n\
-\n\
-eo n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\
- Finds up to 100 EOs of lenth at most 4, possibly using NISS, including\n\
- \"hidden\" EOs, excluding the rl axis.\n\
-\n\
-"
-},
-
-{ "exit",
-"\
-\n\
-HELP PAGE FOR COMMAND exit\n\
-\n\
-SYNTAX\n\
-exit\n\
-\n\
-DESCRIPTION\n\
-Exits nissy.\n\
-\n\
-"
-},
-
-{ "help",
-"\
-\n\
-HELP PAGE FOR COMMAND help\n\
-\n\
-SYNTAX\n\
-help [nissy|COMMAND]\n\
-\n\
-DESCRIPTION\n\
-'help nissy' prints a general user manual. 'help COMMAND' prints a detailed\n\
-help page for the command COMMAND, if it exists. 'help' prints a list of all\n\
-available commands a short description for each.\n\
-\n\
-EXAMPLES\n\
-help help\n\
- Prints this help page.\n\
-\n\
-"
-},
-
-{ "htr",
-"\
-\n\
-HELP PAGE FOR COMMAND htr\n\
-\n\
-SYNTAX\n\
-htr [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Finds HTR for a given scramble. DR must be solved. A scramble can be given as\n\
-last argument of the command, or an ID of a saved scramble can be provided. If\n\
-none of the two is given, a prompt will ask the user to input a new scramble.\n\
-\n\
-OPTIONS\n\
-from {ud|fb|rl} Allows to specify on which axis the DR is. This is usually\n\
- not needed, since nissy will automatically find it out.\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-h Show hidden HTRs.\n\
- Default, if an HTR ending in e.g. R is shown, the equivalent\n\
- one ending in R' is hidden.\n\
-niss Use NISS.\n\
- Default: does not use NISS.\n\
-n=N Specify a maximum number of HTRs to be output. N must be a\n\
- number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-eo n=10 b=7 niss $1\n\
- Finds up to 100 HTRs of lenth at most 7, possibly using NISS, including\n\
- \"hidden\" HTRs, for scramble $1. DR must be solved.\n\
-\n\
-"
-},
-
-{ "htrfinish",
-"\
-\n\
-HELP PAGE FOR COMMAND htrfinish\n\
-\n\
-SYNTAX\n\
-htrfinish [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Similar to drfinish, but uses the moveset <U2,D2,R2,L2,F2,B2>. HTR must be\n\
-solved. The scramble can be given as the last argument of the command, or it\n\
-may be given as an $ID or @ID, or it can be typed out on the following line.\n\
-\n\
-OPTIONS\n\
-b=N Specify a bound for the number of moves. N must be a number.\n\
- Default value: 20.\n\
-n=N Specify a maximum number ofsolutions to be output. N must be\n\
- a number.\n\
- Default value: 1.\n\
-\n\
-EXAMPLES\n\
-htrfinish R L' U2 R' L F2\n\
- Produces the following solution:\n\
-@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\
-\n\
-"
-},
-
-{ "invert",
-"\
-\n\
-HELP PAGE FOR COMMAND invert\n\
-\n\
-SYNTAX\n\
-invert [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Inverts a sequence of moves, which can be given also as $ID or @ID. The given\n\
-sequence must not use NISS (if it does, use the command unniss first).\n\
-\n\
-EXAMPLES\n\
-invert F R D'\n\
- Prints D R' F'\n\
-\n\
-"
-},
-
-{ "nissy",
-"\
-\n\
-*******************************************************************************\n\
-********************* NISSY: a cube solver and FMC helper *********************\n\
-*******************************************************************************\n\
-\n\
-If you just want to solve the cube, type 'solve' followed by the scramble. This\n\
-will not always give you an optimal solution, unless it is 10 moves or less or\n\
-you use the \"o\" option. Finding the optimal solution might take very long if it\n\
-is 16 moves or more, especially for the first time.\n\
-\n\
-Now the fun stuff. With nissy you can save and manipulate move sequences, for\n\
-example:\n\
-\n\
-nissy-# save R' U' F\n\
-$1: R' U' F\n\
-nissy-# add L2D' $1\n\
-$1: R' U' F L2 D'\n\
-\n\
-You can then ask nissy to solve certain substepson a saved scramble:\n\
-\n\
-nissy-# eo axis=rl $1\n\
-@1: U D F' R (4)\n\
-\n\
-And of course it uses also NISS, if you ask:\n\
-\n\
-nissy-# eo niss axis=rl $1\n\
-@1: (R) (1)\n\
-\n\
-Notice that the sequences you save are marked with a $, while the \"output\"\n\
-sequences are marked with @. The difference between these two type of sequences\n\
-is that those marked with @ are temporary and get lost once you get new output.\n\
-Most commands accept as input either a move sequence typed out, a $-sequence or\n\
-a @-sequence. For example, you can however save a @-sequence and make it\n\
-persistent:\n\
-\n\
-nissy-# save @1\n\
-$2: (R)\n\
-\n\
-Nissy also understands NISS. Let's see a more complicated example where you\n\
-save a scramble, ask for some EOs (using NISS) and then a DR on inverse:\n\
-\n\
-nissy-# save R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F\n\
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\
-nissy-# eo n=10 niss axis=fb,rl $3\n\
-Found 10 results.\n\
-@1: (U' L B D F) (5)\n\
-@2: (U' L B' D F) (5)\n\
-@3: (L B U D F) (5)\n\
-@4: (L B' U D F) (5)\n\
-@5: R U B U L (5)\n\
-@6: R U' L (B L) (5)\n\
-@7: R U' B U L (5)\n\
-@8: R L (L B L) (5)\n\
-@9: R (U2 D' F R) (5)\n\
-@10: R (U2 F D' R) (5)\n\
-nissy-# add @6 $3\n\
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\
-nissy-# unniss $3\n\
-@1: L' B' R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L \n\
-nissy-# invert @1\n\
-@1: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\
-nissy-# save @1\n\
-$5: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\
-nissy-# dr from rl $5\n\
-@1: F2 U D2 F' B D B (7)\n\
-nissy-# \n\
-\n\
-If you ask nissy to solve a substep (or the whole cube) using a sequence with\n\
-NISS as scramble, it will first un-NISS it (but without saving the unNISSed\n\
-scramble anywhere):\n\
-\n\
-print $3\n\
-$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\
-nissy-# solve $3\n\
-@1: F U' R2 F2 U2 F U2 R2 L2 D R2 U B2 D' L2 D B2 D2 (18)\n\
-\n\
-Nissy knows how to solve certain common sub-steps for DR (or Thistlethwaite /\n\
-Kociemba algorithms). For now it does know more common speedsolving methods.\n\
-\n\
-For a full list of commands type \"help\". For a more detailed help on a specific\n\
-command, type \"help (command)\". The help pages can also be found in the docs\n\
-folder.\n\
-\n\
-If you want to report a bug (I'm sure there are many!) or give a suggestion,\n\
-you can send an email to sebastiano.tronto@gmail.com.\n\
-\n\
-Have fun!\n\
-\n\
-"
-},
-
-{ "pic",
-"\
-\n\
-HELP PAGE FOR COMMAND pic\n\
-\n\
-SYNTAX\n\
-pic [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Prints the cube state after applying the given scramble.\n\
-\n\
-EXAMPLES\n\
-pic R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\
- Gives the following output:\n\
- UF UL UB UR DF DL DB DR FR FL BL BR \n\
-EP: FR UL FL UR UF DB DR BL UB BR DL DF \n\
-EO(F/B): x x x x x x x x \n\
-\n\
- UFR UFL UBL UBR DFR DFL DBL DBR \n\
-CP: UBR UFR DFL DBL UFL UBL DBR DFR \n\
-CO(U/D): ccw cw ccw cw\n\
-\n\
-"
-},
-
-{ "print",
-"\
-\n\
-HELP PAGE FOR COMMAND print\n\
-\n\
-SYNTAX\n\
-print [$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Prints memorized sequences. If no argument is given, it prints all memorized\n\
-scrambles ($ only). If $ID or @ID is specified, it only prints the relative\n\
-memorized sequence.\n\
-\n\
-EXAMPLES\n\
-print\n\
- Prints a list of all memorized scrambles (only $).\n\
-print $2\n\
- Prints the second memorized scramble.\n\
-print @13\n\
- Prints the 13th sequence that was part of the output of the last command.\n\
-\n\
-"
-},
-
-{ "quit",
-"\
-\n\
-HELP PAGE FOR COMMAND quit\n\
-\n\
-SYNTAX\n\
-quit\n\
-\n\
-DESCRIPTION\n\
-Exits nissy.\n\
-\n\
-"
-},
-
-{ "replace",
-"\
-\n\
-HELP PAGE FOR COMMAND replace\n\
-\n\
-SYNTAX\n\
-eo [OPTIONS] [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Looks for non-optimal subsequences and replaces them to shorten the given\n\
-sequence. By default it tries to shorten every subsequence of up to 10 moves,\n\
-but this can be change with the \"b\" option.\n\
-It outputs at most 10 equivalent optimal sequences for each replaceable part.\n\
-\n\
-OPTIONS\n\
-b=N Finds non-optimal subsequences of up to N moves.\n\
-\n\
-EXAMPLES\n\
-replace D2 F' D2 U2 F' L2 R2 U' D B2 D B2 U B2 F L2 R' F' D U' \n\
- Produces the following output:\n\
-Replace [ R2 U' D B2 D B2 ] (moves 7-12) with: [ D R2 D U' ] (-6+4)\n\
-Replace [ R2 U' D B2 D B2 U ] (moves 7-13) with: [ D R2 D ] (-7+3)\n\
-Replace [ U' D B2 D B2 U ] (moves 8-13) with: [ R2 D R2 D ] (-6+4)\n\
-\n\
-"
-},
-
-{ "save",
-"\
-\n\
-HELP PAGE FOR COMMAND save\n\
-\n\
-SYNTAX\n\
-save [MOVES|@ID|$ID]\n\
-\n\
-DESCRIPTION\n\
-Memorizes the scramble specified by MOVES, given as input or temporarily saved\n\
-as @ID, where ID is a number ('help nissy' for for more on IDs). If an $ID is\n\
-given, it makes a copy of the scramble. An identifier of the form $ID, where ID\n\
-is a number, is assigned to the memorized scramble.\n\
-\n\
-EXAMPLES\n\
-save R U R' U'\n\
- Saves the scramble R U R' U'.\n\
-save F (B)\n\
- Saves the scramble F (B) (NISS notation).\n\
-save @3\n\
- Saves the third output sequence of the last command.\n\
-save $2\n\
- Makes a copy of the second saved scramble.\n\
-\n\
-"
-},
-
-{ "scramble",
-"\
-\n\
-HELP PAGE FOR COMMAND scramble\n\
-\n\
-SYNTAX\n\
-scramble [OPTIONS]\n\
-\n\
-DESCRIPTION\n\
-Produces a random-state scramble. There are options to get a corners-only,\n\
-edges-only or dr-state scramble.\n\
-\n\
-OPTIONS\n\
-c Scrambles corners only (edges are solved).\n\
-e Scrambles edges only (corners are solved).\n\
-dr DR-state scramble. The DR is always on the U/D axis.\n\
-\n\
-\n\
-EXAMPLES\n\
-scramble\n\
- Gives a random-state scramble\n\
-scramble dr\n\
- Gives a random-DR-state scramble\n\
-\n\
-"
-},
-
-{ "solve",
-"\
-\n\
-HELP PAGE FOR COMMAND solve\n\
-\n\
-SYNTAX\n\
-solve [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Solves the given scramble, which can be given as a sequence of moves or as $ID\n\
-or @ID. If none is given, the user can type it on the next line.\n\
-The algorithm first tries to find a short (<=10 moves) solution, and then\n\
-switches to a 2-step algorithm (unless the option \"o\" is specified, in which\n\
-case it keeps looking for an optimal solution).\n\
-The first time it uses the 2-step algorithm it needs to load some tables, which\n\
-can take a few seconds. It runs much faster after that. If the option \"o\" is\n\
-specified, the first time it loads some large tables, which can take a minute\n\
-or two.\n\
-\n\
-OPTIONS\n\
-b=N Only looks for solutions up to N moves.\n\
-n=N Tries to find multiple solutions, at most N. Multiple\n\
- Solutions will only be found if they are <=10 moves.\n\
-o Looks for optimal solution.\n\
-\n\
-\n\
-EXAMPLES\n\
-solve R' U' F\n\
- Solves the scramble R' U' F.\n\
-solve o b=14 $1\n\
- Tries to solve the scramble $1 optimally, but stops if no solution of 14\n\
- moves or shorter is found.\n\
-solve n=16 R L' U2 R' L F2\n\
- Finds the 16 shortest solutions for the scramble above.\n\
-\n\
-"
-},
-
-{ "unniss",
-"\
-\n\
-HELP PAGE FOR COMMAND unniss\n\
-\n\
-SYNTAX\n\
-unniss [MOVES|$ID|@ID]\n\
-\n\
-DESCRIPTION\n\
-Removes NISS from a sequence of moves, which can be given also as $ID or @ID.\n\
-A sequence of the form A (B) is translated to B' A.\n\
-\n\
-EXAMPLES\n\
-invert F R (D' L2)\n\
- Prints L2 D F R\n\
-\n\
-"
-},
-};
diff --git a/src/io.c b/src/io.c
@@ -1,232 +0,0 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "coordinates.h"
-#include "moves.h"
-#include "utils.h"
-
-/* Functions for nice output */
-char *edge_string(int i) {
- return (i > -1 && i < 12) ? edge_string_list[i] : "-";
-}
-
-char *corner_string(int i) {
- return (i > -1 && i < 8) ? corner_string_list[i] : "-";
-}
-
-char *move_string(int i) {
- return (i > -1 && i < 19) ? move_string_list[i] : "err";
-}
-
-void print_ep_array(int ep[12]) {
- for (int i = 0; i < 12; i++)
- printf(" %s ", edge_string(ep[i]));
-}
-
-void print_ep_int(int ep) {
- int aux[12];
- ep_int_to_array(ep, aux);
- print_ep_array(aux);
-}
-
-void print_cp_array(int cp[8]) {
- for (int i = 0; i < 8; i++)
- printf(" %s ", corner_string(cp[i]));
-}
-
-void print_cp_int(int cp) {
- int aux[8];
- cp_int_to_array(cp, aux);
- print_cp_array(aux);
-}
-
-void print_eo_array(int eo[12]) {
- for (int i = 0; i < 12; i++) {
- if (eo[i])
- printf(" x ");
- else
- printf(" ");
- }
-}
-
-void print_eo_int(int eo) {
- int aux[12];
- eo_11bits_to_array(eo, aux);
- print_eo_array(aux);
-}
-
-void print_co_array(int co[8]) {
- for (int i = 0; i < 8; i++) {
- if (co[i] == 0)
- printf(" ");
- if (co[i] == 1)
- printf(" cw ");
- if (co[i] == 2)
- printf(" ccw ");
- }
-}
-
-void print_co_int(int co) {
- int aux[8];
- co_7trits_to_array(co, aux);
- print_co_array(aux);
-}
-
-void print_cube_scram(int *scram) {
- int ep = 0, cp = 0, eofb = 0, coud = 0;
- for (int i = 0; scram[i]; i++) {
- ep = apply_move_ep_int(scram[i], ep);
- cp = cp_transition_table[cp][scram[i]];
- eofb = eofb_transition_table[eofb][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
- }
- printf("\t\t"); print_ep_int(0); printf("\n");
- printf("EP:\t\t"); print_ep_int(ep); printf("\n");
- printf("EO(F/B):\t"); print_eo_int(eofb); printf("\n");
- printf("\n");
- printf("\t\t"); print_cp_int(0); printf("\n");
- printf("CP:\t\t"); print_cp_int(cp); printf("\n");
- printf("CO(U/D):\t"); print_co_int(coud); printf("\n");
-}
-
-
-void copy_moves(int *src, int *dst) {
- for (int i = 0; (dst[i] = src[i]); i++);
-}
-
-void append_moves(int *src, int *dst) {
- int n = 0;
- for (; dst[n]; n++);
- copy_moves(src, dst+n);
-}
-
-/* Parse a string and saves the move in a. Supports NISS notation.
- * Returns the number of moves, or -1 in case of error. */
-int read_moves(char *str, int *a) {
- int count = 0;
- int niss = 0;
- for (int i = 0; str[i] && str[i] != '\n'; i++) {
- while (str[i] == ' ' || str[i] == '\t') i++;
- switch (str[i]) {
- case 'U':
- a[count++] = niss ? -U : U;
- break;
- case 'D':
- a[count++] = niss ? -D : D;
- break;
- case 'R':
- a[count++] = niss ? -R : R;
- break;
- case 'L':
- a[count++] = niss ? -L : L;
- break;
- case 'F':
- a[count++] = niss ? -F : F;
- break;
- case 'B':
- a[count++] = niss ? -B : B;
- break;
- case '(':
- if (niss)
- return -1;
- else
- niss = 1;
- break;
- case ')':
- if (!niss)
- return -1;
- else
- niss = 0;
- break;
- default:
- return -1;
- }
- switch (str[++i]) {
- case '2':
- a[count-1] += niss ? -1 : 1;
- break;
- case '\'':
- case '3':
- a[count-1] += niss ? -2 : 2;
- break;
- case '1':
- default:
- --i;
- }
- }
- a[count] = 0;
- return count;
-}
-
-/* Read moves from standard input, after a prompt. */
-int read_moves_from_prompt(int *a) {
- char str[1000];
- printf("Enter moves: ");
- if (fgets(str, 1000, stdin) == NULL)
- return -1;
- return read_moves(str, a);
-}
-
-/* Read moves from a list of token, each containing one or more moves. */
-int read_moves_from_tok(int n, char tok[][100], int *a) {
- char str[1000] = "";
- for (int i = 0; i < n; i++)
- strcat(str, tok[i]);
- return read_moves(str, a);
-}
-
-/* Checks if a sequence of moves uses NISS */
-int uses_niss(int *str) {
- for (int i = 0; str[i]; i++)
- if (str[i] < 0)
- return 1;
- return 0;
-}
-
-/* A (B) -> B' A */
-int unniss(int *src, int *dst) {
- int n = 0;
- for (int i = 0; src[i]; i++)
- if (src[i] < 0)
- n++;
-
- int norm_count = n, inv_count = n-1;
- for (int i = 0; src[i]; i++)
- if (src[i] > 0)
- dst[norm_count++] = src[i];
- else
- dst[inv_count--] = inverse_move[-src[i]];
-
- dst[norm_count] = 0;
-
- return n;
-}
-
-int invert(int *src, int *dst) {
- int aux[255];
- for (int i = 0; (aux[i] = -src[i]); i++);
- return unniss(aux, dst);
-}
-
-int len(int *scram) {
- int m;
- for (m = 0; scram[m]; m++);
- return m;
-}
-
-void print_moves(int moves_list[]) {
- int niss = 0;
- for (int i = 0; moves_list[i]; i++) {
- if (!niss && moves_list[i] < 0) {
- printf("(");
- niss = 1;
- }
- printf("%s", move_string_list[abs(moves_list[i])]);
- if (niss && moves_list[i+1] >= 0) {
- niss = 0;
- printf(")");
- }
- printf(" ");
- }
-}
diff --git a/src/io.h b/src/io.h
@@ -1,21 +0,0 @@
-#include <stdint.h>
-
-char *edge_string(int edge);
-char *corner_string(int edge);
-char *move_string(int move);
-
-void print_cube_scram(int *scram);
-
-void copy_moves(int *src, int *dst);
-void append_moves(int *src, int *dst);
-
-int read_moves(char *str, int *a);
-int read_moves_from_prompt(int *a);
-int read_moves_from_tok(int n, char tok[][100], int *a);
-
-int uses_niss(int *str);
-int unniss(int *src, int *dst);
-int invert(int *src, int *dst);
-int len(int *scram);
-
-void print_moves(int move_list[]);
diff --git a/src/main.c b/src/main.c
@@ -1,937 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include "utils.h"
-#include "coordinates.h"
-#include "io.h"
-#include "moves.h"
-#include "solver.h"
-#include "string.h"
-#include "helppages.h"
-/* Saved sequences of moves */
-int scr_count=1, tmp_count=1, max_tmp=999;
-int scrambles[255][255], tmp[1000][255];
-
-int read_moves_from_variable(char *id, int *dst) {
- char c = id[0];
- if (c != '$' && c != '@')
- return -1;
- int n = atoi(id+1);
- if (n <= 0 || n >= (c == '$' ? scr_count : tmp_count))
- return -1;
- copy_moves(c == '$' ? scrambles[n] : tmp[n], dst);
- return n;
-}
-
-int read_moves_from_argument(int n, char tok[][100], int *dst) {
- int r = read_moves_from_variable(tok[0], dst);
- return (r != -1) ? r : read_moves_from_tok(n, tok, dst);
-}
-
-void print_results(int n, int res[][30]) {
- if (n == -1)
- printf("Pre-conditions not satisfied (or other error).\n");
-
- if (n == 0)
- printf("No result found (try different bounds).\n");
-
- if (n > 1)
- printf("Found %d results.\n", n);
- tmp_count = 1; /* Reset temporary count */
- for (int i = 0; i < n; i++) {
- if (i < max_tmp) {
- copy_moves(res[i], tmp[tmp_count]);
- printf("@%d:\t", tmp_count++);
- } else {
- printf(" \t");
- }
- print_moves(res[i]);
- printf("(%d)\n", len(res[i]));
- }
-}
-
-/* Removes extra white spaces from the input string */
-int parsecmd(char *cmd, char cmdtok[][100]) {
- char *i = cmd, *j = cmd;
- while (*j != '\n' && *j != EOF) {
- *i = *j;
- if (*i == ' ' || *i == '\t')
- *i = ' ';
- ++j;
- if (*i == ' ' || *i == '\t')
- while (*j == ' ' || *j == '\t')
- ++j;
- ++i;
- }
- if (*(i-1) == ' ')
- *(i-1) = 0;
- else
- *i = 0;
-
- int n = 0;
- char *s = strtok(cmd, " ");
- while (s != NULL) {
- strcpy(cmdtok[n++], s);
- s = strtok(NULL, " ");
- }
- return n;
-}
-
-void scramble_cmd(int n, char cmdtok[][100]) {
- int c = 0, e = 0, dr = 0;
-
- if (n > 2 || cmdtok[0][0] != 's') { /* Second case avoids warning */
- printf("scramble: wrong syntax\n");
- return;
- } else if (n == 2) {
- if (!strcmp(cmdtok[1], "c")) {
- c = 1;
- } else if (!strcmp(cmdtok[1], "e")) {
- e = 1;
- } else if (!strcmp(cmdtok[1], "dr")) {
- dr = 1;
- } else {
- printf("scramble: wrong syntax\n");
- return;
- }
- }
-
- int scram[2][30];
-
- srand(time(NULL));
- int eofb = rand() % pow2to11;
- int coud = rand() % pow3to7;
- int ep = rand() % factorial12;
- int cp = rand() % factorial8;
-
- if (c) {
- eofb = ep = 0;
- } else if (e) {
- coud = cp = 0;
- } else if (dr) {
- eofb = coud = 0;
- int epud = rand() % factorial8;
- int epe = rand() % factorial4;
- int ep_arr[12];
- epud_int_to_array(epud, ep_arr);
- epe_int_to_array(epe, ep_arr);
- ep = ep_array_to_int(ep_arr);
- while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8))
- cp = (cp+1) % factorial8;
- } else {
- while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8))
- cp = (cp+1) % factorial8;
- }
-
- reach_state(eofb, coud, ep, cp, scram);
- /* Debug */
- /* printf("State: %d %d %d %d\n", eofb, coud, ep, cp); */
- print_results(1, scram);
-}
-
-void save_cmd(int n, char cmdtok[][100]) {
- int scram[255];
- if (n == 1) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("save: error reading moves. Not saved.\n");
- return;
- }
- } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) {
- printf("save: error reading moves or ID. Not saved.\n");
- return;
- }
-
- copy_moves(scram, scrambles[scr_count]);
-
- printf("$%d:\t", scr_count);
- print_moves(scrambles[scr_count]);
- printf("\n");
- scr_count++;
-}
-
-void change_cmd(int n, char cmdtok[][100]) {
- int id, scram[255];
- if (n == 1) {
- printf("change: you must specify an $ID.\n");
- return;
- } else if (cmdtok[1][0] != '$') {
- printf("change: invalid $ID.\n");
- return;
- } else {
- id = atoi(cmdtok[1]+1);
- if (id <= 0 || id >= scr_count) {
- printf("change: invalid $ID.\n");
- return;
- }
- if (n == 2) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("change: error reading moves.\n");
- return;
- }
- } else if (read_moves_from_argument(n-2, cmdtok+2, scram) == -1 ) {
- printf("change: error reading moves or ID.\n");
- return;
- }
- }
-
- copy_moves(scram, scrambles[id]);
-
- printf("$%d:\t", id);
- print_moves(scrambles[id]);
- printf("\n");
-}
-
-void print_cmd(int n, char cmdtok[][100]) {
- if (n == 1) {
- for (int i = 1; i < scr_count; i++) {
- printf("$%d:\t", i);
- print_moves(scrambles[i]);
- printf("\n");
- }
- } else if (n == 2) {
- int i = atoi(cmdtok[1]+1);
- char sign = cmdtok[1][0];
- if (sign != '$' && sign != '@') {
- printf("print: invalid ID (must start with $ or @).\n");
- return;
- }
- if (i > 0 && i < (sign == '$' ? scr_count : tmp_count)) {
- printf("%c%d:\t", sign, i);
- print_moves(sign == '$' ? scrambles[i] : tmp[i]);
- printf("\n");
- } else {
- printf("print: invalid ID.\n");
- return;
- }
- } else {
- printf("print: wrong syntax.\n");
- }
-}
-
-void add_cmd(int n, char cmdtok[][100]) {
- int id, scram[255];
- if (n == 1) {
- printf("add: you must specify a destination $ID.\n");
- return;
- } else if (cmdtok[n-1][0] != '$') {
- printf("add: invalid destination $ID.\n");
- return;
- } else {
- id = atoi(cmdtok[n-1]+1);
- if (id <= 0 || id >= scr_count) {
- printf("add: invalid destination $ID.\n");
- return;
- }
- if (n == 2) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("add: error reading moves.\n");
- return;
- }
- } else {
- if (read_moves_from_argument(n-2, cmdtok+1, scram) == -1) {
- printf("add: error reading moves or ID.\n");
- return;
- }
- }
- }
-
- append_moves(scram, scrambles[id]);
-
- printf("$%d:\t", id);
- print_moves(scrambles[id]);
- printf("\n");
-}
-
-void invert_cmd(int n, char cmdtok[][100]) {
- int scram[255];
- if (n == 1) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("invert: error reading moves.\n");
- return;
- }
- } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) {
- printf("invert: error reading moves or ID.\n");
- return;
- }
-
- if (uses_niss(scram)) {
- printf("invert: cannot invert NISS.\n");
- return;
- }
-
- invert(scram, tmp[1]);
- tmp_count = 2;
-
- printf("@1:\t");
- print_moves(tmp[1]);
- printf("\n");
-}
-
-void unniss_cmd(int n, char cmdtok[][100]) {
- int scram[255];
- if (n == 1) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("unniss: error reading moves.\n");
- return;
- }
- } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) {
- printf("unniss: error reading moves or ID.\n");
- return;
- }
-
- unniss(scram, tmp[1]);
- tmp_count = 2;
-
- printf("@1:\t");
- print_moves(tmp[1]);
- printf("\n");
-}
-
-void pic_cmd(int n, char cmdtok[][100]) {
- int scram[255];
- if (n == 1) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("pic: error reading moves.\n");
- return;
- }
- } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) {
- printf("pic: error reading moves or ID.\n");
- return;
- }
- print_cube_scram(scram);
-}
-
-void solve_cmd(int n, char cmdtok[][100]) {
- int m = 1, b = 25, optimal = 0;
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("solve: bad option b.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("solve: bad option n.\n");
- return;
- }
- } else if (!strcmp(cmdtok[i], "o")) {
- optimal = 1;
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("solve: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("solve: error reading moves.\n");
- return;
- }
- }
-
- /* Call solver and print results */
- unniss(scram, scram_unnissed);
- int sol[m+2][30];
- int s = solve_scram(scram_unnissed, sol, m, b, optimal);
- print_results(s, sol);
-}
-
-void replace_cmd(int n, char cmdtok[][100]) {
- int m = 10; /* max length */
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strncmp(cmdtok[i], "b=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("replace: bad option n.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("replace: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("replace: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
- int l = len(scram_unnissed);
- int aux1[255], aux2[15][30], aux3[30];
- for (int i = 0; i < l; i++) {
- for (int j = 2; j <= m && i + j <= l; j++) {
- copy_moves(scram_unnissed+i, aux1);
- aux1[j] = 0;
- int s = solve_scram(aux1, aux2, 10, j-1, 1);
- for (int k = 0; k < s; k++) {
- invert(aux2[k], aux3);
- /* TODO: the following part should also chek for the case when
- * the last moves are R L or similar. */
- if (aux3[0] != aux1[0] && aux3[len(aux3)-1] != aux1[len(aux1)-1]) {
- printf("Replace [ ");
- print_moves(aux1);
- printf("] (moves %d-%d) with: [ ", i+1, i+j);
- print_moves(aux3);
- printf("] (-%d+%d)\n", j, len(aux3));
- }
- }
- }
- }
-}
-
-void clear_cmd(int n, char cmdtok[][100]) {
- if (n > 1 || cmdtok[0][0] != 'c') { /* Avoid unused variable warning */
- printf("clear: syntax error.\n");
- return;
- }
- scr_count = tmp_count = 1;
-}
-
-void eo_cmd(int n, char cmdtok[][100]) {
-
- /* Default values */
- int m = 1, b = 20;
- int niss = 0, hide = 1;
- int fb = 1, rl = 1, ud = 1;
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strcmp(cmdtok[i], "h")) {
- hide = 0;
- } else if (!strcmp(cmdtok[i], "niss")) {
- niss = 1;
- } else if (!strncmp(cmdtok[i], "axis=", 5)) {
- fb = rl = ud = 0;
- if (strstr(cmdtok[i], "fb") != NULL)
- fb = 1;
- if (strstr(cmdtok[i], "rl") != NULL)
- rl = 1;
- if (strstr(cmdtok[i], "ud") != NULL)
- ud = 1;
- if (fb + rl + ud == 0) {
- printf("eo: bad axis option.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("eo: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("eo: bad option b.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("eo: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("eo: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver and print results */
- int eo_list[m+5][30];
- int neo = eo_scram_spam(scram_unnissed, eo_list, fb, rl, ud, m, b, niss,
- hide);
- print_results(neo, eo_list);
-}
-
-void co_cmd(int n, char cmdtok[][100]) {
-
- /* Default values */
- int m = 1, b = 20, ignore = 0;
- int niss = 0, hide = 1;
- int fb = 1, rl = 1, ud = 1;
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strcmp(cmdtok[i], "h")) {
- hide = 0;
- } else if (!strcmp(cmdtok[i], "niss")) {
- niss = 1;
- } else if (!strcmp(cmdtok[i], "i")) {
- ignore = 1;
- } else if (!strncmp(cmdtok[i], "axis=", 5)) {
- fb = rl = ud = 0;
- if (strstr(cmdtok[i], "fb") != NULL)
- fb = 1;
- if (strstr(cmdtok[i], "rl") != NULL)
- rl = 1;
- if (strstr(cmdtok[i], "ud") != NULL)
- ud = 1;
- if (fb + rl + ud == 0) {
- printf("co: bad axis option.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("co: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("co: bad option b.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("co: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("co: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver and print results */
- int co_list[m+5][30];
- int nco = co_scram_spam(scram_unnissed, co_list, fb, rl, ud, m, b, niss,
- hide, ignore);
- print_results(nco, co_list);
-}
-
-void dr_cmd(int n, char cmdtok[][100]) {
-
- /* Default values */
- int m = 1, b = 20;
- int niss = 0, hide = 1;
- int from = 0; /* 0: direct dr; {1,2,3}: from {eofb,eorl,eoud} */
- int fb = 1, rl = 1, ud = 1;
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strcmp(cmdtok[i], "h")) {
- hide = 0;
- } else if (!strcmp(cmdtok[i], "niss")) {
- niss = 1;
- } else if (!strncmp(cmdtok[i], "axis=", 5)) {
- fb = rl = ud = 0;
- if (strstr(cmdtok[i], "fb") != NULL)
- fb = 1;
- if (strstr(cmdtok[i], "rl") != NULL)
- rl = 1;
- if (strstr(cmdtok[i], "ud") != NULL)
- ud = 1;
- if (fb + rl + ud == 0) {
- printf("dr: bad axis option.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("dr: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("dr: bad option b.\n");
- return;
- }
- } else if (!strcmp(cmdtok[i], "from")) {
- i++;
- char x[3][3] = {"fb", "rl", "ud"};
- for (int j = 0; j < 3; j++)
- if (!strcmp(cmdtok[i], x[j]))
- from = j+1;
- if (!from) {
- printf("dr: bad from option.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("dr: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("dr: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver */
- int dr_list[m+5][30], ndr;
- if (from) {
- ndr = drfrom_scram_spam(scram_unnissed, dr_list, from, fb, rl, ud,
- m, b, niss, hide);
- if (ndr == -1) {
- printf("dr: from given, but EO not found (possibly other error).\n");
- return;
- }
- } else {
- if (niss)
- printf("Warning: not using NISS for direct DR.\n");
- ndr = dr_scram_spam(scram_unnissed, dr_list, fb, rl, ud, m, b, hide);
- }
- print_results(ndr, dr_list);
-}
-
-void htr_cmd(int n, char cmdtok[][100]) {
-
- /* Default values */
- int m = 1, b = 20;
- int niss = 0, hide = 1;
- int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strcmp(cmdtok[i], "h")) {
- hide = 0;
- } else if (!strcmp(cmdtok[i], "niss")) {
- niss = 1;
- } else if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("htr: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("htr: bad option b.\n");
- return;
- }
- } else if (!strcmp(cmdtok[i], "from")) {
- i++;
- char x[3][3] = {"ud", "fb", "rl"};
- for (int j = 0; j < 3; j++)
- if (!strcmp(cmdtok[i], x[j]))
- from = j+1;
- if (!from) {
- printf("htr: bad from option.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("htr: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("htr: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver */
- int htr_list[m+5][30], nhtr;
- nhtr = htr_scram_spam(scram_unnissed, htr_list, from, m, b, niss, hide);
- print_results(nhtr, htr_list);
-}
-
-void drfinish_cmd(int n, char cmdtok[][100]) {
- /* Default values */
- int m = 1, b = 20;
- int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("drfinish: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("drfinish: bad option b.\n");
- return;
- }
- } else if (!strcmp(cmdtok[i], "from")) {
- i++;
- char x[3][3] = {"ud", "fb", "rl"};
- for (int j = 0; j < 3; j++)
- if (!strcmp(cmdtok[i], x[j]))
- from = j+1;
- if (!from) {
- printf("drfinish: bad from option.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("drfinish: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("drfinish: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver */
- int c_list[m+5][30], nc;
- nc = dr_finish_scram_spam(scram_unnissed, c_list, from, m, b);
- print_results(nc, c_list);
-}
-
-void htrfinish_cmd(int n, char cmdtok[][100]) {
- /* Default values */
- int m = 1, b = 20;
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("htrfinish: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("htrfinish: bad option b.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("htrfinish: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("htrfinish: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver */
- int c_list[m+5][30], nc;
- nc = htr_finish_scram_spam(scram_unnissed, c_list, m, b);
- print_results(nc, c_list);
-}
-
-void drcorners_cmd(int n, char cmdtok[][100]) {
- /* Default values */
- int m = 1, b = 20, ignore = 0;
- int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */
- int scram[255] = {[0] = 0};
- int scram_unnissed[255];
-
- /* Parse options */
- for (int i = 1; i < n && scram[0] == 0; i++) {
- if (!strncmp(cmdtok[i], "n=", 2)) {
- m = atoi(cmdtok[i]+2);
- if (m <= 0) {
- printf("drcorners: bad option n.\n");
- return;
- }
- } else if (!strncmp(cmdtok[i], "b=", 2)) {
- b = atoi(cmdtok[i]+2);
- if (b <= 0) {
- printf("drcorners: bad option b.\n");
- return;
- }
- } else if (!strcmp(cmdtok[i], "i")) {
- ignore = 1;
- } else if (!strcmp(cmdtok[i], "from")) {
- i++;
- char x[3][3] = {"ud", "fb", "rl"};
- for (int j = 0; j < 3; j++)
- if (!strcmp(cmdtok[i], x[j]))
- from = j+1;
- if (!from) {
- printf("drcorners: bad from option.\n");
- return;
- }
- } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) {
- printf("drcorners: error reading moves or ID.\n");
- return;
- }
- }
-
- if (scram[0] == 0) {
- if (read_moves_from_prompt(scram) == -1) {
- printf("drcorners: error reading moves.\n");
- return;
- }
- }
-
- unniss(scram, scram_unnissed);
-
- /* Call solver */
- int c_list[m+5][30], nc;
- nc = dr_corners_scram_spam(scram_unnissed, c_list, from, m, b, ignore);
- print_results(nc, c_list);
-}
-
-
-void exit_quit_cmd(int n, char cmdtok[][100]) {
- if (n == 1)
- exit(0);
- else
- printf("%s: wrong synstax.\n", cmdtok[0]);
-}
-
-/***************************************************************/
-/* List of all commands */
-/* Important: they must be in the same order in the two arrays */
-/***************************************************************/
-
-char *commands[][10] = {
- {"help", "[COMMAND]",
- "Print this help, or a help page for COMMAND."},
- {"scramble", "[OPTIONS]",
- "Prints a random-state scramble."},
- {"save", "[MOVES|@ID|$ID]",
- "Save or copy a scramble."},
- {"change", "$ID1 [MOVES|$ID2|@ID2]",
- "Change a memorized scramble."},
- {"print", "[$ID|@ID]",
- "Print memorized sequences."},
- {"add", "[MOVES|$ID1|@ID1] $ID2",
- "Add moves at the end of a memorized scramble."},
- {"invert", "[MOVES|$ID|@ID]",
- "Inverts the given sequence of moves."},
- {"unniss", "[MOVES|$ID|@ID]}",
- "Removes NISS: A (B) -> B\' A."},
- {"pic", "[MOVES|$ID|@ID]",
- "Show a text description of the scrambled cube."},
- {"solve", "[MOVES|$ID|@ID]",
- "Solves a scramble."},
- {"replace", "[MOVES|$ID|@ID]",
- "Find non-optimal subsequences."},
- {"clear", "",
- "Delete saved scrambles and output sequences."},
- {"eo", "[MOVES|$ID|@ID]",
- "Solves EO."},
- {"co", "[MOVES|$ID|@ID]",
- "Solves CO."},
- {"dr", "[MOVES|$ID|@ID]",
- "Solves DR, either directly or from eo."},
- {"htr", "[MOVES|$ID|@ID]",
- "Solves HTR from DR."},
- {"drfinish", "[MOVES|$ID|@ID]",
- "Solves the cube after DR."},
- {"htrfinish", "[MOVES|$ID|@ID]",
- "Solves the cube using only half turns."},
- {"drcorners", "[MOVES|$ID|@ID]",
- "Solves corners after DR."},
- {"exit", "",
- "Exit nissy."},
- {"quit", "",
- "Exit nissy."},
- {"", "", ""}
-};
-
-void help_cmd(int n, char cmdtok[][100]) {
- if (n == 1) {
- printf("\n");
- for (int i = 0; commands[i][0][0]; i++)
- printf("%-10s%-25s%s\n", commands[i][0], commands[i][1], commands[i][2]);
- printf("\n");
- printf("Type \'help\' followed by a command for a detailed help page.\n");
- printf("Type \'help nissy\' for a general user guide.\n");
- } else if (n == 2) {
- for (int i = 0; i < Npages; i++) {
- if (!strcmp(helppages[i][0], cmdtok[1])) {
- printf("%s", helppages[i][1]);
- return;
- }
- }
- printf("No help page for %s.\n", cmdtok[1]);
- return;
- } else {
- printf("help: wrong syntax.\n");
- }
-}
-
-void (*cmd_list[])(int n, char cmdtok[][100]) = {
- help_cmd, scramble_cmd, save_cmd, change_cmd, print_cmd,
- add_cmd, invert_cmd, unniss_cmd, pic_cmd,
- solve_cmd, replace_cmd, clear_cmd,
- eo_cmd, co_cmd, dr_cmd, htr_cmd,
- drfinish_cmd, htrfinish_cmd, drcorners_cmd,
- exit_quit_cmd, exit_quit_cmd, NULL
-};
-
-
-void execcmd(int n, char cmdtok[][100]) {
- int i = 0;
- while (strcmp(commands[i][0], cmdtok[0]) && strcmp(commands[i][0], ""))
- i++;
- if (strcmp(commands[i][0], ""))
- (*cmd_list[i])(n, cmdtok);
- else
- printf("%s: not a command.\n", cmdtok[0]);
-}
-
-
-/* Main loop */
-
-int main() {
- init_transition_table();
- init_possible_next();
-
- printf("Type help for a list of commands.\n");
-
- char cmd[1000] = "";
- while (1) {
- printf("nissy-# ");
- if (fgets(cmd, 1000, stdin) == NULL)
- break;
- char cmdtok[100][100];
- int n = parsecmd(cmd, cmdtok);
- if (n == 0)
- continue;
- execcmd(n, cmdtok);
- }
-
- return 0;
-}
diff --git a/src/moves.c b/src/moves.c
@@ -1,521 +1,475 @@
-/* This file contains the definitions of the basic moves of the cube.
- * There is no object or type representing the cube.
- * Data about the cube can be represented by arrays (describing the position
- * of pieces of certain types), integers (representing for example a bitmask
- * for the orientation of pieces of certain type, or the permutation index of
- * an array representing the permutation of pieces).
- * Each of the moves functions operates on one such piece of data.
- *
- * For example, a way of representing the cube can be:
- * - An integer eo, which is a bitmask for the orientation of the edges.
- * - An integer co, same for corners.
- * - An array ep[12], where a[i]=j means that the edge j is in place i.
- * - An integer cp representing the permutation index of a permutation array
- * which is the analogue of that described for edges.
- *
- * Different representations will be used for different use-cases. */
-
-#include "coordinates.h"
#include "moves.h"
-/* possible_next[i][j] is a bitmask representing the possible
- * next moves we can apply. For example, if the last moves a 0 R then it does
- * not make sense to apply R, R2 or R'. If they are U D2 it does not make
- * sense to apply any U* or D*. */
-int possible_next[19][19];
+/* Local functions ***********************************************************/
-int parallel(int m1, int m2) {
- if (m1 == 0 || m2 == 0) return 0;
- return ((m1-1)/6 == (m2-1)/6);
-}
-
-int compute_possible_next(int last1, int last2) {
- if (last1 == 0) return move_mask_all;
-
- /* Removes the 2 or ' (e.g. turns U2 to U, R' to R). */
- last2 = (last2 == 0) ? last2 : 3*((last2-1)/3) + 1;
- last1 = 3*((last1-1)/3) + 1;
+static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f);
+static bool read_mtables_file();
+static bool write_mtables_file();
- int mask = move_mask_all ^ (7 << last1);
+/* Tables and other data *****************************************************/
- if (parallel(last1, last2))
- mask ^= 7 << last2;
- else if (last1 % 6 == 4) /*Always U before D, R before L, F before B*/
- mask ^= 7 << (last1-3);
+/* Every move is translated to a an <U, x, y> alg before filling the
+ transition tables, see init_moves() */
- return mask;
-}
-
-void init_possible_next() {
- for (int i = 0; i < 19; i++)
- for (int j = 0; j < 19; j++)
- possible_next[i][j] = compute_possible_next(i, j);
-}
-
-/* Piece cycles depending on the move. For example edge_cycle[U2][UF]
- * gives the piece in position UF after applying U2 to a solved cube */
-
-int edge_cycle[19][12] = {
- {UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR}, /* - */
- {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, /* U */
- {UB, UR, UF, UL, DF, DL, DB, DR, FR, FL, BL, BR}, /* U2 */
- {UL, UB, UR, UF, DF, DL, DB, DR, FR, FL, BL, BR}, /* U' */
- {UF, UL, UB, UR, DL, DB, DR, DF, FR, FL, BL, BR}, /* D */
- {UF, UL, UB, UR, DB, DR, DF, DL, FR, FL, BL, BR}, /* D2 */
- {UF, UL, UB, UR, DR, DF, DL, DB, FR, FL, BL, BR}, /* D' */
- {UF, UL, UB, FR, DF, DL, DB, BR, DR, FL, BL, UR}, /* R */
- {UF, UL, UB, DR, DF, DL, DB, UR, BR, FL, BL, FR}, /* R2 */
- {UF, UL, UB, BR, DF, DL, DB, FR, UR, FL, BL, DR}, /* R' */
- {UF, BL, UB, UR, DF, FL, DB, DR, FR, UL, DL, BR}, /* L */
- {UF, DL, UB, UR, DF, UL, DB, DR, FR, BL, FL, BR}, /* L2 */
- {UF, FL, UB, UR, DF, BL, DB, DR, FR, DL, UL, BR}, /* L' */
- {FL, UL, UB, UR, FR, DL, DB, DR, UF, DF, BL, BR}, /* F */
- {DF, UL, UB, UR, UF, DL, DB, DR, FL, FR, BL, BR}, /* F2 */
- {FR, UL, UB, UR, FL, DL, DB, DR, DF, UF, BL, BR}, /* F' */
- {UF, UL, BR, UR, DF, DL, BL, DR, FR, FL, UB, DB}, /* B */
- {UF, UL, DB, UR, DF, DL, UB, DR, FR, FL, BR, BL}, /* B2 */
- {UF, UL, BL, UR, DF, DL, BR, DR, FR, FL, DB, UB} /* B' */
+static int edge_cycle[NMOVES][12] =
+{
+ [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR },
+ [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR },
+ [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL }
};
-int corner_cycle[19][8] = {
- {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, /* - */
- {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, /* U */
- {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR}, /* U2 */
- {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR}, /* U' */
- {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR}, /* D */
- {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL}, /* D2 */
- {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL}, /* D' */
- {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR}, /* R */
- {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR}, /* R2 */
- {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR}, /* R' */
- {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR}, /* L */
- {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR}, /* L2 */
- {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR}, /* L' */
- {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR}, /* F */
- {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR}, /* F2 */
- {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR}, /* F' */
- {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL}, /* B */
- {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL}, /* B2 */
- {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR}, /* U' */
+static int corner_cycle[NMOVES][8] =
+{
+ [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR },
+ [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR },
+ [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL }
};
-/* Transition tables */
-
-int eofb_transition_table[pow2to11][19];
-int eorl_transition_table[pow2to11][19];
-int eoud_transition_table[pow2to11][19];
-int coud_transition_table[pow3to7][19];
-int cofb_transition_table[pow3to7][19];
-int corl_transition_table[pow3to7][19];
-int epud_transition_table[factorial8][19];
-int eprl_transition_table[factorial8][19];
-int epfb_transition_table[factorial8][19];
-int epose_transition_table[binom12on4][19];
-int eposs_transition_table[binom12on4][19];
-int eposm_transition_table[binom12on4][19];
-int epe_transition_table[factorial4][19];
-int eps_transition_table[factorial4][19];
-int epm_transition_table[factorial4][19];
-int emslices_transition_table[binom12on4*binom8on4][19];
-int cp_transition_table[factorial8][19];
-
-/***/
-/* Functions for permuting pieces (given in array format) */
-/***/
-
-void apply_move_ep_array(int move, int ep[12]) {
- int aux[12];
- for (int i = 0; i < 12; i++)
- aux[i] = ep[i];
- for (int i = 0; i < 12; i++)
- ep[i] = aux[edge_cycle[move][i]];
-}
-
-void apply_move_cp_array(int move, int cp[8]) {
- int aux[8];
- for (int i = 0; i < 8; i++)
- aux[i] = cp[i];
- for (int i = 0; i < 8; i++)
- cp[i] = aux[corner_cycle[move][i]];
-}
-
-/***/
-/* Functions for permuting pieces (given in integer format) */
-/***/
-
-int apply_move_ep_int(int move, int ep) {
- int a[12];
- ep_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return ep_array_to_int(a);
-}
-
-int apply_move_epud_int(int move, int ep) {
- int a[12];
- epud_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return epud_array_to_int(a);
-}
-
-int apply_move_eprl_int(int move, int ep) {
- int a[12];
- eprl_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return eprl_array_to_int(a);
-}
-
-int apply_move_epfb_int(int move, int ep) {
- int a[12];
- epfb_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return epfb_array_to_int(a);
-}
-
-int apply_move_epose_int(int move, int ep) {
- int a[12];
- epose_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return epose_array_to_int(a);
-}
-
-int apply_move_eposs_int(int move, int ep) {
- int a[12];
- eposs_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return eposs_array_to_int(a);
-}
-
-int apply_move_eposm_int(int move, int ep) {
- int a[12];
- eposm_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return eposm_array_to_int(a);
-}
-
-int apply_move_epe_int(int move, int ep) {
- int a[12];
- epe_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return epe_array_to_int(a);
-}
-
-int apply_move_eps_int(int move, int ep) {
- int a[12];
- eps_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return eps_array_to_int(a);
-}
-
-int apply_move_epm_int(int move, int ep) {
- int a[12];
- epm_int_to_array(ep, a);
- apply_move_ep_array(move, a);
- return epm_array_to_int(a);
-}
-
-int apply_move_emslices_int(int move, int e) {
- int a[12];
- emslices_int_to_array(e, a);
- apply_move_ep_array(move, a);
- return emslices_array_to_int(a);
-}
-
-int apply_move_cp_int(int move, int cp) {
- int a[8];
- cp_int_to_array(cp, a);
- apply_move_cp_array(move, a);
- return cp_array_to_int(a);
-}
-
-int apply_move_eofb_int(int move, int eo) {
- int a[12];
- eo_11bits_to_array(eo, a);
- apply_move_ep_array(move, a);
- /* Change edge orientation */
- if (move == F || move == F3) {
- a[UF] = 1 - a[UF];
- a[DF] = 1 - a[DF];
- a[FR] = 1 - a[FR];
- a[FL] = 1 - a[FL];
- }
- if (move == B || move == B3) {
- a[UB] = 1 - a[UB];
- a[DB] = 1 - a[DB];
- a[BL] = 1 - a[BL];
- a[BR] = 1 - a[BR];
- }
- return eo_array_to_11bits(a);
-}
-
-int apply_move_eorl_int(int move, int eo) {
- int a[12];
- eo_11bits_to_array(eo, a);
- apply_move_ep_array(move, a);
- /* Change edge orientation */
- if (move == R || move == R3) {
- a[UR] = 1 - a[UR];
- a[DR] = 1 - a[DR];
- a[FR] = 1 - a[FR];
- a[BR] = 1 - a[BR];
- }
- if (move == L || move == L3) {
- a[UL] = 1 - a[UL];
- a[DL] = 1 - a[DL];
- a[FL] = 1 - a[FL];
- a[BL] = 1 - a[BL];
- }
- return eo_array_to_11bits(a);
-}
-
-int apply_move_eoud_int(int move, int eo) {
- int a[12];
- eo_11bits_to_array(eo, a);
- apply_move_ep_array(move, a);
- /* Change edge orientation */
- if (move == U || move == U3) {
- a[UF] = 1 - a[UF];
- a[UL] = 1 - a[UL];
- a[UB] = 1 - a[UB];
- a[UR] = 1 - a[UR];
- }
- if (move == D || move == D3) {
- a[DF] = 1 - a[DF];
- a[DL] = 1 - a[DL];
- a[DB] = 1 - a[DB];
- a[DR] = 1 - a[DR];
- }
- return eo_array_to_11bits(a);
-}
-
-int apply_move_coud_int(int move, int co) {
- int a[8];
- co_7trits_to_array(co, a);
- apply_move_cp_array(move, a);
- /* Change corner orientation */
- if (move == R || move == R3) {
- a[UFR] = (a[UFR] + 2) % 3;
- a[UBR] = (a[UBR] + 1) % 3;
- a[DBR] = (a[DBR] + 2) % 3;
- a[DFR] = (a[DFR] + 1) % 3;
- }
- if (move == L || move == L3) {
- a[UBL] = (a[UBL] + 2) % 3;
- a[UFL] = (a[UFL] + 1) % 3;
- a[DFL] = (a[DFL] + 2) % 3;
- a[DBL] = (a[DBL] + 1) % 3;
- }
- if (move == F || move == F3) {
- a[UFL] = (a[UFL] + 2) % 3;
- a[UFR] = (a[UFR] + 1) % 3;
- a[DFR] = (a[DFR] + 2) % 3;
- a[DFL] = (a[DFL] + 1) % 3;
- }
- if (move == B || move == B3) {
- a[UBR] = (a[UBR] + 2) % 3;
- a[UBL] = (a[UBL] + 1) % 3;
- a[DBL] = (a[DBL] + 2) % 3;
- a[DBR] = (a[DBR] + 1) % 3;
- }
- return co_array_to_7trits(a);
-}
-
-int apply_move_cofb_int(int move, int co) {
- int a[8];
- co_7trits_to_array(co, a);
- apply_move_cp_array(move, a);
- /* Change corner orientation */
- if (move == R || move == R3) {
- a[UFR] = (a[UFR] + 1) % 3;
- a[UBR] = (a[UBR] + 2) % 3;
- a[DBR] = (a[DBR] + 1) % 3;
- a[DFR] = (a[DFR] + 2) % 3;
- }
- if (move == L || move == L3) {
- a[UBL] = (a[UBL] + 1) % 3;
- a[UFL] = (a[UFL] + 2) % 3;
- a[DFL] = (a[DFL] + 1) % 3;
- a[DBL] = (a[DBL] + 2) % 3;
- }
- if (move == U || move == U3) {
- a[UFL] = (a[UFL] + 1) % 3;
- a[UFR] = (a[UFR] + 2) % 3;
- a[UBL] = (a[UBL] + 2) % 3;
- a[UBR] = (a[UBR] + 1) % 3;
- }
- if (move == D || move == D3) {
- a[DFL] = (a[DFL] + 2) % 3;
- a[DFR] = (a[DFR] + 1) % 3;
- a[DBL] = (a[DBL] + 1) % 3;
- a[DBR] = (a[DBR] + 2) % 3;
- }
- return co_array_to_7trits(a);
-}
-
-int apply_move_corl_int(int move, int co) {
- int a[8];
- co_7trits_to_array(co, a);
- apply_move_cp_array(move, a);
- /* Change corner orientation */
- if (move == F || move == F3) {
- a[UFR] = (a[UFR] + 2) % 3;
- a[UFL] = (a[UFL] + 1) % 3;
- a[DFL] = (a[DFL] + 2) % 3;
- a[DFR] = (a[DFR] + 1) % 3;
- }
- if (move == B || move == B3) {
- a[UBL] = (a[UBL] + 2) % 3;
- a[UBR] = (a[UBR] + 1) % 3;
- a[DBR] = (a[DBR] + 2) % 3;
- a[DBL] = (a[DBL] + 1) % 3;
- }
- if (move == U || move == U3) {
- a[UFL] = (a[UFL] + 2) % 3;
- a[UFR] = (a[UFR] + 1) % 3;
- a[UBL] = (a[UBL] + 1) % 3;
- a[UBR] = (a[UBR] + 2) % 3;
- }
- if (move == D || move == D3) {
- a[DFL] = (a[DFL] + 1) % 3;
- a[DFR] = (a[DFR] + 2) % 3;
- a[DBL] = (a[DBL] + 2) % 3;
- a[DBR] = (a[DBR] + 1) % 3;
- }
- return co_array_to_7trits(a);
-}
-
-
-
-/* Initialize transition tables */
-
-void init_epud_transition_table() {
- for (int i = 0; i < factorial8; i++)
- for (int j = 0; j < 19; j++)
- if (move_mask_drud & (1 << j))
- epud_transition_table[i][j] = apply_move_epud_int(j, i);
-}
-
-void init_eprl_transition_table() {
- for (int i = 0; i < factorial8; i++)
- for (int j = 0; j < 19; j++)
- if (move_mask_drrl & (1 << j))
- eprl_transition_table[i][j] = apply_move_eprl_int(j, i);
-}
-
-void init_epfb_transition_table() {
- for (int i = 0; i < factorial8; i++)
- for (int j = 0; j < 19; j++)
- if (move_mask_drfb & (1 << j))
- epfb_transition_table[i][j] = apply_move_epfb_int(j, i);
-}
-
-void init_epose_transition_table() {
- for (int i = 0; i < binom12on4; i++)
- for (int j = 0; j < 19; j++)
- epose_transition_table[i][j] = apply_move_epose_int(j, i);
-}
-
-void init_eposs_transition_table() {
- for (int i = 0; i < binom12on4; i++)
- for (int j = 0; j < 19; j++)
- eposs_transition_table[i][j] = apply_move_eposs_int(j, i);
-}
-
-void init_eposm_transition_table() {
- for (int i = 0; i < binom12on4; i++)
- for (int j = 0; j < 19; j++)
- eposm_transition_table[i][j] = apply_move_eposm_int(j, i);
-}
-
-void init_epe_transition_table() {
- for (int i = 0; i < factorial4; i++) {
- for (int j = 0; j < 19; j++)
- if (move_mask_drud & (1 << j))
- epe_transition_table[i][j] = apply_move_epe_int(j, i);
- }
-}
-
-void init_eps_transition_table() {
- for (int i = 0; i < factorial4; i++) {
- for (int j = 0; j < 19; j++)
- if (move_mask_drfb & (1 << j))
- eps_transition_table[i][j] = apply_move_eps_int(j, i);
- }
-}
-
-void init_epm_transition_table() {
- for (int i = 0; i < factorial4; i++) {
- for (int j = 0; j < 19; j++)
- if (move_mask_drrl & (1 << j))
- epm_transition_table[i][j] = apply_move_epm_int(j, i);
- }
-}
-
-void init_emslices_transition_table() {
- for (int i = 0; i < binom12on4*binom8on4; i++) {
- for (int j = 0; j < 19; j++)
- emslices_transition_table[i][j] = apply_move_emslices_int(j, i);
- }
-}
+static int center_cycle[NMOVES][6] =
+{
+ [x] = { F_center, B_center, R_center, L_center, D_center, U_center },
+ [y] = { U_center, D_center, B_center, F_center, R_center, L_center }
+};
-void init_cp_transition_table() {
- for (int i = 0; i < factorial8; i++)
- for (int j = 0; j < 19; j++)
- cp_transition_table[i][j] = apply_move_cp_int(j, i);
-}
+static int eofb_flipped[NMOVES][12] = {
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
-void init_eofb_transition_table() {
- for (int i = 0; i < pow2to11; i++)
- for (int j = 0; j < 19; j++)
- eofb_transition_table[i][j] = apply_move_eofb_int(j, i);
-}
+static int eorl_flipped[NMOVES][12] = {
+ [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }
+};
-void init_eorl_transition_table() {
- for (int i = 0; i < pow2to11; i++)
- for (int j = 0; j < 19; j++)
- eorl_transition_table[i][j] = apply_move_eorl_int(j, i);
-}
+static int eoud_flipped[NMOVES][12] = {
+ [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 },
+ [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 },
+ [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+};
-void init_eoud_transition_table() {
- for (int i = 0; i < pow2to11; i++)
- for (int j = 0; j < 19; j++)
- eoud_transition_table[i][j] = apply_move_eoud_int(j, i);
-}
+static int coud_flipped[NMOVES][8] = {
+ [x] = {
+ [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2,
+ [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2
+ }
+};
-void init_coud_transition_table() {
- for (int i = 0; i < pow3to7; i++)
- for (int j = 0; j < 19; j++ )
- coud_transition_table[i][j] = apply_move_coud_int(j, i);
-}
+static int corl_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 },
+ [y] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ }
+};
-void init_cofb_transition_table() {
- for (int i = 0; i < pow3to7; i++)
- for (int j = 0; j < 19; j++ )
- cofb_transition_table[i][j] = apply_move_cofb_int(j, i);
-}
+static int cofb_flipped[NMOVES][8] = {
+ [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 },
+ [x] = {
+ [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2,
+ [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1
+ },
+ [y] = {
+ [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1,
+ [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2
+ }
+};
-void init_corl_transition_table() {
- for (int i = 0; i < pow3to7; i++)
- for (int j = 0; j < 19; j++ )
- corl_transition_table[i][j] = apply_move_corl_int(j, i);
-}
+static char equiv_alg_string[100][NMOVES] = {
+ [NULLMOVE] = "",
+
+ [U] = " U ",
+ [U2] = " UU ",
+ [U3] = " UUU ",
+ [D] = " xx U xx ",
+ [D2] = " xx UU xx ",
+ [D3] = " xx UUU xx ",
+ [R] = " yx U xxxyyy ",
+ [R2] = " yx UU xxxyyy ",
+ [R3] = " yx UUU xxxyyy ",
+ [L] = " yyyx U xxxy ",
+ [L2] = " yyyx UU xxxy ",
+ [L3] = " yyyx UUU xxxy ",
+ [F] = " x U xxx ",
+ [F2] = " x UU xxx ",
+ [F3] = " x UUU xxx ",
+ [B] = " xxx U x ",
+ [B2] = " xxx UU x ",
+ [B3] = " xxx UUU x ",
+
+ [Uw] = " xx U xx y ",
+ [Uw2] = " xx UU xx yy ",
+ [Uw3] = " xx UUU xx yyy ",
+ [Dw] = " U yyy ",
+ [Dw2] = " UU yy ",
+ [Dw3] = " UUU y ",
+ [Rw] = " yyyx U xxxy x ",
+ [Rw2] = " yyyx UU xxxy xx ",
+ [Rw3] = " yyyx UUU xxxy xxx ",
+ [Lw] = " yx U xxxyyy xxx ",
+ [Lw2] = " yx UU xxxyyy xx ",
+ [Lw3] = " yx UUU xxxyyy x ",
+ [Fw] = " xxx U x yxxxyyy ",
+ [Fw2] = " xxx UU x yxxyyy ",
+ [Fw3] = " xxx UUU x yxyyy ",
+ [Bw] = " x U xxx yxyyy ",
+ [Bw2] = " x UU xxx yxxyyy ",
+ [Bw3] = " x UUU xxx yxxxyyy ",
+
+ [M] = " yx U xx UUU yxyyy ",
+ [M2] = " yx UU xx UU xxxy ",
+ [M3] = " yx UUU xx U yxxxy ",
+ [S] = " x UUU xx U yyyx ",
+ [S2] = " x UU xx UU yyx ",
+ [S3] = " x U xx UUU yx ",
+ [E] = " U xx UUU xxyyy ",
+ [E2] = " UU xx UU xxyy ",
+ [E3] = " UUU xx U xxy ",
+
+ [x] = " x ",
+ [x2] = " xx ",
+ [x3] = " xxx ",
+ [y] = " y ",
+ [y2] = " yy ",
+ [y3] = " yyy ",
+ [z] = " yyy x y ",
+ [z2] = " yy xx ",
+ [z3] = " y x yyy "
+};
-void init_transition_table() {
- init_epud_transition_table();
- init_eprl_transition_table();
- init_epfb_transition_table();
- init_epose_transition_table();
- init_eposs_transition_table();
- init_eposm_transition_table();
- init_epe_transition_table();
- init_eps_transition_table();
- init_epm_transition_table();
- init_emslices_transition_table();
- init_cp_transition_table();
- init_eofb_transition_table();
- init_eorl_transition_table();
- init_eoud_transition_table();
- init_coud_transition_table();
- init_cofb_transition_table();
- init_corl_transition_table();
+/* Transition tables, to be loaded up at the beginning */
+static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8];
+static int eofb_mtable[NMOVES][POW2TO11];
+static int eorl_mtable[NMOVES][POW2TO11];
+static int eoud_mtable[NMOVES][POW2TO11];
+static int cp_mtable[NMOVES][FACTORIAL8];
+static int coud_mtable[NMOVES][POW3TO7];
+static int cofb_mtable[NMOVES][POW3TO7];
+static int corl_mtable[NMOVES][POW3TO7];
+static int cpos_mtable[NMOVES][FACTORIAL6];
+
+
+/* Local functions implementation ********************************************/
+
+static Cube
+apply_move_cubearray(Move m, Cube cube, PieceFilter f)
+{
+ /*init_moves();*/
+
+ CubeArray m_arr = {
+ edge_cycle[m],
+ eofb_flipped[m],
+ eorl_flipped[m],
+ eoud_flipped[m],
+ corner_cycle[m],
+ coud_flipped[m],
+ corl_flipped[m],
+ cofb_flipped[m],
+ center_cycle[m]
+ };
+
+ return move_via_arrays(&m_arr, cube, f);
+}
+
+/* Public functions **********************************************************/
+
+Cube
+apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a)
+{
+ Cube ret = {0};
+ int i;
+
+ for (i = 0; i < alg->len; i++)
+ if (alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ ret = compose_filtered(c, inverse_cube(ret), f);
+
+ for (i = 0; i < alg->len; i++)
+ if (!alg->inv[i])
+ ret = a ? apply_move(alg->move[i], ret) :
+ apply_move_cubearray(alg->move[i], ret, f);
+
+ return ret;
+}
+
+Cube
+apply_alg(Alg *alg, Cube cube)
+{
+ return apply_alg_generic(alg, cube, pf_all, true);
+}
+
+Cube
+apply_move(Move m, Cube cube)
+{
+ /*init_moves();*/
+
+ return (Cube) {
+ .epose = epose_mtable[m][cube.epose],
+ .eposs = eposs_mtable[m][cube.eposs],
+ .eposm = eposm_mtable[m][cube.eposm],
+ .eofb = eofb_mtable[m][cube.eofb],
+ .eorl = eorl_mtable[m][cube.eorl],
+ .eoud = eoud_mtable[m][cube.eoud],
+ .coud = coud_mtable[m][cube.coud],
+ .cofb = cofb_mtable[m][cube.cofb],
+ .corl = corl_mtable[m][cube.corl],
+ .cp = cp_mtable[m][cube.cp],
+ .cpos = cpos_mtable[m][cube.cpos]
+ };
+}
+
+static bool
+read_mtables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = POW2TO11,
+ [5] = POW2TO11,
+ [6] = FACTORIAL8,
+ [7] = POW3TO7,
+ [8] = POW3TO7,
+ [9] = POW3TO7,
+ [10] = FACTORIAL6
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fread(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fread(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fread(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fread(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fread(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fread(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fread(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fread(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fread(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_mtables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int m, b = sizeof(int);
+ bool r = true;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = POW2TO11,
+ [5] = POW2TO11,
+ [6] = FACTORIAL8,
+ [7] = POW3TO7,
+ [8] = POW3TO7,
+ [9] = POW3TO7,
+ [10] = FACTORIAL6
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/mtables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NMOVES; m++) {
+ r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3];
+ r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4];
+ r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6];
+ r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7];
+ r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8];
+ r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9];
+ r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10];
+ }
+
+ fclose(f);
+ return r;
+}
+
+bool
+commute(Move m1, Move m2)
+{
+ static bool initialized = false;
+ static bool commute_aux[NMOVES][NMOVES];
+
+ if (!initialized) {
+ Cube c1, c2;
+ int i, j;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ c1 = apply_move(i, apply_move(j, (Cube){0}));
+ c2 = apply_move(j, apply_move(i, (Cube){0}));
+ commute_aux[i][j] = equal(c1, c2) && i && j;
+ }
+ }
+
+ initialized = true;
+ }
+
+ return commute_aux[m1][m2];
+}
+
+bool
+possible_next(Move m1, Move m2, Move m3)
+{
+ static bool initialized = false;
+ static bool paux[NMOVES][NMOVES][NMOVES];
+
+ if (!initialized) {
+ int i, j, k;
+ bool p, q, c;
+
+ for (i = 0; i < NMOVES; i++) {
+ for (j = 0; j < NMOVES; j++) {
+ for (k = 0; k < NMOVES; k++) {
+ p = j && base_move(j) == base_move(k);
+ q = i && base_move(i) == base_move(k);
+ c = commute(i, j);
+ paux[i][j][k] = !(p || (c && q));
+ }
+ }
+ }
+
+ initialized = true;
+ }
+
+ return paux[m1][m2][m3];
+}
+
+void
+init_moves() {
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ Cube c;
+ CubeArray arrs;
+ int i;
+ unsigned int ui;
+ Move m;
+ Alg *equiv_alg[NMOVES];
+
+ for (i = 0; i < NMOVES; i++)
+ equiv_alg[i] = new_alg(equiv_alg_string[i]);
+
+ /* Generate all move cycles and flips; I do this regardless */
+ for (i = 0; i < NMOVES; i++) {
+ if (i == U || i == x || i == y)
+ continue;
+
+ c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false);
+
+ arrs = (CubeArray) {
+ edge_cycle[i],
+ eofb_flipped[i],
+ eorl_flipped[i],
+ eoud_flipped[i],
+ corner_cycle[i],
+ coud_flipped[i],
+ corl_flipped[i],
+ cofb_flipped[i],
+ center_cycle[i]
+ };
+ cube_to_arrays(c, &arrs, pf_all);
+ }
+
+ if (read_mtables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "mtables");
+
+ /* Initialize transition tables */
+ for (m = 0; m < NMOVES; m++) {
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c = (Cube){ .epose = ui };
+ c = apply_move_cubearray(m, c, pf_e);
+ epose_mtable[m][ui] = c.epose;
+
+ c = (Cube){ .eposs = ui };
+ c = apply_move_cubearray(m, c, pf_s);
+ eposs_mtable[m][ui] = c.eposs;
+
+ c = (Cube){ .eposm = ui };
+ c = apply_move_cubearray(m, c, pf_m);
+ eposm_mtable[m][ui] = c.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ c = (Cube){ .eofb = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eofb_mtable[m][ui] = c.eofb;
+
+ c = (Cube){ .eorl = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eorl_mtable[m][ui] = c.eorl;
+
+ c = (Cube){ .eoud = ui };
+ c = apply_move_cubearray(m, c, pf_eo);
+ eoud_mtable[m][ui] = c.eoud;
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ c = (Cube){ .coud = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ coud_mtable[m][ui] = c.coud;
+
+ c = (Cube){ .corl = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ corl_mtable[m][ui] = c.corl;
+
+ c = (Cube){ .cofb = ui };
+ c = apply_move_cubearray(m, c, pf_co);
+ cofb_mtable[m][ui] = c.cofb;
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ c = (Cube){ .cp = ui };
+ c = apply_move_cubearray(m, c, pf_cp);
+ cp_mtable[m][ui] = c.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ c = (Cube){ .cpos = ui };
+ c = apply_move_cubearray(m, c, pf_cpos);
+ cpos_mtable[m][ui] = c.cpos;
+ }
+ }
+
+ if (!write_mtables_file())
+ fprintf(stderr, "Error writing mtables\n");
+
+ for (i = 0; i < NMOVES; i++)
+ free_alg(equiv_alg[i]);
}
diff --git a/src/moves.h b/src/moves.h
@@ -1,83 +1,16 @@
-#include "utils.h"
+#ifndef MOVES_H
+#define MOVES_H
-/* Bitmask that define certain movesets. */
-#define move_mask_all 524287 /* Reverse 1111111111111111111 */
-#define move_mask_eofb 155647 /* Reverse 1111111111111010010 */
-#define move_mask_eorl 518527 /* Reverse 1111111010101111111 */
-#define move_mask_eoud 524197 /* Reverse 1010010111111111111 */
-#define move_mask_drud 149887 /* Reverse 1111111010010010010 */
-#define move_mask_drfb 518437 /* Reverse 1010010010010111111 */
-#define move_mask_drrl 155557 /* Reverse 1010010111111010010 */
-#define move_mask_htr 149797 /* Reverse 1010010010010010010 */
+#include "alg.h"
+#include "cube.h"
+#include "env.h"
-extern int possible_next[19][19];
+Cube apply_alg(Alg *alg, Cube cube);
+Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a);
+Cube apply_move(Move m, Cube cube);
+bool commute(Move m1, Move m2);
+bool possible_next(Move m1, Move m2, Move m3);
-int parallel(int m1, int m2);
-void init_possible_next();
-
-/* Transition tables */
-extern int eofb_transition_table[pow2to11][19];
-extern int eorl_transition_table[pow2to11][19];
-extern int eoud_transition_table[pow2to11][19];
-extern int coud_transition_table[pow3to7][19];
-extern int cofb_transition_table[pow3to7][19];
-extern int corl_transition_table[pow3to7][19];
-extern int epud_transition_table[factorial8][19];
-extern int epfb_transition_table[factorial8][19];
-extern int eprl_transition_table[factorial8][19];
-extern int epose_transition_table[binom12on4][19];
-extern int eposs_transition_table[binom12on4][19];
-extern int eposm_transition_table[binom12on4][19];
-extern int epe_transition_table[factorial4][19];
-extern int eps_transition_table[factorial4][19];
-extern int epm_transition_table[factorial4][19];
-extern int emslices_transition_table[binom12on4*binom8on4][19];
-extern int cp_transition_table[factorial8][19];
-
-
-/* Functions for permuting pieces (given in array format) */
-
-void apply_move_ep_array(int move, int ep[12]);
-void apply_move_cp_array(int move, int cp[8]);
-
-/* Functions for permuting pieces (given in integer format) */
-
-int apply_move_ep_int(int move, int ep);
-int apply_move_epud_int(int move, int ep);
-int apply_move_epfb_int(int move, int ep);
-int apply_move_eprl_int(int move, int ep);
-int apply_move_epose_int(int move, int ep);
-int apply_move_eposs_int(int move, int ep);
-int apply_move_eposm_int(int move, int ep);
-int apply_move_epe_int(int move, int ep);
-int apply_move_eps_int(int move, int ep);
-int apply_move_epm_int(int move, int ep);
-int apply_move_cp_int(int move, int cp);
-int apply_move_eofb_int(int move, int eo);
-int apply_move_eorl_int(int move, int eo);
-int apply_move_eoud_int(int move, int eo);
-int apply_move_coud_int(int move, int co);
-int apply_move_cofb_int(int move, int co);
-int apply_move_corl_int(int move, int co);
-
-/* Initialize transition tables */
-
-void init_epud_transition_table();
-void init_epfb_transition_table();
-void init_eprl_transition_table();
-void init_epose_transition_table();
-void init_eposs_transition_table();
-void init_eposm_transition_table();
-void init_epe_transition_table();
-void init_eps_transition_table();
-void init_epm_transition_table();
-void init_cp_transition_table();
-void init_eofb_transition_table();
-void init_eorl_transition_table();
-void init_eoud_transition_table();
-void init_coud_transition_table();
-void init_cofb_transition_table();
-void init_corl_transition_table();
-
-void init_transition_table();
+void init_moves();
+#endif
diff --git a/src/pf.c b/src/pf.c
@@ -0,0 +1,80 @@
+#include "pf.h"
+
+PieceFilter
+pf_all = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .eofb = true,
+ .eorl = true,
+ .eoud = true,
+ .cp = true,
+ .cofb = true,
+ .corl = true,
+ .coud = true,
+ .cpos = true
+};
+
+PieceFilter
+pf_4val = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .eofb = true,
+ .coud = true,
+ .cp = true
+};
+
+PieceFilter
+pf_epcp = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true,
+ .cp = true
+};
+
+PieceFilter
+pf_cpos = {
+ .cpos = true
+};
+
+PieceFilter
+pf_cp = {
+ .cp = true
+};
+
+PieceFilter
+pf_ep = {
+ .epose = true,
+ .eposs = true,
+ .eposm = true
+};
+
+PieceFilter
+pf_e = {
+ .epose = true
+};
+
+PieceFilter
+pf_s = {
+ .eposs = true
+};
+
+PieceFilter
+pf_m = {
+ .eposm = true
+};
+
+PieceFilter
+pf_eo = {
+ .eofb = true,
+ .eorl = true,
+ .eoud = true
+};
+
+PieceFilter
+pf_co = {
+ .cofb = true,
+ .corl = true,
+ .coud = true
+};
diff --git a/src/pf.h b/src/pf.h
@@ -0,0 +1,18 @@
+#ifndef PF_H
+#define PF_H
+
+#include "cubetypes.h"
+
+extern PieceFilter pf_all;
+extern PieceFilter pf_4val;
+extern PieceFilter pf_epcp;
+extern PieceFilter pf_cpos;
+extern PieceFilter pf_cp;
+extern PieceFilter pf_ep;
+extern PieceFilter pf_e;
+extern PieceFilter pf_s;
+extern PieceFilter pf_m;
+extern PieceFilter pf_eo;
+extern PieceFilter pf_co;
+
+#endif
diff --git a/src/pruning.c b/src/pruning.c
@@ -0,0 +1,271 @@
+#include "pruning.h"
+
+static void genptable_bfs(PruneData *pd, int d, Move *ms);
+static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m);
+static void ptable_update(PruneData *pd, Cube cube, int m);
+static void ptable_update_index(PruneData *pd, uint64_t ind, int m);
+static int ptableval_index(PruneData *pd, uint64_t ind);
+static bool read_ptable_file(PruneData *pd);
+static bool write_ptable_file(PruneData *pd);
+
+PruneData
+pd_eofb_HTM = {
+ .filename = "pt_eofb_HTM",
+ .coord = &coord_eofb,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_coud_HTM = {
+ .filename = "pt_coud_HTM",
+ .coord = &coord_coud,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_cornershtr_HTM = {
+ .filename = "pt_cornershtr_HTM",
+ .coord = &coord_cornershtr,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_corners_HTM = {
+ .filename = "pt_corners_HTM",
+ .coord = &coord_corners,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_drud_sym16_HTM = {
+ .filename = "pt_drud_sym16_HTM",
+ .coord = &coord_drud_sym16,
+ .moveset = moveset_HTM,
+};
+
+PruneData
+pd_drud_eofb = {
+ .filename = "pt_drud_eofb",
+ .coord = &coord_drud_eofb,
+ .moveset = moveset_eofb,
+};
+
+PruneData
+pd_drudfin_noE_sym16_drud = {
+ .filename = "pt_drudfin_noE_sym16_drud",
+ .coord = &coord_drudfin_noE_sym16,
+ .moveset = moveset_drud,
+};
+
+PruneData
+pd_htr_drud = {
+ .filename = "pt_htr_drud",
+ .coord = &coord_htr_drud,
+ .moveset = moveset_drud,
+};
+
+PruneData
+pd_htrfin_htr = {
+ .filename = "pt_htrfin_htr",
+ .coord = &coord_htrfin,
+ .moveset = moveset_htr,
+};
+
+PruneData
+pd_khuge_HTM = {
+ .filename = "pt_khuge_HTM",
+ .coord = &coord_khuge,
+ .moveset = moveset_HTM,
+};
+
+void
+genptable(PruneData *pd)
+{
+ Move ms[NMOVES];
+ int d;
+ uint64_t j, oldn;
+
+ if (pd->generated)
+ return;
+
+ /* TODO: check if memory is enough, otherwise maybe exit gracefully? */
+ pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t));
+
+ if (read_ptable_file(pd)) {
+ pd->generated = true;
+ return;
+ }
+ pd->generated = true;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", pd->filename);
+
+ moveset_to_list(pd->moveset, ms);
+
+ /* We use 4 bits per value, so any distance >= 15 is set to 15 */
+ for (j = 0; j < pd->coord->max; j++)
+ ptable_update_index(pd, j, 15);
+
+ for (j = 0; j < pd->coord->max; j++)
+ if (ptableval_index(pd, j) != 15) {
+ printf("Error, non-max value at index %lu!\n", j);
+ break;
+ }
+ printf("Table set, ready to start\n");
+
+ ptable_update(pd, (Cube){0}, 0);
+ pd->n = 1;
+ oldn = 0;
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ 0, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = 1;
+ for (d = 0; d < 15 && pd->n < pd->coord->max; d++) {
+ genptable_bfs(pd, d, ms);
+ fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n",
+ d+1, pd->n - oldn, pd->n, pd->coord->max);
+ oldn = pd->n;
+ }
+
+ if (!write_ptable_file(pd))
+ fprintf(stderr, "Error writing ptable file\n");
+}
+
+static void
+genptable_bfs(PruneData *pd, int d, Move *ms)
+{
+ uint64_t i;
+
+ for (i = 0; i < pd->coord->max; i++)
+ if (ptableval_index(pd, i) == d)
+ genptable_branch(pd, i, d, ms);
+}
+
+static void
+genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms)
+{
+ int i, j;
+ Cube ci, cc, c;
+
+ /*
+ * This is the only line of the whole program where we REALLY need an
+ * anti-indexer function. We could get rid of it if only we could save
+ * a cube object for each index value as we go, but then we would need
+ * an incredible amount of memory to generate each ptable: assuming
+ * fields in struct cube are 32 bit ints that would take 88 times the
+ * memory of the table to be generated, more than 120Gb for
+ * ptable_khuge for example!
+ */
+ ci = pd->coord->cube(ind);
+
+ for (i = 0; i < pd->coord->ntrans; i++) {
+ /* For simplicity trans[] is NULL when ntrans = 1 */
+ c = i == 0 ? ci :
+ apply_trans(pd->coord->trans[i], ci);
+ for (j = 0; ms[j] != NULLMOVE; j++) {
+ cc = apply_move(ms[j], c);
+ if (ptableval(pd, cc) > d+1)
+ ptable_update(pd, cc, d+1);
+ }
+ }
+}
+
+void
+print_ptable(PruneData *pd)
+{
+ uint64_t i, a[16];
+
+ for (i = 0; i < 16; i++)
+ a[i] = 0;
+
+ if (!pd->generated)
+ genptable(pd);
+
+ for (i = 0; i < pd->coord->max; i++)
+ a[ptableval_index(pd, i)]++;
+
+ fprintf(stderr, "Values for table %s\n", pd->filename);
+ for (i = 0; i < 16; i++)
+ printf("%2lu\t%10lu\n", i, a[i]);
+}
+
+uint64_t
+ptablesize(PruneData *pd)
+{
+ return (pd->coord->max + 1) / 2;
+}
+
+static void
+ptable_update(PruneData *pd, Cube cube, int n)
+{
+ uint64_t ind = pd->coord->index(cube);
+ ptable_update_index(pd, ind, n);
+}
+
+static void
+ptable_update_index(PruneData *pd, uint64_t ind, int n)
+{
+ uint8_t oldval2 = pd->ptable[ind/2];
+ int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16;
+
+ pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n;
+ pd->n++;
+}
+
+int
+ptableval(PruneData *pd, Cube cube)
+{
+ return ptableval_index(pd, pd->coord->index(cube));
+}
+
+static int
+ptableval_index(PruneData *pd, uint64_t ind)
+{
+ if (!pd->generated)
+ genptable(pd);
+
+ return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16;
+}
+
+static bool
+read_ptable_file(PruneData *pd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t r;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return r == ptablesize(pd);
+}
+
+static bool
+write_ptable_file(PruneData *pd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t written;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, pd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f);
+ fclose(f);
+
+ return written == ptablesize(pd);
+}
+
diff --git a/src/pruning.h b/src/pruning.h
@@ -0,0 +1,23 @@
+#ifndef PRUNING_H
+#define PRUNING_H
+
+#include "symcoord.h"
+
+extern PruneData pd_eofb_HTM;
+extern PruneData pd_coud_HTM;
+extern PruneData pd_corners_HTM;
+extern PruneData pd_cornershtr_HTM;
+extern PruneData pd_drud_sym16_HTM;
+extern PruneData pd_drud_eofb;
+extern PruneData pd_drudfin_noE_sym16_drud;
+extern PruneData pd_htr_drud;
+extern PruneData pd_htrfin_htr;
+extern PruneData pd_khuge_HTM;
+
+void genptable(PruneData *pd);
+void print_ptable(PruneData *pd);
+uint64_t ptablesize(PruneData *pd);
+int ptableval(PruneData *pd, Cube cube);
+
+#endif
+
diff --git a/src/pruning_tables.c b/src/pruning_tables.c
@@ -1,483 +0,0 @@
-#include <stdint.h>
-#include "pruning_tables.h"
-#include "moves.h"
-
-/* The data contained in e.g. eofb pruning table is the same that is contained
- * in eolr pruning table and so on. For small tables the memory wasted is not
- * too much and it makes things easier. I may change this when I implement
- * bigger tables. */
-int eofb_pruning_table[pow2to11];
-int eorl_pruning_table[pow2to11];
-int eoud_pruning_table[pow2to11];
-int coud_pruning_table[pow3to7];
-int cofb_pruning_table[pow3to7];
-int corl_pruning_table[pow3to7];
-int cp_pruning_table[factorial8];
-
-int eorl_from_eofb_pruning_table[pow2to11];
-int eoud_from_eofb_pruning_table[pow2to11];
-int eoud_from_eorl_pruning_table[pow2to11];
-int eofb_from_eorl_pruning_table[pow2to11];
-int eofb_from_eoud_pruning_table[pow2to11];
-int eorl_from_eoud_pruning_table[pow2to11];
-
-int coud_from_eofb_pruning_table[pow3to7];
-int coud_from_eorl_pruning_table[pow3to7];
-int cofb_from_eorl_pruning_table[pow3to7];
-int cofb_from_eoud_pruning_table[pow3to7];
-int corl_from_eoud_pruning_table[pow3to7];
-int corl_from_eofb_pruning_table[pow3to7];
-
-int cp_drud_pruning_table[factorial8];
-int cp_drfb_pruning_table[factorial8];
-int cp_drrl_pruning_table[factorial8];
-int epud_pruning_table[factorial8];
-int epfb_pruning_table[factorial8];
-int eprl_pruning_table[factorial8];
-
-int cp_htr_pruning_table[factorial8];
-
-int cpud_to_htr_pruning_table[factorial8];
-int cpfb_to_htr_pruning_table[factorial8];
-int cprl_to_htr_pruning_table[factorial8];
-
-
-/* About 1Mb each */
-int8_t eofb_epose_pruning_table[pow2to11][binom12on4];
-int8_t eorl_eposs_pruning_table[pow2to11][binom12on4];
-int8_t eoud_eposm_pruning_table[pow2to11][binom12on4];
-
-/* About 4.5Mb each */
-int8_t eofb_coud_pruning_table[pow2to11][pow3to7];
-int8_t eofb_corl_pruning_table[pow2to11][pow3to7];
-int8_t eorl_coud_pruning_table[pow2to11][pow3to7];
-int8_t eorl_cofb_pruning_table[pow2to11][pow3to7];
-int8_t eoud_cofb_pruning_table[pow2to11][pow3to7];
-int8_t eoud_corl_pruning_table[pow2to11][pow3to7];
-
-/* About 1Mb each */
-int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4];
-int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4];
-int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4];
-int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4];
-int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4];
-int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4];
-
-
-/* Firs one is 88Mb, second one is 71Mb */
-int8_t cp_co_pruning_table[factorial8][pow3to7];
-int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4];
-
-int initialized_small = 0;
-int initialized_directdr = 0;
-int initialized_drfromeo = 0;
-int initialized_huge = 0;
-
-void init_single_table(int n, int t_tab[][19], int p_tab[n], int mask) {
- int state[n];
- state[0] = 0; /* 0 should always be the solved state. */
- p_tab[0] = 0;
- int state_count = 1;
- for (int i = 0; i < state_count; i++) {
- for (int m = 1; m < 19; m++) {
- int next = t_tab[state[i]][m];
- if (mask & (1<<m) && !p_tab[next] && next) {
- p_tab[next] = p_tab[state[i]] + 1;
- state[state_count++] = next;
- }
- }
- }
-}
-
-/* Similar to single table, but specific to "cp to htr".
- * The idea is that we are considering the distance not necessarily to the
- * solved state, but to any state that is either solved or reachable from
- * cp_pruning_table. */
-void init_cptohtr_table(int n, int t_tab[][19], int p_tab[n], int mask) {
-
- for (int i = 0; i < n; i++)
- p_tab[i] = 21;
-
- /* List of htr states */
- int good[n]; good[0] = 0;
- int good_count = 1;
- for (int i = 0; i < n; i++)
- if (cp_htr_pruning_table[i])
- good[good_count++] = i;
-
- /* Init pruning table starting from each possible state */
- int state[n];
- for (int j = 0; j < good_count; j++) {
- state[0] = good[j];
- p_tab[state[0]] = 0;
- int state_count = 1;
- for (int i = 0; i < state_count; i++) {
- for (int m = 1; m < 19; m++) {
- int next = t_tab[state[i]][m];
- if (mask & (1<<m) && (p_tab[next] > p_tab[state[i]] + 1) && next) {
- p_tab[next] = p_tab[state[i]] + 1;
- state[state_count++] = next;
- }
- }
- }
- }
-}
-
-void init_double_table(int n1, int n2,
- int t_table1[n1][19], int t_table2[n2][19],
- int8_t p_table[n1][n2], int mask) {
- static int state1[factorial8*pow3to7], state2[factorial8*pow3to7];
- state1[0] = 0;
- state2[0] = 0;
- p_table[0][0] = 0;
- int state_count = 1;
- for (int i = 0; i < state_count; i++) {
- for (int m = 1; m < 19; m++) {
- int next1 = t_table1[state1[i]][m];
- int next2 = t_table2[state2[i]][m];
- if (mask & (1<<m) && !p_table[next1][next2] && (next1 || next2)) {
- p_table[next1][next2] = p_table[state1[i]][state2[i]] + 1;
- state1[state_count] = next1;
- state2[state_count] = next2;
- state_count++;
- }
- }
- }
-}
-
-void init_eofb_pruning_table() {
- init_single_table(pow2to11, eofb_transition_table, eofb_pruning_table,
- move_mask_all);
-}
-
-void init_eorl_pruning_table() {
- init_single_table(pow2to11, eorl_transition_table, eorl_pruning_table,
- move_mask_all);
-}
-
-void init_eoud_pruning_table() {
- init_single_table(pow2to11, eoud_transition_table, eoud_pruning_table,
- move_mask_all);
-}
-
-void init_coud_pruning_table() {
- init_single_table(pow3to7, coud_transition_table, coud_pruning_table,
- move_mask_all);
-}
-
-void init_cofb_pruning_table() {
- init_single_table(pow3to7, cofb_transition_table, cofb_pruning_table,
- move_mask_all);
-}
-
-void init_corl_pruning_table() {
- init_single_table(pow3to7, corl_transition_table, corl_pruning_table,
- move_mask_all);
-}
-
-void init_cp_pruning_table() {
- init_single_table(factorial8, cp_transition_table, cp_pruning_table,
- move_mask_all);
-}
-
-/* The following tables use the eo moveset */
-void init_eorl_from_eofb_pruning_table() {
- init_single_table(pow2to11, eorl_transition_table,
- eorl_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_eoud_from_eofb_pruning_table() {
- init_single_table(pow2to11, eoud_transition_table,
- eoud_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_eoud_from_eorl_pruning_table() {
- init_single_table(pow2to11, eoud_transition_table,
- eoud_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_eofb_from_eorl_pruning_table() {
- init_single_table(pow2to11, eofb_transition_table,
- eofb_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_eofb_from_eoud_pruning_table() {
- init_single_table(pow2to11, eofb_transition_table,
- eofb_from_eoud_pruning_table, move_mask_eoud);
-}
-
-void init_eorl_from_eoud_pruning_table() {
- init_single_table(pow2to11, eorl_transition_table,
- eorl_from_eoud_pruning_table, move_mask_eoud);
-}
-
-void init_coud_from_eofb_pruning_table() {
- init_single_table(pow3to7, coud_transition_table,
- coud_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_corl_from_eofb_pruning_table() {
- init_single_table(pow3to7, corl_transition_table,
- corl_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_coud_from_eorl_pruning_table() {
- init_single_table(pow3to7, coud_transition_table,
- coud_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_cofb_from_eorl_pruning_table() {
- init_single_table(pow3to7, cofb_transition_table,
- cofb_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_corl_from_eoud_pruning_table() {
- init_single_table(pow3to7, corl_transition_table,
- corl_from_eoud_pruning_table, move_mask_eoud);
-}
-
-void init_cofb_from_eoud_pruning_table() {
- init_single_table(pow3to7, cofb_transition_table,
- cofb_from_eoud_pruning_table, move_mask_eoud);
-}
-
-/* The following tables always use DR moveset */
-void init_epud_pruning_table() {
- init_single_table(factorial8, epud_transition_table, epud_pruning_table,
- move_mask_drud);
-}
-
-void init_epfb_pruning_table() {
- init_single_table(factorial8, epfb_transition_table, epfb_pruning_table,
- move_mask_drfb);
-}
-
-void init_eprl_pruning_table() {
- init_single_table(factorial8, eprl_transition_table, eprl_pruning_table,
- move_mask_drrl);
-}
-
-void init_cp_drud_pruning_table() {
- init_single_table(factorial8, cp_transition_table, cp_drud_pruning_table,
- move_mask_drud);
-}
-
-void init_cp_drfb_pruning_table() {
- init_single_table(factorial8, cp_transition_table, cp_drfb_pruning_table,
- move_mask_drfb);
-}
-
-void init_cp_drrl_pruning_table() {
- init_single_table(factorial8, cp_transition_table, cp_drrl_pruning_table,
- move_mask_drrl);
-}
-
-void init_cp_htr_table() {
- init_single_table(factorial8, cp_transition_table, cp_htr_pruning_table,
- move_mask_htr);
-}
-
-void init_cpud_to_htr_table() {
- init_cptohtr_table(factorial8, cp_transition_table,
- cpud_to_htr_pruning_table, move_mask_drud);
-}
-
-void init_cpfb_to_htr_table() {
- init_cptohtr_table(factorial8, cp_transition_table,
- cpfb_to_htr_pruning_table, move_mask_drfb);
-}
-
-void init_cprl_to_htr_table() {
- init_cptohtr_table(factorial8, cp_transition_table,
- cprl_to_htr_pruning_table, move_mask_drrl);
-}
-
-
-void init_eofb_epose_pruning_table() {
- init_double_table(pow2to11, binom12on4,
- eofb_transition_table, epose_transition_table,
- eofb_epose_pruning_table, move_mask_all);
-}
-
-void init_eorl_eposs_pruning_table() {
- init_double_table(pow2to11, binom12on4,
- eorl_transition_table, eposs_transition_table,
- eorl_eposs_pruning_table, move_mask_all);
-}
-
-void init_eoud_eposm_pruning_table() {
- init_double_table(pow2to11, binom12on4,
- eoud_transition_table, eposm_transition_table,
- eoud_eposm_pruning_table, move_mask_all);
-}
-
-
-
-void init_eofb_coud_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eofb_transition_table, coud_transition_table,
- eofb_coud_pruning_table, move_mask_all);
-}
-
-void init_eofb_corl_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eofb_transition_table, corl_transition_table,
- eofb_corl_pruning_table, move_mask_all);
-}
-
-void init_eorl_coud_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eorl_transition_table, coud_transition_table,
- eorl_coud_pruning_table, move_mask_all);
-}
-
-void init_eorl_cofb_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eorl_transition_table, cofb_transition_table,
- eorl_cofb_pruning_table, move_mask_all);
-}
-
-void init_eoud_corl_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eoud_transition_table, corl_transition_table,
- eoud_corl_pruning_table, move_mask_all);
-}
-
-void init_eoud_cofb_pruning_table() {
- init_double_table(pow2to11, pow3to7,
- eoud_transition_table, cofb_transition_table,
- eoud_cofb_pruning_table, move_mask_all);
-}
-
-void init_coud_epose_from_eofb_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- coud_transition_table, epose_transition_table,
- coud_epose_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_cofb_eposs_from_eorl_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- cofb_transition_table, eposs_transition_table,
- cofb_eposs_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_corl_eposm_from_eoud_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- corl_transition_table, eposm_transition_table,
- corl_eposm_from_eoud_pruning_table, move_mask_eoud);
-}
-
-void init_coud_epose_from_eorl_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- coud_transition_table, epose_transition_table,
- coud_epose_from_eorl_pruning_table, move_mask_eorl);
-}
-
-void init_cofb_eposs_from_eoud_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- cofb_transition_table, eposs_transition_table,
- cofb_eposs_from_eoud_pruning_table, move_mask_eoud);
-}
-
-void init_corl_eposm_from_eofb_pruning_table() {
- init_double_table(pow3to7, binom12on4,
- corl_transition_table, eposm_transition_table,
- corl_eposm_from_eofb_pruning_table, move_mask_eofb);
-}
-
-void init_cp_co_pruning_table() {
- init_double_table(factorial8, pow3to7,
- cp_transition_table, coud_transition_table,
- cp_co_pruning_table, move_mask_all);
-}
-
-void init_triple_eo_pruning_table() {
- init_double_table(pow2to11, binom12on4*binom8on4,
- eofb_transition_table, emslices_transition_table,
- triple_eo_pruning_table, move_mask_all);
-}
-
-
-void init_small_pruning_tables() {
- if (initialized_small)
- return;
-
- init_eofb_pruning_table();
- init_eorl_pruning_table();
- init_eoud_pruning_table();
- init_coud_pruning_table();
- init_cofb_pruning_table();
- init_corl_pruning_table();
- init_cp_pruning_table();
-
- init_eorl_from_eofb_pruning_table();
- init_eoud_from_eofb_pruning_table();
- init_eoud_from_eorl_pruning_table();
- init_eofb_from_eorl_pruning_table();
- init_eofb_from_eoud_pruning_table();
- init_eorl_from_eoud_pruning_table();
-
- init_coud_from_eofb_pruning_table();
- init_corl_from_eofb_pruning_table();
- init_coud_from_eorl_pruning_table();
- init_cofb_from_eorl_pruning_table();
- init_cofb_from_eoud_pruning_table();
- init_corl_from_eoud_pruning_table();
-
- init_epud_pruning_table();
- init_epfb_pruning_table();
- init_eprl_pruning_table();
- init_cp_drud_pruning_table();
- init_cp_drfb_pruning_table();
- init_cp_drrl_pruning_table();
-
- init_cp_htr_table();
- init_cpud_to_htr_table();
- init_cpfb_to_htr_table();
- init_cprl_to_htr_table();
-
- initialized_small = 1;
-}
-
-void init_directdr_pruning_tables() {
- if (initialized_directdr)
- return;
-
- init_eofb_epose_pruning_table();
- init_eorl_eposs_pruning_table();
- init_eoud_eposm_pruning_table();
-
- init_eofb_coud_pruning_table();
- init_eofb_corl_pruning_table();
- init_eorl_coud_pruning_table();
- init_eorl_cofb_pruning_table();
- init_eoud_cofb_pruning_table();
- init_eoud_corl_pruning_table();
-
- initialized_directdr = 1;
-}
-
-void init_drfromeo_pruning_tables() {
- if (initialized_drfromeo)
- return;
-
- init_coud_epose_from_eofb_pruning_table();
- init_cofb_eposs_from_eorl_pruning_table();
- init_corl_eposm_from_eoud_pruning_table();
- init_coud_epose_from_eorl_pruning_table();
- init_cofb_eposs_from_eoud_pruning_table();
- init_corl_eposm_from_eofb_pruning_table();
-
- initialized_drfromeo = 1;
-}
-
-void init_huge_pruning_tables() {
- if (initialized_huge)
- return;
-
- init_cp_co_pruning_table();
- init_triple_eo_pruning_table();
-
- initialized_huge = 1;
-}
-
diff --git a/src/pruning_tables.h b/src/pruning_tables.h
@@ -1,66 +0,0 @@
-#include <stdint.h>
-#include "utils.h"
-
-extern int eofb_pruning_table[pow2to11];
-extern int eorl_pruning_table[pow2to11];
-extern int eoud_pruning_table[pow2to11];
-extern int coud_pruning_table[pow3to7];
-extern int cofb_pruning_table[pow3to7];
-extern int corl_pruning_table[pow3to7];
-extern int cp_pruning_table[factorial8];
-
-extern int eorl_from_eofb_pruning_table[pow2to11];
-extern int eoud_from_eofb_pruning_table[pow2to11];
-extern int eoud_from_eorl_pruning_table[pow2to11];
-extern int eofb_from_eorl_pruning_table[pow2to11];
-extern int eofb_from_eoud_pruning_table[pow2to11];
-extern int eorl_from_eoud_pruning_table[pow2to11];
-
-extern int coud_from_eofb_pruning_table[pow3to7];
-extern int coud_from_eorl_pruning_table[pow3to7];
-extern int cofb_from_eorl_pruning_table[pow3to7];
-extern int cofb_from_eoud_pruning_table[pow3to7];
-extern int corl_from_eoud_pruning_table[pow3to7];
-extern int corl_from_eofb_pruning_table[pow3to7];
-
-extern int cp_drud_pruning_table[factorial8];
-extern int cp_drfb_pruning_table[factorial8];
-extern int cp_drrl_pruning_table[factorial8];
-extern int epud_pruning_table[factorial8];
-extern int epfb_pruning_table[factorial8];
-extern int eprl_pruning_table[factorial8];
-
-extern int cp_htr_pruning_table[factorial8];
-extern int cpud_to_htr_pruning_table[factorial8];
-extern int cpfb_to_htr_pruning_table[factorial8];
-extern int cprl_to_htr_pruning_table[factorial8];
-
-/* About 1Mb each */
-extern int8_t eofb_epose_pruning_table[pow2to11][binom12on4];
-extern int8_t eorl_eposs_pruning_table[pow2to11][binom12on4];
-extern int8_t eoud_eposm_pruning_table[pow2to11][binom12on4];
-
-/* About 4.5Mb each */
-extern int8_t eofb_coud_pruning_table[pow2to11][pow3to7];
-extern int8_t eofb_corl_pruning_table[pow2to11][pow3to7];
-extern int8_t eorl_coud_pruning_table[pow2to11][pow3to7];
-extern int8_t eorl_cofb_pruning_table[pow2to11][pow3to7];
-extern int8_t eoud_cofb_pruning_table[pow2to11][pow3to7];
-extern int8_t eoud_corl_pruning_table[pow2to11][pow3to7];
-
-/* About 1Mb each */
-extern int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4];
-extern int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4];
-extern int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4];
-extern int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4];
-extern int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4];
-extern int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4];
-
-/* First one 88Mb, second one 21Mb */
-extern int8_t cp_co_pruning_table[factorial8][pow3to7];
-extern int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4];
-
-void init_small_pruning_tables();
-void init_directdr_pruning_tables();
-void init_drfromeo_pruning_tables();
-void init_huge_pruning_tables();
diff --git a/src/shell.c b/src/shell.c
@@ -0,0 +1,93 @@
+#include "shell.h"
+
+static void cleanwhitespaces(char *line);
+static int parseline(char *line, char **v);
+
+static void
+cleanwhitespaces(char *line)
+{
+ char *i;
+
+ for (i = line; *i != 0; i++)
+ if (*i == '\t' || *i == '\n')
+ *i = ' ';
+}
+
+/* This function assumes that **v is large enough */
+static int
+parseline(char *line, char **v)
+{
+ char *t;
+ int n = 0;
+
+ cleanwhitespaces(line);
+
+ for (t = strtok(line, " "); t != NULL; t = strtok(NULL, " "))
+ strcpy(v[n++], t);
+
+ return n;
+}
+
+void
+exec_args(int c, char **v)
+{
+ int i;
+ Command *cmd = NULL;
+ CommandArgs *args;
+
+ for (i = 0; i < NCOMMANDS; i++)
+ if (commands[i] != NULL && !strcmp(v[0], commands[i]->name))
+ cmd = commands[i];
+
+ if (cmd == NULL) {
+ fprintf(stderr, "%s: command not found\n", v[0]);
+ return;
+ }
+
+ args = cmd->parse_args(c-1, &v[1]);
+ if (!args->success) {
+ fprintf(stderr, "usage: %s\n", cmd->usage);
+ return;
+ }
+
+ cmd->exec(args);
+ free_args(args);
+}
+
+void
+launch()
+{
+ int i, shell_argc;
+ char line[MAXLINELEN], **shell_argv;
+
+ shell_argv = malloc(MAXNTOKENS * sizeof(char *));
+ for (i = 0; i < MAXNTOKENS; i++)
+ shell_argv[i] = malloc((MAXTOKENLEN+1) * sizeof(char));
+
+ fprintf(stderr, "Welcome to Nissy "VERSION".\n");
+ fprintf(stderr, "Type 'help' for a list.\n");
+
+ while (true) {
+ fprintf(stderr, "nissy-# ");
+ if (fgets(line, MAXLINELEN, stdin) == NULL)
+ break;
+ shell_argc = parseline(line, shell_argv);
+ if (shell_argc > 0)
+ exec_args(shell_argc, shell_argv);
+ }
+
+ for (i = 0; i < MAXNTOKENS; i++)
+ free(shell_argv[i]);
+ free(shell_argv);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc > 1)
+ exec_args(argc-1, &argv[1]);
+ else
+ launch();
+
+ return 0;
+}
diff --git a/src/shell.h b/src/shell.h
@@ -0,0 +1,13 @@
+#ifndef SHELL_H
+#define SHELL_H
+
+#include "commands.h"
+
+#define MAXLINELEN 10000
+#define MAXTOKENLEN 255
+#define MAXNTOKENS 255
+
+void exec_args(int c, char **v);
+void launch();
+
+#endif
diff --git a/src/solve.c b/src/solve.c
@@ -0,0 +1,173 @@
+#include "solve.h"
+
+/* Local functions ***********************************************************/
+
+static bool allowed_next(Move move, DfsData *dd);
+static void dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+static void dfs_branch(Cube c, Step *s, SolveOptions *os, DfsData *dd);
+static bool dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd);
+static void dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+static bool dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd);
+
+/* Local functions ***********************************************************/
+
+static bool
+allowed_next(Move move, DfsData *dd)
+{
+ if (!possible_next(dd->last2, dd->last1, move))
+ return false;
+
+ if (commute(dd->last1, move))
+ return dd->move_position[dd->last1] < dd->move_position[move];
+
+ return true;
+}
+
+static void
+dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ if (dfs_stop(c, s, opts, dd))
+ return;
+
+ if (dfs_check_solved(s, opts, dd))
+ return;
+
+ dfs_branch(c, s, opts, dd);
+
+ if (opts->can_niss && !dd->niss)
+ dfs_niss(c, s, opts, dd);
+}
+
+static void
+dfs_branch(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves;
+
+ int i, maxnsol = opts->max_solutions;
+
+ for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) {
+ m = moves[i];
+ if (allowed_next(m, dd)) {
+ dd->last2 = dd->last1;
+ dd->last1 = m;
+ append_move(dd->current_alg, m, dd->niss);
+
+ dfs(apply_move(m, c), s, opts, dd);
+
+ dd->current_alg->len--;
+ dd->last2 = l2;
+ dd->last1 = l1;
+ }
+ }
+}
+
+static bool
+dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd)
+{
+ if (dd->lb != 0)
+ return false;
+
+ if (dd->current_alg->len == dd->d) {
+ if (s->is_valid(dd->current_alg) || opts->all)
+ append_alg(dd->sols, dd->current_alg);
+
+ if (opts->verbose)
+ print_alg(dd->current_alg, false);
+ }
+
+ return true;
+}
+
+static void
+dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ Move l1 = dd->last1, l2 = dd->last2;
+ CubeTarget ct;
+
+ ct.cube = apply_move(inverse_move(l1), (Cube){0});
+ ct.target = 1;
+
+ if (dd->current_alg->len == 0 || s->estimate(ct)) {
+ dd->niss = true;
+ dd->last1 = NULLMOVE;
+ dd->last2 = NULLMOVE;
+
+ dfs(inverse_cube(c), s, opts, dd);
+
+ dd->last1 = l1;
+ dd->last2 = l2;
+ dd->niss = false;
+ }
+}
+
+static bool
+dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd)
+{
+ CubeTarget ct = {
+ .cube = c,
+ .target = dd->d - dd->current_alg->len
+ };
+
+ if (dd->sols->len >= opts->max_solutions)
+ return true;
+
+ dd->lb = s->estimate(ct);
+ if (opts->can_niss && !dd->niss)
+ dd->lb = MIN(1, dd->lb);
+
+ if (dd->current_alg->len + dd->lb > dd->d)
+ return true;
+
+ return false;
+}
+
+/* Public functions **********************************************************/
+
+AlgList *
+solve(Cube cube, Step *step, SolveOptions *opts)
+{
+ AlgListNode *node;
+ AlgList *sols = new_alglist();
+ Cube c;
+
+ if (step->detect != NULL)
+ step->pre_trans = step->detect(cube);
+ c = apply_trans(step->pre_trans, cube);
+
+ DfsData dd = {
+ .m = 0,
+ .niss = false,
+ .lb = -1,
+ .last1 = NULLMOVE,
+ .last2 = NULLMOVE,
+ .sols = sols,
+ .current_alg = new_alg("")
+ };
+
+ if (step->ready != NULL && !step->ready(c)) {
+ fprintf(stderr, "Cube not ready for solving step: ");
+ fprintf(stderr, "%s\n", step->ready_msg);
+ return sols;
+ }
+
+ moveset_to_list(step->moveset, dd.sorted_moves);
+ movelist_to_position(dd.sorted_moves, dd.move_position);
+
+ for (dd.d = opts->min_moves;
+ dd.d <= opts->max_moves &&
+ !(sols->len && opts->optimal_only) &&
+ sols->len < opts->max_solutions;
+ dd.d++) {
+ if (opts->verbose)
+ fprintf(stderr,
+ "Found %d solutions, searching depth %d...\n",
+ sols->len, dd.d);
+ dfs(c, step, opts, &dd);
+ }
+
+ for (node = sols->first; node != NULL; node = node->next)
+ transform_alg(inverse_trans(step->pre_trans), node->alg);
+
+ free_alg(dd.current_alg);
+ return sols;
+}
diff --git a/src/solve.h b/src/solve.h
@@ -0,0 +1,9 @@
+#ifndef SOLVE_H
+#define SOLVE_H
+
+#include "moves.h"
+#include "trans.h"
+
+AlgList * solve(Cube cube, Step *step, SolveOptions *opts);
+
+#endif
diff --git a/src/solver.c b/src/solver.c
@@ -1,1058 +0,0 @@
-#include <stdint.h>
-#include <stdio.h>
-
-#include "utils.h"
-#include "coordinates.h"
-#include "moves.h"
-#include "io.h"
-#include "pruning_tables.h"
-
-/* Applies inverse of moves, inverse of prev_moves and then inverse of scramble
- * and returns a coordinate determined by t_table. */
-int premoves_inverse(int moves[30], int scramble[], int prev_moves[30],
- int t_table[][19]) {
- int nprevmoves, nmoves, nscramble, coord = 0;
-
- for (nmoves = 0; moves[nmoves]; nmoves++);
- for (nprevmoves = 0; prev_moves[nprevmoves]; nprevmoves++);
- for (nscramble = 0; scramble[nscramble]; nscramble++);
-
- for (int i = nmoves - 1; i >= 0; i--)
- coord = t_table[coord][inverse_move[moves[i]]];
- for (int i = nprevmoves - 1; i >= 0; i--)
- coord = t_table[coord][inverse_move[prev_moves[i]]];
- for (int i = nscramble - 1; i >= 0; i--)
- coord = t_table[coord][inverse_move[scramble[i]]];
-
- return coord;
-}
-
-
-/******/
-/* EO */
-/******/
-void niss_eo_dfs(int eo, int scramble[], int eo_list[][30], int *eo_count,
- int t_table[pow2to11][19], int p_table[pow2to11],
- int last1, int last2, int moves, int m, int d, int niss,
- int can_use_niss, int hide) {
-
-
- if (*eo_count >= m || moves > d ||
- ((!can_use_niss || niss) && moves + p_table[eo] > d))
- return;
-
- eo_list[*eo_count][moves] = 0;
-
- if (eo == 0) {
- /* If an early EO is found, or if "case F2 B", or if hide is on. */
- if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) ||
- (hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- return;
- /* Copy moves for the next solution */
- if (*eo_count < m - 1)
- copy_moves(eo_list[*eo_count], eo_list[(*eo_count)+1]);
- (*eo_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- eo_list[*eo_count][moves] = niss ? -i : i;
- niss_eo_dfs(t_table[eo][i], scramble, eo_list, eo_count, t_table,
- p_table, i, last1, moves+1, m, d, niss,
- can_use_niss, hide);
- }
- }
-
- if (*eo_count >= m)
- return;
- eo_list[*eo_count][moves] = 0;
-
- /* If not nissing already and we either have not done any move yet or
- * the last move was F/F' etc, and if I am allowed to niss, try niss! */
- if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss &&
- !(hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) {
- int aux[] = {0,0};
- niss_eo_dfs(premoves_inverse(eo_list[*eo_count], scramble, aux, t_table),
- scramble, eo_list, eo_count, t_table, p_table,
- 0, 0, moves, m, d, 1, can_use_niss, hide);
- }
-}
-
-int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud,
- int m, int b, int niss, int h) {
-
- init_small_pruning_tables();
-
- int n = 0, eofb = 0, eorl = 0, eoud = 0;
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
- }
- for (int i = 0; i <= b; i++) {
- if (fb)
- niss_eo_dfs(eofb, scram, eo_list, &n, eofb_transition_table,
- eofb_pruning_table, 0, 0, 0, m, i, 0, niss, h);
- if (rl)
- niss_eo_dfs(eorl, scram, eo_list, &n, eorl_transition_table,
- eorl_pruning_table, 0, 0, 0, m, i, 0, niss, h);
- if (ud)
- niss_eo_dfs(eoud, scram, eo_list, &n, eoud_transition_table,
- eoud_pruning_table, 0, 0, 0, m, i, 0, niss, h);
- }
- return n;
-}
-
-
-/******/
-/* CO */
-/******/
-void niss_co_dfs(int co, int scramble[], int co_list[][30], int *co_count,
- int t_table[pow3to7][19], int p_table[pow3to7],
- int last1, int last2, int moves, int m, int d, int niss,
- int can_use_niss, int hide, int ignore) {
-
-
- if (*co_count >= m || moves > d ||
- ((!can_use_niss || niss) && ((!ignore && moves + p_table[co] > d) ||
- ( ignore && moves + p_table[co] - 2 > d))))
- return;
-
- co_list[*co_count][moves] = 0;
-
- if (co == 0 || (ignore && ( t_table[t_table[co][F]][B] == 0 ||
- t_table[t_table[co][R]][L] == 0 ||
- t_table[t_table[co][U]][D] == 0 ))) {
- /* If an early CO is found, or if "case F2 B", or if hide is on. */
- if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) ||
- (hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- return;
- /* Copy moves for the next solution */
- if (*co_count < m - 1)
- copy_moves(co_list[*co_count], co_list[(*co_count)+1]);
- (*co_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- co_list[*co_count][moves] = niss ? -i : i;
- niss_co_dfs(t_table[co][i], scramble, co_list, co_count, t_table,
- p_table, i, last1, moves+1, m, d, niss,
- can_use_niss, hide, ignore);
- }
- }
-
- if (*co_count >= m)
- return;
- co_list[*co_count][moves] = 0;
-
- /* If not nissing already and we either have not done any move yet or
- * the last move was F/F' etc, and if I am allowed to niss, try niss! */
- if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss &&
- !(hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) {
- int aux[] = {0,0};
- niss_co_dfs(premoves_inverse(co_list[*co_count], scramble, aux, t_table),
- scramble, co_list, co_count, t_table, p_table,
- 0, 0, moves, m, d, 1, can_use_niss, hide, ignore);
- }
-}
-
-int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud,
- int m, int b, int niss, int h, int ignore) {
-
- init_small_pruning_tables();
-
- int n = 0, cofb = 0, corl = 0, coud = 0;
- for (int i = 0; scram[i]; i++) {
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
- }
- for (int i = 0; i <= b; i++) {
- if (fb)
- niss_co_dfs(cofb, scram, co_list, &n, cofb_transition_table,
- cofb_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore);
- if (rl)
- niss_co_dfs(corl, scram, co_list, &n, corl_transition_table,
- corl_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore);
- if (ud)
- niss_co_dfs(coud, scram, co_list, &n, coud_transition_table,
- coud_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore);
- }
- return n;
-}
-
-
-/**************/
-/* DR from EO */
-/**************/
-
-
-/* Scramble includes premoves for previous EO */
-void niss_dr_from_eo_dfs(int co, int epos, int scramble[], int eo_moves[30],
- int dr_list[][30], int *dr_count,
- int co_t_table[pow3to7][19],
- int epos_t_table[binom12on4][19],
- int8_t p_table[pow3to7][binom12on4], int mask,
- int last1, int last2, int last1_inv, int last2_inv,
- int moves, int m, int d, int niss,
- int can_use_niss, int hide) {
-
- if (*dr_count >= m || moves > d ||
- ((!can_use_niss || niss) && moves + p_table[co][epos] > d))
- return;
-
- dr_list[*dr_count][moves] = 0;
-
- if (co == 0 && epos == 0) {
- if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) ||
- (hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- return;
- /* Copy moves for the next solution */
- if (*dr_count < m - 1)
- copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]);
- (*dr_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i) & mask) {
- dr_list[*dr_count][moves] = niss ? -i : i;
- niss_dr_from_eo_dfs(co_t_table[co][i], epos_t_table[epos][i],
- scramble, eo_moves, dr_list, dr_count,
- co_t_table, epos_t_table, p_table, mask,
- i, last1, last1_inv, last2_inv,
- moves+1, m, d, niss, can_use_niss, hide);
- }
- }
-
- if (*dr_count >= m)
- return;
- dr_list[*dr_count][moves] = 0;
-
- /* If not nissing already and we either have not done any move yet or
- * the last move was F/F' etc and I am allowed to niss, try niss! */
- if (!niss && (last1 == 0 || co_t_table[0][last1] != 0) && can_use_niss &&
- !(hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- niss_dr_from_eo_dfs(premoves_inverse(dr_list[*dr_count], scramble,
- eo_moves, co_t_table),
- premoves_inverse(dr_list[*dr_count], scramble,
- eo_moves, epos_t_table),
- scramble, eo_moves, dr_list, dr_count,
- co_t_table, epos_t_table, p_table,
- mask, last1_inv, last2_inv, 0, 0,
- moves, m, d, 1, can_use_niss, hide);
-}
-
-int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb,
- int rl, int ud, int m, int b, int niss, int hide) {
-
- init_drfromeo_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int epose = 0, eposm = 0, eposs = 0;
- int coud = 0, corl = 0, cofb = 0;
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
-
- epose = epose_transition_table[epose][scram[i]];
- eposm = eposm_transition_table[eposm][scram[i]];
- eposs = eposs_transition_table[eposs][scram[i]];
- }
-
- int fake_eom[2] = {0, 0}; /* Fake EO moves */
-
- if (from == 1) {
- if (eofb)
- return -1;
- for (int i = 0; i <= b; i++) {
- if (ud)
- niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n,
- coud_transition_table, epose_transition_table,
- coud_epose_from_eofb_pruning_table, move_mask_eofb,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- if (rl)
- niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n,
- corl_transition_table, eposm_transition_table,
- corl_eposm_from_eofb_pruning_table, move_mask_eofb,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else if (from == 2) {
- if (eorl)
- return -1;
- for (int i = 0; i <= b; i++) {
- if (fb)
- niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n,
- cofb_transition_table, eposs_transition_table,
- cofb_eposs_from_eorl_pruning_table, move_mask_eorl,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- if (ud)
- niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n,
- coud_transition_table, epose_transition_table,
- coud_epose_from_eorl_pruning_table, move_mask_eorl,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else if (from == 3) {
- if (eoud)
- return -1;
- for (int i = 0; i <= b; i++) {
- if (rl)
- niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n,
- corl_transition_table, eposm_transition_table,
- corl_eposm_from_eoud_pruning_table, move_mask_eoud,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- if (fb)
- niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n,
- cofb_transition_table, eposs_transition_table,
- cofb_eposs_from_eoud_pruning_table, move_mask_eoud,
- 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else {
- return -1;
- }
- return n;
-}
-
-
-/***************/
-/* HTR from DR */
-/***************/
-
-/* Scramble includes premoves for previous DR */
-void niss_htr_from_dr_dfs(int cp, int eo3, int scramble[], int eodr_moves[30],
- int htr_list[][30], int *htr_count,
- int eo3_t_table[pow2to11][19],
- int cp_to_htr_pruning_table[factorial8],
- int cp_htr_pruning_table[factorial8],
- int cp_finish_pruning_table[factorial8],
- int mask, int last1, int last2,
- int last1_inv, int last2_inv, int moves,
- int m, int d, int niss,
- int can_use_niss, int hide) {
-
- if (*htr_count >= m || moves > d ||
- ((!can_use_niss || niss) && moves + cp_to_htr_pruning_table[cp] > d) ||
- moves + cp_finish_pruning_table[cp] - 4 > d)
- return;
-
- htr_list[*htr_count][moves] = 0;
-
- if ((cp == 0 || cp_htr_pruning_table[cp]) && eo3 == 0) {
- if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) ||
- (hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- return;
- /* Copy moves for the next solution */
- if (*htr_count < m - 1)
- copy_moves(htr_list[*htr_count], htr_list[(*htr_count)+1]);
- (*htr_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i) & mask) {
- htr_list[*htr_count][moves] = niss ? -i : i;
- niss_htr_from_dr_dfs(cp_transition_table[cp][i], eo3_t_table[eo3][i],
- scramble, eodr_moves, htr_list, htr_count,
- eo3_t_table, cp_to_htr_pruning_table,
- cp_htr_pruning_table, cp_finish_pruning_table,
- mask, i, last1, last1_inv, last2_inv,
- moves+1, m, d, niss, can_use_niss, hide);
- }
- }
-
- if (*htr_count >= m)
- return;
- htr_list[*htr_count][moves] = 0;
-
- /* If not nissing already and we either have not done any move yet or
- * the last move was a quarter turn and I am allowed to niss, try niss! */
- if (!niss && last1 % 3 != 2 && can_use_niss &&
- !(hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- niss_htr_from_dr_dfs(premoves_inverse(htr_list[*htr_count], scramble,
- eodr_moves, cp_transition_table),
- premoves_inverse(htr_list[*htr_count], scramble,
- eodr_moves, eo3_t_table),
- scramble, eodr_moves, htr_list, htr_count,
- eo3_t_table, cp_to_htr_pruning_table,
- cp_htr_pruning_table, cp_finish_pruning_table,
- mask, last1_inv, last2_inv,
- 0, 0, moves, m, d, 1, can_use_niss, hide);
-}
-
-int htr_scram_spam(int scram[], int htr_list[][30], int from,
- int m, int b, int niss, int hide) {
-
- init_small_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int coud = 0, corl = 0, cofb = 0;
- int cp = 0;
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
-
- cp = cp_transition_table[cp][scram[i]];
- }
-
- int fake_drm[2] = {0, 0}; /* Fake DR moves */
-
- if ((from == 1 || from == 0) && (!eofb && !eorl && !coud)) {
- for (int i = 0; i <= b; i++) {
- niss_htr_from_dr_dfs(cp, eoud, scram, fake_drm, htr_list, &n,
- eoud_transition_table, cpud_to_htr_pruning_table,
- cp_htr_pruning_table, cp_drud_pruning_table,
- move_mask_drud, 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb)) {
- for (int i = 0; i <= b; i++) {
- niss_htr_from_dr_dfs(cp, eofb, scram, fake_drm, htr_list, &n,
- eofb_transition_table, cpfb_to_htr_pruning_table,
- cp_htr_pruning_table, cp_drfb_pruning_table,
- move_mask_drfb, 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else if ((from == 3 || from == 0) && (!eoud && !eofb && !corl)) {
- for (int i = 0; i <= b; i++) {
- niss_htr_from_dr_dfs(cp, eorl, scram, fake_drm, htr_list, &n,
- eorl_transition_table, cprl_to_htr_pruning_table,
- cp_htr_pruning_table, cp_drrl_pruning_table,
- move_mask_drrl, 0, 0, 0, 0, 0, m, i, 0, niss, hide);
- }
- } else {
- return -1;
- }
- return n;
-}
-
-
-/***********************/
-/* Direct DR (no NISS) */
-/***********************/
-void dr_dfs(int eo, int eo2, int eslice, int co,
- int dr_list[][30], int *dr_count,
- int eo_t_table[pow2to11][19], int eo2_t_table[pow2to11][19],
- int eslice_t_table[binom12on4][19], int co_t_table[pow3to7][19],
- int8_t eo_eslice_p_table[pow2to11][binom12on4],
- int8_t eo_co_p_table[pow2to11][pow3to7],
- int8_t eo2_co_p_table[pow2to11][pow3to7],
- int last1, int last2, int moves, int max_sol,
- int depth, int hide) {
- if (*dr_count >= max_sol || moves + eo_eslice_p_table[eo][eslice] > depth ||
- moves + eo_co_p_table[eo][co] > depth ||
- moves + eo2_co_p_table[eo2][co] > depth)
- return;
-
- dr_list[*dr_count][moves] = 0;
-
- if (eo == 0 && eslice == 0 && co == 0) {
- /* If an early DR is found, or if "case R2 L". */
- if (moves != depth || (parallel(last1, last2) && last2 % 3 == 2) ||
- (hide && moves > 0 &&
- (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0))))
- return;
- /* Copy moves for the next solution */
- if (*dr_count < max_sol - 1)
- copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]);
- (*dr_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- dr_list[*dr_count][moves] = i;
- dr_dfs(eo_t_table[eo][i], eo2_t_table[eo2][i],
- eslice_t_table[eslice][i], co_t_table[co][i],
- dr_list, dr_count,
- eo_t_table, eo2_t_table, eslice_t_table, co_t_table,
- eo_eslice_p_table, eo_co_p_table, eo2_co_p_table,
- i, last1, moves+1, max_sol, depth, hide);
- }
- }
-}
-
-int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud,
- int m, int b, int h) {
-
- init_directdr_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int epose = 0, eposm = 0, eposs = 0;
- int coud = 0, corl = 0, cofb = 0;
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
-
- epose = epose_transition_table[epose][scram[i]];
- eposm = eposm_transition_table[eposm][scram[i]];
- eposs = eposs_transition_table[eposs][scram[i]];
- }
-
- for (int i = 0; i <= b; i++) {
- if (ud)
- dr_dfs(eofb, eorl, epose, coud, dr_list, &n,
- eofb_transition_table, eorl_transition_table,
- epose_transition_table, coud_transition_table,
- eofb_epose_pruning_table, eofb_coud_pruning_table,
- eorl_coud_pruning_table, 0, 0, 0, m, i, h);
- if (fb)
- dr_dfs(eorl, eoud, eposs, cofb, dr_list, &n,
- eorl_transition_table, eoud_transition_table,
- eposs_transition_table, cofb_transition_table,
- eorl_eposs_pruning_table, eorl_cofb_pruning_table,
- eoud_cofb_pruning_table, 0, 0, 0, m, i, h);
- if (rl)
- dr_dfs(eoud, eofb, eposm, corl, dr_list, &n,
- eoud_transition_table, eofb_transition_table,
- eposm_transition_table, corl_transition_table,
- eoud_eposm_pruning_table, eoud_corl_pruning_table,
- eofb_corl_pruning_table, 0, 0, 0, m, i, h);
- }
- return n;
-}
-
-
-/*************/
-/* DR finish */
-/*************/
-void dr_finish_dfs(int cp, int ep8, int ep4, int sol[][30], int *sol_count,
- int ep8_t_table[factorial8][19],
- int ep4_t_table[factorial4][19],
- int cp_p_table[factorial8],
- int ep8_p_table[factorial8],
- int mask, int last1, int last2, int moves, int m, int d) {
-
-
- if (*sol_count >= m || moves + cp_p_table[cp] > d ||
- moves + ep8_p_table[ep8] > d)
- return;
-
- sol[*sol_count][moves] = 0;
-
- if (cp == 0 && ep8 == 0 && ep4 == 0) {
- if (moves != d)
- return;
- /* Copy moves for the next solution */
- if (*sol_count < m - 1)
- copy_moves(sol[*sol_count], sol[(*sol_count)+1]);
- (*sol_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i) & mask) {
- sol[*sol_count][moves] = i;
- dr_finish_dfs(cp_transition_table[cp][i], ep8_t_table[ep8][i],
- ep4_t_table[ep4][i], sol, sol_count,
- ep8_t_table, ep4_t_table,
- cp_p_table, ep8_p_table,
- mask, i, last1, moves+1, m, d);
- }
- }
-
- return;
-}
-
-int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b) {
-
- init_small_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int coud = 0, corl = 0, cofb = 0;
- int cp = 0;
- int ep[12];
- ep_int_to_array(0, ep);
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
-
- cp = cp_transition_table[cp][scram[i]];
- apply_move_ep_array(scram[i], ep);
- }
-
- if ((from == 1 && (eofb || eorl || coud)) ||
- (from == 2 && (eorl || eoud || cofb)) ||
- (from == 3 && (eoud || eofb || corl)) ||
- ((eofb || eorl || coud) && (eorl || eoud ||cofb) && (eoud ||eofb || corl)))
- return -1;
-
- for (int i = 0; i <= b; i++) {
- if ((from == 1 || from == 0) && (!eofb && !eorl && !coud))
- dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep),
- sol, &n, epud_transition_table, epe_transition_table,
- cp_drud_pruning_table, epud_pruning_table,
- move_mask_drud, 0, 0, 0, m, i);
- if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb))
- dr_finish_dfs(cp, epfb_array_to_int(ep), eps_array_to_int(ep),
- sol, &n, epfb_transition_table, eps_transition_table,
- cp_drfb_pruning_table, epfb_pruning_table,
- move_mask_drfb, 0, 0, 0, m, i);
- if ((from == 3 || from == 0) && (!eoud && !eofb && !corl))
- dr_finish_dfs(cp, eprl_array_to_int(ep), epm_array_to_int(ep),
- sol, &n, eprl_transition_table, epm_transition_table,
- cp_drrl_pruning_table, eprl_pruning_table,
- move_mask_drrl, 0, 0, 0, m, i);
- }
-
- return n;
-}
-
-int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b) {
-
- init_small_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int coud = 0, cp = 0;
- int ep[12];
- ep_int_to_array(0, ep);
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- coud = coud_transition_table[coud][scram[i]];
-
- cp = cp_transition_table[cp][scram[i]];
- apply_move_ep_array(scram[i], ep);
- }
-
- if (eofb || eorl || eoud || coud || cpud_to_htr_pruning_table[cp] != 0)
- return -1;
-
- for (int i = 0; i <= b; i++)
- dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep),
- sol, &n, epud_transition_table, epe_transition_table,
- cp_drud_pruning_table, epud_pruning_table,
- move_mask_htr, 0, 0, 0, m, i);
-
- return n;
-}
-
-
-/**************/
-/* DR corners */
-/**************/
-void dr_corners_dfs(int cp, int sol[][30], int *sol_count,
- int cp_p_table[factorial8], int mask, int last1, int last2,
- int moves, int m, int d, int ignore) {
-
- if (*sol_count >= m || (!ignore && moves + cp_p_table[cp] > d) ||
- (ignore && moves + cp_p_table[cp] - 2 > d))
- return;
-
-
- sol[*sol_count][moves] = 0;
-
- if (cp == 0 ||
- (ignore && mask == move_mask_drud &&
- (cp_transition_table[cp_transition_table[cp][U]][D3] == 0 ||
- cp_transition_table[cp_transition_table[cp][U2]][D2] == 0 ||
- cp_transition_table[cp_transition_table[cp][U3]][D] == 0 )) ||
- (ignore && mask == move_mask_drfb &&
- (cp_transition_table[cp_transition_table[cp][F]][B3] == 0 ||
- cp_transition_table[cp_transition_table[cp][F2]][B2] == 0 ||
- cp_transition_table[cp_transition_table[cp][F3]][B] == 0 )) ||
- (ignore && mask == move_mask_drrl &&
- (cp_transition_table[cp_transition_table[cp][R]][L3] == 0 ||
- cp_transition_table[cp_transition_table[cp][R2]][L2] == 0 ||
- cp_transition_table[cp_transition_table[cp][R3]][L] == 0 ))
- ) {
- if (moves != d)
- return;
- /* Copy moves for the next solution */
- if (*sol_count < m - 1)
- copy_moves(sol[*sol_count], sol[(*sol_count)+1]);
- (*sol_count)++;
- return;
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i) & mask) {
- sol[*sol_count][moves] = i;
- dr_corners_dfs(cp_transition_table[cp][i], sol, sol_count,
- cp_p_table, mask, i, last1, moves+1, m, d, ignore);
- }
- }
-}
-
-int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b,
- int ignore) {
-
- init_small_pruning_tables();
-
- int n = 0;
- int eofb = 0, eorl = 0, eoud = 0;
- int coud = 0, corl = 0, cofb = 0;
- int cp = 0;
-
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- coud = coud_transition_table[coud][scram[i]];
-
- cp = cp_transition_table[cp][scram[i]];
- }
-
- if ((from == 1 && coud) || (from == 2 && cofb) || (from == 3 && corl) ||
- (coud && cofb && corl))
- return -1;
-
- for (int i = 0; i <= b; i++) {
- if ((from == 1 || from == 0) && !coud)
- dr_corners_dfs(cp, sol, &n, cp_drud_pruning_table, move_mask_drud,
- 0, 0, 0, m, i, ignore);
- if ((from == 2 || from == 0) && !cofb)
- dr_corners_dfs(cp, sol, &n, cp_drfb_pruning_table, move_mask_drfb,
- 0, 0, 0, m, i, ignore);
- if ((from == 3 || from == 0) && !corl)
- dr_corners_dfs(cp, sol, &n, cp_drrl_pruning_table, move_mask_drrl,
- 0, 0, 0, m, i, ignore);
- }
-
- return n;
-}
-
-/***************/
-/* Full solver */
-/***************/
-
-int is_ep_solved(int ep, int moves[30]) {
- int ep_arr[12];
- ep_int_to_array(ep, ep_arr);
- for (int i = 0; moves[i]; i++)
- apply_move_ep_array(moves[i], ep_arr);
- return !ep_array_to_int(ep_arr);
-}
-
-/* Solves directly using only small tables. Suitable for short solutions. */
-void small_optimal_dfs(int eofb, int eorl, int eoud, int ep,
- int coud, int cofb, int corl, int cp,
- int sol[][30], int *sol_count, int last1, int last2,
- int moves, int m, int d) {
- if (moves + eofb_pruning_table[eofb] > d ||
- moves + eorl_pruning_table[eorl] > d ||
- moves + eoud_pruning_table[eoud] > d ||
- moves + coud_pruning_table[coud] > d ||
- moves + cofb_pruning_table[cofb] > d ||
- moves + corl_pruning_table[corl] > d ||
- moves + cp_pruning_table[cp] > d ||
- *sol_count >= m)
- return;
-
- sol[*sol_count][moves] = 0;
-
- if (eofb == 0 && coud == 0 && cp == 0) {
- if (is_ep_solved(ep, sol[*sol_count])) {
- if (moves != d)
- return;
- if (*sol_count < m - 1)
- copy_moves(sol[*sol_count], sol[(*sol_count)+1]);
- (*sol_count)++;
- return;
- }
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- sol[*sol_count][moves] = i;
- small_optimal_dfs(eofb_transition_table[eofb][i],
- eorl_transition_table[eorl][i],
- eoud_transition_table[eoud][i], ep,
- coud_transition_table[coud][i],
- cofb_transition_table[cofb][i],
- corl_transition_table[corl][i],
- cp_transition_table[cp][i],
- sol, sol_count, i, last1, moves+1, m, d);
- }
- }
-}
-
-/* Solves directly using only medium tables. Suitable for short solutions.
-void medium_optimal_dfs(int eofb, int eorl, int eoud,
- int epose, int eposs, int eposm, int ep,
- int coud, int cofb, int corl, int cp,
- int sol[][30], int *sol_count, int last1, int last2,
- int moves, int m, int d) {
- if (moves + eofb_epose_pruning_table[eofb][epose] > d ||
- moves + eorl_eposs_pruning_table[eorl][eposs] > d ||
- moves + eoud_eposm_pruning_table[eoud][eposm] > d ||
- moves + eofb_coud_pruning_table[eofb][coud] > d ||
- moves + eofb_corl_pruning_table[eofb][corl] > d ||
- moves + eorl_coud_pruning_table[eorl][coud] > d ||
- moves + eorl_cofb_pruning_table[eorl][cofb] > d ||
- moves + eoud_cofb_pruning_table[eoud][cofb] > d ||
- moves + eoud_corl_pruning_table[eoud][corl] > d ||
- moves + cp_pruning_table[cp] > d ||
- *sol_count >= m)
- return;
-
- sol[*sol_count][moves] = 0;
-
- if (eofb == 0 && coud == 0 && cp == 0) {
- if (is_ep_solved(ep, sol[*sol_count])) {
- if (moves != d)
- return;
- if (*sol_count < m - 1)
- copy_moves(sol[*sol_count], sol[(*sol_count)+1]);
- (*sol_count)++;
- return;
- }
- }
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- sol[*sol_count][moves] = i;
- medium_optimal_dfs(eofb_transition_table[eofb][i],
- eorl_transition_table[eorl][i],
- eoud_transition_table[eoud][i],
- epose_transition_table[epose][i],
- eposs_transition_table[eposs][i],
- eposm_transition_table[eposm][i], ep,
- coud_transition_table[coud][i],
- cofb_transition_table[cofb][i],
- corl_transition_table[corl][i],
- cp_transition_table[cp][i],
- sol, sol_count, i, last1, moves+1, m, d);
- }
- }
-}
-*/
-
-/* Uses huge tables */
-int optimal_dfs(int ep, int cp, int eo, int co, int emslices,
- int sol[][30], int last1, int last2, int moves, int d) {
- if (moves + cp_co_pruning_table[cp][co] > d ||
- moves + triple_eo_pruning_table[eo][emslices] > d)
- return 0;
-
- sol[0][moves] = 0;
-
- /* If solved, no need to check the depth */
- if (cp == 0 && co == 0 && eo == 0 && emslices == 0)
- if (is_ep_solved(ep, sol[0]))
- return 1;
-
- for (int i = 1; i < 19; i++) {
- if (possible_next[last1][last2] & (1 << i)) {
- sol[0][moves] = i;
- if (optimal_dfs(ep, cp_transition_table[cp][i],
- eofb_transition_table[eo][i],
- coud_transition_table[co][i],
- emslices_transition_table[emslices][i],
- sol, i, last1, moves+1, d))
- return 1;
- }
- }
- return 0;
-}
-
-int solve_scram(int scram[], int sol[][30], int m, int b, int optimal) {
-
- /* Initialize pieces. */
- int eofb = 0, eorl = 0, eoud = 0, ep = 0;
- int epose = 0, eposs = 0, eposm = 0;
- int coud = 0, cofb = 0, corl = 0, cp = 0;
- int emslices = 0;
- for (int i = 0; scram[i]; i++) {
- eofb = eofb_transition_table[eofb][scram[i]];
- eorl = eorl_transition_table[eorl][scram[i]];
- eoud = eoud_transition_table[eoud][scram[i]];
-
- epose = epose_transition_table[epose][scram[i]];
- eposs = eposs_transition_table[eposs][scram[i]];
- eposm = eposm_transition_table[eposm][scram[i]];
-
- ep = apply_move_ep_int(scram[i], ep);
-
- coud = coud_transition_table[coud][scram[i]];
- cofb = cofb_transition_table[cofb][scram[i]];
- corl = corl_transition_table[corl][scram[i]];
- cp = cp_transition_table[cp][scram[i]];
-
- emslices = emslices_transition_table[emslices][scram[i]];
- }
-
- /* First we check if there are solutions of up to max_small moves. */
- int max_small = 10;
- int n = 0;
- init_small_pruning_tables();
- for (int i = 0; i <= min(b, max_small); i++) {
- small_optimal_dfs(eofb, eorl, eoud, ep, coud, cofb, corl, cp,
- sol, &n, 0, 0, 0, m, i);
- if (n > 0 && optimal)
- b = min(b, len(sol[0]));
- }
-
-
- if (n >= m || b <= 10)
- return n;
-
-
- /* If we found at least a solution, we return */
- if (n > 0)
- return n;
-
- /* Then we try a 2-step solver */
- int max_step1 = 100;
- int db = 12;
- int step1[max_step1+10][30];
- int ss[300], step2[2][30];
- int best = b+1;
-
- /* TODO maybe: for now, multiple solutions can be found only using the
- * short solver. */
-
- int n_step1 = dr_scram_spam(scram, step1, 1, 1, 1, max_step1, min(b, db), 0);
- for (int i = 0; i < n_step1; i++) {
- copy_moves(scram, ss);
- append_moves(step1[i], ss);
- if (dr_finish_scram_spam(ss, step2, 0, 1, min(best-1,b) - len(step1[i]))) {
- copy_moves(step1[i], sol[0]);
- append_moves(step2[0], sol[0]);
- best = len(sol[0]);
- }
- }
-
- /* If optimal solving was not required, or we have already found an optimal
- * solution, we return. */
- if (best <= len(step1[n_step1-1]) || !optimal)
- return best > b ? 0 : 1;
-
- /* Otherwise, we go on with the optimal solver. */
- int searched = len(step1[n_step1-1])-1;
-
- printf("Searched up to %d moves, no solution found.\n", searched);
- printf("Using huge pruning tables, if not loaded it might take a while.\n");
- init_huge_pruning_tables();
-
- for (int i = searched+1; i <= min(b, best-1); i++) {
- if (i >= 10)
- printf("Searching at depth %d.\n", i);
- if (optimal_dfs(ep, cp, eofb, coud, emslices, sol, 0, 0, 0, i)) {
- return 1;
- }
- }
- return best > b ? 0 : 1;
-}
-
-/* Given eofb, coud, ep and cp it finds a scramble that reaches that state.
- * It uses a simple 3-step solver to find a preliminary "solution", and then
- * gives this solutions as a scramble to a better solver (see above). */
-int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]) {
-
- int fake_count = 0, fake_scram[30];
- int eo_list[2][30], dr_list[2][30], finish_list[2][30];
-
- /* Convert ep to array */
- int ep_arr[12];
- ep_int_to_array(ep, ep_arr);
-
- /* Find EO */
- init_small_pruning_tables();
- for (int d = 0; d < 10; d++) {
- niss_eo_dfs(eofb, fake_scram, eo_list, &fake_count, eofb_transition_table,
- eofb_pruning_table, 0, 0, 0, 1, d, 0, 0, 0);
- if (fake_count) {
- fake_count = 0;
- break;
- }
- }
-
- /* Apply moves found, find epose */
- for (int i = 0; eo_list[0][i]; i++) {
- coud = coud_transition_table[coud][eo_list[0][i]];
- cp = cp_transition_table[cp][eo_list[0][i]];
- apply_move_ep_array(eo_list[0][i], ep_arr);
- }
- int epose = epose_array_to_int(ep_arr);
-
- /* Find DR */
- init_drfromeo_pruning_tables();
- for (int d = 0; d < 16; d++) {
- niss_dr_from_eo_dfs(coud, epose, fake_scram, fake_scram, dr_list,
- &fake_count, coud_transition_table,
- epose_transition_table,
- coud_epose_from_eofb_pruning_table, move_mask_eofb,
- 0, 0, 0, 0, 0, 1, d, 0, 0, 0);
- if (fake_count) {
- fake_count = 0;
- break;
- }
- }
-
- /* Apply moves found, find epud and epe */
- for (int i = 0; dr_list[0][i]; i++) {
- cp = cp_transition_table[cp][dr_list[0][i]];
- apply_move_ep_array(dr_list[0][i], ep_arr);
- }
- int epud = epud_array_to_int(ep_arr);
- int epe = epe_array_to_int(ep_arr);
-
- /* Find finish */
- init_small_pruning_tables();
- for (int d = 0; d < 16; d++) {
- dr_finish_dfs(cp, epud, epe, finish_list, &fake_count,
- epud_transition_table, epe_transition_table,
- cp_drud_pruning_table, epud_pruning_table, move_mask_drud,
- 0, 0, 0, 1, d);
- if (fake_count) {
- fake_count = 0;
- break;
- }
- }
-
- int scram[50];
-
- /* Debug */
- /*print_moves(eo_list[0]); printf("\n");
- print_moves(dr_list[0]); printf("\n");
- print_moves(finish_list[0]); printf("\n");*/
-
- copy_moves(eo_list[0], scram);
- append_moves(dr_list[0], scram);
- append_moves(finish_list[0], scram);
- return solve_scram(scram, sol, 1, 25, 0);
-}
diff --git a/src/solver.h b/src/solver.h
@@ -1,16 +0,0 @@
-int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud,
- int m, int b, int niss, int h);
-int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud,
- int m, int b, int niss, int h, int i);
-int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud,
- int m, int b, int h);
-int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb,
- int rl, int ud, int m, int b, int niss, int hide);
-int htr_scram_spam(int scram[], int htr_list[][30], int from,
- int m, int b, int niss, int hide);
-int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b,
- int ignore);
-int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b);
-int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b);
-int solve_scram(int scram[], int sol[][30], int m, int b, int optimal);
-int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]);
diff --git a/src/steps.c b/src/steps.c
@@ -0,0 +1,941 @@
+#include "steps.h"
+
+/* Checkers, estimators and validators ***************************************/
+
+static bool check_centers(Cube cube);
+static bool check_eofb(Cube cube);
+static bool check_drud(Cube cube);
+static bool check_htr(Cube cube);
+
+static int estimate_eoany_HTM(CubeTarget ct);
+static int estimate_eofb_HTM(CubeTarget ct);
+static int estimate_coany_HTM(CubeTarget ct);
+static int estimate_coud_HTM(CubeTarget ct);
+static int estimate_coany_URF(CubeTarget ct);
+static int estimate_coud_URF(CubeTarget ct);
+static int estimate_corners_HTM(CubeTarget ct);
+static int estimate_cornershtr_HTM(CubeTarget ct);
+static int estimate_corners_URF(CubeTarget ct);
+static int estimate_cornershtr_URF(CubeTarget ct);
+static int estimate_drany_HTM(CubeTarget ct);
+static int estimate_drud_HTM(CubeTarget ct);
+static int estimate_drud_eofb(CubeTarget ct);
+static int estimate_dr_eofb(CubeTarget ct);
+static int estimate_drudfin_drud(CubeTarget ct);
+static int estimate_htr_drud(CubeTarget ct);
+static int estimate_htrfin_htr(CubeTarget ct);
+static int estimate_optimal_HTM(CubeTarget ct);
+
+static bool always_valid(Alg *alg);
+static bool validate_singlecw_ending(Alg *alg);
+
+/* Pre-transformation detectors **********************************************/
+
+static Trans detect_pretrans_eofb(Cube cube);
+static Trans detect_pretrans_drud(Cube cube);
+
+/* Messages for when cube is not ready ***************************************/
+
+static char check_centers_msg[100] = "cube must be oriented (centers solved)";
+static char check_eo_msg[100] = "EO must be solved on given axis";
+static char check_dr_msg[100] = "DR must be solved on given axis";
+static char check_htr_msg[100] = "HTR must be solved";
+static char check_drany_msg[100] = "DR must be solved on at least one axis";
+
+/* Steps *********************************************************************/
+
+Step
+optimal_HTM = {
+ .shortname = "optimal",
+ .name = "Optimal solve (in HTM)",
+
+ .estimate = estimate_optimal_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+/* EO steps **************************/
+Step
+eoany_HTM = {
+ .shortname = "eo",
+ .name = "EO on any axis",
+
+ .estimate = estimate_eoany_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+eofb_HTM = {
+ .shortname = "eofb",
+ .name = "EO on F/B",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+eorl_HTM = {
+ .shortname = "eorl",
+ .name = "EO on R/L",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = ur,
+};
+
+Step
+eoud_HTM = {
+ .shortname = "eoud",
+ .name = "EO on U/D",
+
+ .estimate = estimate_eofb_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+/* CO steps **************************/
+Step
+coany_HTM = {
+ .shortname = "co",
+ .name = "CO on any axis",
+
+ .estimate = estimate_coany_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+coud_HTM = {
+ .shortname = "coud",
+ .name = "CO on U/D",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+corl_HTM = {
+ .shortname = "corl",
+ .name = "CO on R/L",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = rf,
+};
+
+Step
+cofb_HTM = {
+ .shortname = "cofb",
+ .name = "CO on F/B",
+
+ .estimate = estimate_coud_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+Step
+coany_URF = {
+ .shortname = "co-URF",
+ .name = "CO any axis (URF moveset)",
+
+ .estimate = estimate_coany_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+coud_URF = {
+ .shortname = "coud-URF",
+ .name = "CO on U/D (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+corl_URF = {
+ .shortname = "corl-URF",
+ .name = "CO on R/L (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = rf,
+};
+
+Step
+cofb_URF = {
+ .shortname = "cofb-URF",
+ .name = "CO on F/B (URF moveset)",
+
+ .estimate = estimate_coud_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = fd,
+};
+
+/* Misc corner steps *****************/
+Step
+cornershtr_HTM = {
+ .shortname = "chtr",
+ .name = "Solve corners to HTR state",
+
+ .estimate = estimate_cornershtr_HTM,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+cornershtr_URF = {
+ .shortname = "chtr-URF",
+ .name = "Solve corners to HTR state (URF moveset)",
+
+ .estimate = estimate_cornershtr_URF,
+ .ready = NULL,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+Step
+corners_HTM = {
+ .shortname = "corners",
+ .name = "Solve corners",
+
+ .estimate = estimate_corners_HTM,
+ .ready = NULL,
+ .is_valid = always_valid,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+corners_URF = {
+ .shortname = "corners-URF",
+ .name = "Solve corners (URF moveset)",
+
+ .estimate = estimate_corners_URF,
+ .ready = NULL,
+ .is_valid = always_valid,
+ .moveset = moveset_URF,
+
+ .pre_trans = uf,
+};
+
+/* DR steps **************************/
+Step
+drany_HTM = {
+ .shortname = "dr",
+ .name = "DR on any axis",
+
+ .estimate = estimate_drany_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+drud_HTM = {
+ .shortname = "drud",
+ .name = "DR on U/D",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = uf,
+};
+
+Step
+drrl_HTM = {
+ .shortname = "drrl",
+ .name = "DR on R/L",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = rf,
+};
+
+Step
+drfb_HTM = {
+ .shortname = "drfb",
+ .name = "DR on F/B",
+
+ .estimate = estimate_drud_HTM,
+ .ready = check_centers,
+ .ready_msg = check_centers_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_HTM,
+
+ .pre_trans = fd,
+};
+
+/* DR from EO */
+Step
+dr_eo = {
+ .shortname = "dr-eo",
+ .name = "DR without breaking EO (automatically detected)",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .detect = detect_pretrans_eofb,
+};
+
+Step
+dr_eofb = {
+ .shortname = "dr-eofb",
+ .name = "DR on U/D or R/L without breaking EO on F/B",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = uf,
+};
+
+Step
+dr_eorl = {
+ .shortname = "dr-eorl",
+ .name = "DR on U/D or F/B without breaking EO on R/L",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = ur,
+};
+
+Step
+dr_eoud = {
+ .shortname = "dr-eoud",
+ .name = "DR on R/L or F/B without breaking EO on U/R",
+
+ .estimate = estimate_dr_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fd,
+};
+
+Step
+drud_eofb = {
+ .shortname = "drud-eofb",
+ .name = "DR on U/D without breaking EO on F/B",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = uf,
+};
+
+Step
+drrl_eofb = {
+ .shortname = "drrl-eofb",
+ .name = "DR on R/L without breaking EO on F/B",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = rf,
+};
+
+Step
+drud_eorl = {
+ .shortname = "drud-eorl",
+ .name = "DR on U/D without breaking EO on R/L",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = ur,
+};
+
+Step
+drfb_eorl = {
+ .shortname = "drfb-eorl",
+ .name = "DR on F/B without breaking EO on R/L",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fr,
+};
+
+Step
+drfb_eoud = {
+ .shortname = "drfb-eoud",
+ .name = "DR on F/B without breaking EO on U/D",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = fd,
+};
+
+Step
+drrl_eoud = {
+ .shortname = "drrl-eoud",
+ .name = "DR on R/L without breaking EO on U/D",
+
+ .estimate = estimate_drud_eofb,
+ .ready = check_eofb,
+ .ready_msg = check_eo_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_eofb,
+
+ .pre_trans = rd,
+};
+
+/* DR finish steps */
+Step
+dranyfin_DR = {
+ .shortname = "drfin",
+ .name = "DR finish on any axis without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_drany_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .detect = detect_pretrans_drud,
+};
+
+Step
+drudfin_drud = {
+ .shortname = "drudfin",
+ .name = "DR finish on U/D without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = uf,
+};
+
+Step
+drrlfin_drrl = {
+ .shortname = "drrlfin",
+ .name = "DR finish on R/L without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = rf,
+};
+
+Step
+drfbfin_drfb = {
+ .shortname = "drfbfin",
+ .name = "DR finish on F/B without breaking DR",
+
+ .estimate = estimate_drudfin_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_drud,
+
+ .pre_trans = fd,
+};
+
+/* HTR from DR */
+Step
+htr_any = {
+ .shortname = "htr",
+ .name = "HTR from DR",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_drany_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .detect = detect_pretrans_drud,
+};
+
+Step
+htr_drud = {
+ .shortname = "htr-drud",
+ .name = "HTR from DR on U/D",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = uf,
+};
+
+Step
+htr_drrl = {
+ .shortname = "htr-drrl",
+ .name = "HTR from DR on R/L",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = rf,
+};
+
+Step
+htr_drfb = {
+ .shortname = "htr-drfb",
+ .name = "HTR from DR on F/B",
+
+ .estimate = estimate_htr_drud,
+ .ready = check_drud,
+ .ready_msg = check_dr_msg,
+ .is_valid = validate_singlecw_ending,
+ .moveset = moveset_drud,
+
+ .pre_trans = fd,
+};
+
+/* HTR finish */
+Step
+htrfin_htr = {
+ .shortname = "htrfin",
+ .name = "HTR finish without breaking HTR",
+
+ .estimate = estimate_htrfin_htr,
+ .ready = check_htr,
+ .ready_msg = check_htr_msg,
+ .is_valid = always_valid,
+ .moveset = moveset_htr,
+
+ .pre_trans = uf,
+};
+
+Step *steps[NSTEPS] = {
+ &optimal_HTM, /* first is default */
+
+ &eoany_HTM,
+ &eofb_HTM,
+ &eorl_HTM,
+ &eoud_HTM,
+
+ &coany_HTM,
+ &coud_HTM,
+ &corl_HTM,
+ &cofb_HTM,
+
+ &coany_URF,
+ &coud_URF,
+ &corl_URF,
+ &cofb_URF,
+
+ &drany_HTM,
+ &drud_HTM,
+ &drrl_HTM,
+ &drfb_HTM,
+
+ &dr_eo,
+ &dr_eofb,
+ &dr_eorl,
+ &dr_eoud,
+ &drud_eofb,
+ &drrl_eofb,
+ &drud_eorl,
+ &drfb_eorl,
+ &drfb_eoud,
+ &drrl_eoud,
+
+ &dranyfin_DR,
+ &drudfin_drud,
+ &drrlfin_drrl,
+ &drfbfin_drfb,
+
+ &htr_any,
+ &htr_drud,
+ &htr_drrl,
+ &htr_drfb,
+
+ &htrfin_htr,
+
+ &cornershtr_HTM,
+ &cornershtr_URF,
+ &corners_HTM,
+ &corners_URF,
+};
+
+/* Checkers, estimators and validators ***************************************/
+
+static bool
+check_centers(Cube cube)
+{
+ return cube.cpos == 0;
+}
+
+static bool
+check_eofb(Cube cube)
+{
+ return cube.eofb == 0;
+}
+
+static bool
+check_drud(Cube cube)
+{
+ return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0;
+}
+
+static bool
+check_htr(Cube cube)
+{
+ return check_drud(cube) && coord_htr_drud.index(cube) == 0;
+}
+
+static int
+estimate_eoany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_eofb_HTM, ct.cube);
+ r2 = ptableval(&pd_eofb_HTM, apply_trans(ur, ct.cube));
+ r3 = ptableval(&pd_eofb_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_eofb_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_eofb_HTM, ct.cube);
+}
+
+static int
+estimate_coany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_coud_HTM, ct.cube);
+ r2 = ptableval(&pd_coud_HTM, apply_trans(rf, ct.cube));
+ r3 = ptableval(&pd_coud_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_coud_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_coud_HTM, ct.cube);
+}
+
+static int
+estimate_coany_URF(CubeTarget ct)
+{
+ int r1, r2, r3;
+ CubeTarget ct2, ct3;
+
+ ct2.cube = apply_trans(rf, ct.cube);
+ ct2.target = ct.target;
+
+ ct3.cube = apply_trans(fd, ct.cube);
+ ct3.target = ct.target;
+
+ r1 = estimate_coud_URF(ct);
+ r2 = estimate_coud_URF(ct2);
+ r3 = estimate_coud_URF(ct3);
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_coud_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the orientation of
+ * the corner in DBL and use that as a reference */
+
+ CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target};
+ CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target};
+
+ int ud = estimate_coud_HTM(ct);
+ int rl = estimate_coud_HTM(ct2);
+ int fb = estimate_coud_HTM(ct3);
+
+ return MIN(ud, MIN(rl, fb));
+}
+
+static int
+estimate_corners_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_corners_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_cornershtr_HTM, ct.cube);
+}
+
+static int
+estimate_cornershtr_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_cornershtr_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_corners_URF(CubeTarget ct)
+{
+ /* TODO: I can improve this by checking first the corner in DBL
+ * and use that as a reference */
+
+ int c, ret = 15;
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++) {
+ ct.cube = apply_alg(rotation_alg(i), ct.cube);
+ c = estimate_corners_HTM(ct);
+ ret = MIN(ret, c);
+ }
+
+ return ret;
+}
+
+static int
+estimate_drany_HTM(CubeTarget ct)
+{
+ int r1, r2, r3;
+
+ r1 = ptableval(&pd_drud_sym16_HTM, ct.cube);
+ r2 = ptableval(&pd_drud_sym16_HTM, apply_trans(rf, ct.cube));
+ r3 = ptableval(&pd_drud_sym16_HTM, apply_trans(fd, ct.cube));
+
+ return MIN(r1, MIN(r2, r3));
+}
+
+static int
+estimate_drud_HTM(CubeTarget ct)
+{
+ return ptableval(&pd_drud_sym16_HTM, ct.cube);
+}
+
+static int
+estimate_drud_eofb(CubeTarget ct)
+{
+ return ptableval(&pd_drud_eofb, ct.cube);
+}
+
+static int
+estimate_dr_eofb(CubeTarget ct)
+{
+ int r1, r2;
+
+ r1 = ptableval(&pd_drud_eofb, ct.cube);
+ r2 = ptableval(&pd_drud_eofb, apply_trans(rf, ct.cube));
+
+ return MIN(r1, r2);
+}
+
+static int
+estimate_drudfin_drud(CubeTarget ct)
+{
+ int val = ptableval(&pd_drudfin_noE_sym16_drud, ct.cube);
+
+ if (val != 0)
+ return val;
+
+ return ct.cube.epose % 24 == 0 ? 0 : 1;
+}
+
+static int
+estimate_htr_drud(CubeTarget ct)
+{
+ return ptableval(&pd_htr_drud, ct.cube);
+}
+
+static int
+estimate_htrfin_htr(CubeTarget ct)
+{
+ return ptableval(&pd_htrfin_htr, ct.cube);
+}
+
+static int
+estimate_optimal_HTM(CubeTarget ct)
+{
+ int dr1, dr2, dr3, cor, ret;
+ Cube cube = ct.cube;
+
+ dr1 = ptableval(&pd_khuge_HTM, cube);
+ cor = estimate_corners_HTM(ct);
+ ret = MAX(dr1, cor);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(rf, ct.cube);
+ dr2 = ptableval(&pd_khuge_HTM, cube);
+ ret = MAX(ret, dr2);
+
+ if (ret > ct.target)
+ return ret;
+
+ cube = apply_trans(fd, ct.cube);
+ dr3 = ptableval(&pd_khuge_HTM, cube);
+
+ /* Michiel de Bondt's trick */
+ if (dr1 == dr2 && dr2 == dr3 && dr1 != 0)
+ dr3++;
+
+ return MAX(ret, dr3);
+}
+
+static bool
+always_valid(Alg *alg)
+{
+ return true;
+}
+
+static bool
+validate_singlecw_ending(Alg *alg)
+{
+ int i;
+ bool nor, inv;
+ Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE;
+
+ for (i = 0; i < alg->len; i++) {
+ if (alg->inv[i]) {
+ l2i = l1i;
+ l1i = alg->move[i];
+ } else {
+ l2 = l1;
+ l1 = alg->move[i];
+ }
+ }
+
+ nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2));
+ inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i));
+
+ return nor && inv;
+}
+
+/* Pre-transformation detectors **********************************************/
+
+static Trans
+detect_pretrans_eofb(Cube cube)
+{
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ if (check_eofb(apply_trans(i, cube)))
+ return i;
+
+ return 0;
+}
+
+static Trans
+detect_pretrans_drud(Cube cube)
+{
+ Trans i;
+
+ for (i = 0; i < NROTATIONS; i++)
+ if (check_drud(apply_trans(i, cube)))
+ return i;
+
+ return 0;
+}
diff --git a/src/steps.h b/src/steps.h
@@ -0,0 +1,10 @@
+#ifndef STEPS_H
+#define STEPS_H
+
+#include "pruning.h"
+
+#define NSTEPS 50
+
+extern Step * steps[NSTEPS];
+
+#endif
diff --git a/src/symcoord.c b/src/symcoord.c
@@ -0,0 +1,355 @@
+#include "symcoord.h"
+
+static Cube antindex_coud_sym16(uint64_t ind);
+static Cube antindex_cp_sym16(uint64_t ind);
+static Cube antindex_eofbepos_sym16(uint64_t ind);
+static Cube antindex_drud_sym16(uint64_t ind);
+static Cube antindex_drudfin_noE_sym16(uint64_t ind);
+static Cube antindex_khuge(uint64_t ind);
+
+static uint64_t index_coud_sym16(Cube cube);
+static uint64_t index_cp_sym16(Cube cube);
+static uint64_t index_eofbepos_sym16(Cube cube);
+static uint64_t index_drud_sym16(Cube cube);
+static uint64_t index_drudfin_noE_sym16(Cube cube);
+static uint64_t index_khuge(Cube cube);
+
+static void gensym(SymData *sd);
+static bool read_symdata_file(SymData *sd);
+static bool write_symdata_file(SymData *sd);
+
+/* Transformation groups and symmetry data ***********************************/
+
+static Trans
+trans_group_udfix[16] = {
+ uf, ur, ub, ul,
+ df, dr, db, dl,
+ uf_mirror, ur_mirror, ub_mirror, ul_mirror,
+ df_mirror, dr_mirror, db_mirror, dl_mirror,
+};
+
+static SymData
+sd_coud_16 = {
+ .filename = "sd_coud_16",
+ .coord = &coord_coud,
+ .sym_coord = &coord_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static SymData
+sd_cp_16 = {
+ .filename = "sd_cp_16",
+ .coord = &coord_cp,
+ .sym_coord = &coord_cp_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static SymData
+sd_eofbepos_16 = {
+ .filename = "sd_eofbepos_16",
+ .coord = &coord_eofbepos,
+ .sym_coord = &coord_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix
+};
+
+static int nsymdata = 3;
+static SymData * all_sd[] = {
+ &sd_coud_16,
+ &sd_cp_16,
+ &sd_eofbepos_16,
+};
+
+
+/* Coordinates and their implementation **************************************/
+
+Coordinate
+coord_eofbepos_sym16 = {
+ .index = index_eofbepos_sym16,
+ .cube = antindex_eofbepos_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_coud_sym16 = {
+ .index = index_coud_sym16,
+ .cube = antindex_coud_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_cp_sym16 = {
+ .index = index_cp_sym16,
+ .cube = antindex_cp_sym16,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drud_sym16 = {
+ .index = index_drud_sym16,
+ .cube = antindex_drud_sym16,
+ .max = POW3TO7 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_drudfin_noE_sym16 = {
+ .index = index_drudfin_noE_sym16,
+ .cube = antindex_drudfin_noE_sym16,
+ .max = FACTORIAL8 * 2768,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+Coordinate
+coord_khuge = {
+ .index = index_khuge,
+ .cube = antindex_khuge,
+ .max = POW3TO7 * FACTORIAL4 * 64430,
+ .ntrans = 16,
+ .trans = trans_group_udfix,
+};
+
+/* Functions *****************************************************************/
+
+static Cube
+antindex_coud_sym16(uint64_t ind)
+{
+ return sd_coud_16.rep[ind];
+}
+
+static Cube
+antindex_cp_sym16(uint64_t ind)
+{
+ return sd_cp_16.rep[ind];
+}
+
+static Cube
+antindex_eofbepos_sym16(uint64_t ind)
+{
+ return sd_eofbepos_16.rep[ind];
+}
+
+static Cube
+antindex_drud_sym16(uint64_t ind)
+{
+ Cube c;
+
+ c = antindex_eofbepos_sym16(ind/POW3TO7);
+ c.coud = ind % POW3TO7;
+ c.cofb = c.coud;
+ c.corl = c.coud;
+
+ return c;
+}
+
+static Cube
+antindex_drudfin_noE_sym16(uint64_t ind)
+{
+ Cube c1, c2;
+
+ c1 = coord_epud.cube(ind % FACTORIAL8);
+ c2 = antindex_cp_sym16(ind/FACTORIAL8);
+ c1.cp = c2.cp;
+
+ return c1;
+}
+
+static Cube
+antindex_khuge(uint64_t ind)
+{
+ Cube c;
+
+ c = antindex_eofbepos_sym16(ind/(FACTORIAL4*POW3TO7));
+ c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24);
+ c.coud = ind % POW3TO7;
+
+ return c;
+}
+
+static uint64_t
+index_coud_sym16(Cube cube)
+{
+ return sd_coud_16.class[coord_coud.index(cube)];
+}
+
+static uint64_t
+index_cp_sym16(Cube cube)
+{
+ return sd_cp_16.class[coord_cp.index(cube)];
+}
+
+static uint64_t
+index_drud_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)];
+ c = apply_trans(t, cube);
+
+ return index_eofbepos_sym16(c) * POW3TO7 + c.coud;
+}
+
+static uint64_t
+index_drudfin_noE_sym16(Cube cube)
+{
+ Trans t;
+ Cube c;
+
+ t = sd_cp_16.transtorep[coord_cp.index(cube)];
+ c = apply_trans(t, cube);
+
+ return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c);
+}
+
+static uint64_t
+index_eofbepos_sym16(Cube cube)
+{
+ return sd_eofbepos_16.class[coord_eofbepos.index(cube)];
+}
+
+static uint64_t
+index_khuge(Cube cube)
+{
+ Trans t;
+ Cube c;
+ uint64_t a;
+
+ t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)];
+ c = apply_trans(t, cube);
+ a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24);
+
+ return a * POW3TO7 + c.coud;
+}
+
+/* Other functions ***********************************************************/
+
+static void
+gensym(SymData *sd)
+{
+ uint64_t i, in, nreps = 0;
+ int j;
+ Cube c, d;
+
+ if (sd->generated)
+ return;
+
+ sd->class = malloc(sd->coord->max * sizeof(uint64_t));
+ sd->rep = malloc(sd->coord->max * sizeof(Cube));
+ sd->transtorep = malloc(sd->coord->max * sizeof(Trans));
+
+ if (read_symdata_file(sd)) {
+ sd->generated = true;
+ return;
+ }
+
+ fprintf(stderr, "Cannot load %s, generating it\n", sd->filename);
+
+ for (i = 0; i < sd->coord->max; i++)
+ sd->class[i] = sd->coord->max + 1;
+
+ for (i = 0; i < sd->coord->max; i++) {
+ if (sd->class[i] == sd->coord->max + 1) {
+ c = sd->coord->cube(i);
+ sd->rep[nreps] = c;
+ for (j = 0; j < sd->ntrans; j++) {
+ d = apply_trans(sd->trans[j], c);
+ in = sd->coord->index(d);
+
+ if (sd->class[in] == sd->coord->max + 1) {
+ sd->class[in] = nreps;
+ sd->transtorep[in] =
+ inverse_trans(sd->trans[j]);
+ }
+ }
+ nreps++;
+ }
+ }
+
+ sd->sym_coord->max = nreps;
+ sd->rep = realloc(sd->rep, nreps * sizeof(Cube));
+ sd->generated = true;
+
+ fprintf(stderr, "Found %lu classes\n", nreps);
+
+ if (!write_symdata_file(sd))
+ fprintf(stderr, "Error writing SymData file\n");
+
+ return;
+}
+
+static bool
+read_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fread(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+static bool
+write_symdata_file(SymData *sd)
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+100];
+ uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max;
+ bool r = true;
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, sd->filename);
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1;
+ r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn;
+ r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n;
+ r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n;
+
+ fclose(f);
+ return r;
+}
+
+void
+init_symcoord()
+{
+ int i;
+
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ init_coord();
+
+ for (i = 0; i < nsymdata; i++)
+ gensym(all_sd[i]);
+}
+
diff --git a/src/symcoord.h b/src/symcoord.h
@@ -0,0 +1,15 @@
+#ifndef SYMCOORD_H
+#define SYMCOORD_H
+
+#include "coord.h"
+
+extern Coordinate coord_coud_sym16;
+extern Coordinate coord_cp_sym16;
+extern Coordinate coord_eofbepos_sym16;
+extern Coordinate coord_drud_sym16;
+extern Coordinate coord_drudfin_noE_sym16;
+extern Coordinate coord_khuge;
+
+void init_symcoord();
+
+#endif
diff --git a/src/trans.c b/src/trans.c
@@ -0,0 +1,375 @@
+#include "trans.h"
+
+/* Local functions ***********************************************************/
+
+static bool read_ttables_file();
+static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f);
+static bool write_ttables_file();
+
+/* Tables and other data *****************************************************/
+
+static int ep_mirror[12] = {
+ [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL,
+ [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL,
+ [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL
+};
+
+static int cp_mirror[8] = {
+ [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL,
+ [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL
+};
+
+static int cpos_mirror[6] = {
+ [U_center] = U_center, [D_center] = D_center,
+ [R_center] = L_center, [L_center] = R_center,
+ [F_center] = F_center, [B_center] = B_center
+};
+
+/* TODO Is there a more elegant way? */
+static char rotation_alg_string[100][NROTATIONS] = {
+ [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3",
+ [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2",
+ [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3",
+ [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y",
+ [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3",
+ [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3",
+};
+
+static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */
+static int eposs_source[NTRANS];
+static int eposm_source[NTRANS];
+static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */
+static int eorl_source[NTRANS];
+static int eoud_source[NTRANS];
+static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */
+static int cofb_source[NTRANS];
+static int corl_source[NTRANS];
+
+static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8];
+static int eo_ttable[NTRANS][POW2TO11];
+static int cp_ttable[NTRANS][FACTORIAL8];
+static int co_ttable[NTRANS][POW3TO7];
+static int cpos_ttable[NTRANS][FACTORIAL6];
+static Move moves_ttable[NTRANS][NMOVES];
+
+/* Local functions implementation ********************************************/
+
+static bool
+read_ttables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ int b = sizeof(int);
+ bool r = true;
+ Move m;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = FACTORIAL8,
+ [5] = POW3TO7,
+ [6] = FACTORIAL6,
+ [7] = NMOVES
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/");
+ strcat(fname, "ttables");
+
+ if ((f = fopen(fname, "rb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fread(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fread(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fread(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fread(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fread(cp_ttable[m], b, me[4], f) == me[4];
+ r = r && fread(co_ttable[m], b, me[5], f) == me[5];
+ r = r && fread(cpos_ttable[m], b, me[6], f) == me[6];
+ r = r && fread(moves_ttable[m], b, me[7], f) == me[7];
+ }
+
+ fclose(f);
+ return r;
+}
+
+static Cube
+rotate_via_compose(Trans r, Cube c, PieceFilter f)
+{
+ static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static CubeArray ma = {
+ .ep = ep_mirror,
+ .eofb = zero12,
+ .eorl = zero12,
+ .eoud = zero12,
+ .cp = cp_mirror,
+ .coud = zero8,
+ .corl = zero8,
+ .cofb = zero8,
+ .cpos = cpos_mirror
+ };
+
+ Alg *inv = inverse_alg(rotation_alg(r));
+ Cube ret = {0};
+
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+ ret = apply_alg_generic(inv, ret, f, true);
+
+ ret = compose_filtered(c, ret, f);
+
+ ret = apply_alg_generic(rotation_alg(r), ret, f, true);
+ if (r >= NROTATIONS)
+ ret = move_via_arrays(&ma, ret, f);
+
+ free_alg(inv);
+ return ret;
+}
+
+static bool
+write_ttables_file()
+{
+ init_env();
+
+ FILE *f;
+ char fname[strlen(tabledir)+20];
+ bool r = true;
+ int b = sizeof(int);
+ Move m;
+
+ /* Table sizes, used for reading and writing files */
+ uint64_t me[11] = {
+ [0] = FACTORIAL12/FACTORIAL8,
+ [1] = FACTORIAL12/FACTORIAL8,
+ [2] = FACTORIAL12/FACTORIAL8,
+ [3] = POW2TO11,
+ [4] = FACTORIAL8,
+ [5] = POW3TO7,
+ [6] = FACTORIAL6,
+ [7] = NMOVES
+ };
+
+ strcpy(fname, tabledir);
+ strcat(fname, "/ttables");
+
+ if ((f = fopen(fname, "wb")) == NULL)
+ return false;
+
+ for (m = 0; m < NTRANS; m++) {
+ r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0];
+ r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1];
+ r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2];
+ r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3];
+ r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4];
+ r = r && fwrite(co_ttable[m], b, me[5], f) == me[5];
+ r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6];
+ r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7];
+ }
+
+ fclose(f);
+ return r;
+}
+
+/* Public functions **********************************************************/
+
+Cube
+apply_trans(Trans t, Cube cube)
+{
+ /*init_trans();*/
+
+ int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm };
+ int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb };
+ int aux_co[3] = { cube.coud, cube.corl, cube.cofb };
+
+ return (Cube) {
+ .epose = epose_ttable[t][aux_epos[epose_source[t]]],
+ .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]],
+ .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]],
+ .eofb = eo_ttable[t][aux_eo[eofb_source[t]]],
+ .eorl = eo_ttable[t][aux_eo[eorl_source[t]]],
+ .eoud = eo_ttable[t][aux_eo[eoud_source[t]]],
+ .coud = co_ttable[t][aux_co[coud_source[t]]],
+ .corl = co_ttable[t][aux_co[corl_source[t]]],
+ .cofb = co_ttable[t][aux_co[cofb_source[t]]],
+ .cp = cp_ttable[t][cube.cp],
+ .cpos = cpos_ttable[t][cube.cpos]
+ };
+}
+
+Trans
+inverse_trans(Trans t)
+{
+ /* TODO is there a more elegant way? */
+ static Trans inverse_trans_aux[NTRANS] = {
+ [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub,
+ [df] = df, [dr] = dr, [dl] = dl, [db] = db,
+ [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr,
+ [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl,
+ [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu,
+ [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd,
+
+ [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror,
+ [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror,
+ [df_mirror] = df_mirror, [dr_mirror] = dl_mirror,
+ [dl_mirror] = dr_mirror, [db_mirror] = db_mirror,
+ [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror,
+ [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror,
+ [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror,
+ [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror,
+ [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror,
+ [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror,
+ [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror,
+ [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror
+ };
+
+ return inverse_trans_aux[t];
+}
+
+Alg *
+rotation_alg(Trans t)
+{
+ int i;
+
+ static Alg *rotation_alg_arr[NROTATIONS];
+ static bool initialized = false;
+
+ if (!initialized) {
+ for (i = 0; i < NROTATIONS; i++)
+ rotation_alg_arr[i] = new_alg(rotation_alg_string[i]);
+
+ initialized = true;
+ }
+
+ return rotation_alg_arr[t % NROTATIONS];
+}
+
+void
+transform_alg(Trans t, Alg *alg)
+{
+ int i;
+
+ /*init_trans();*/
+
+ for (i = 0; i < alg->len; i++)
+ alg->move[i] = moves_ttable[t][alg->move[i]];
+}
+
+void
+init_trans() {
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ init_moves();
+
+ Cube aux, cube, c[3];
+ CubeArray epcp;
+ int i, eparr[12], eoarr[12], cparr[8], coarr[8];
+ unsigned int ui;
+ Move mi, move;
+ Trans m;
+
+ /* Compute sources */
+ for (i = 0; i < NTRANS; i++) {
+ cube = apply_alg(rotation_alg(i), (Cube){0});
+
+ epose_source[i] = edge_slice(what_edge_at(cube, FR));
+ eposs_source[i] = edge_slice(what_edge_at(cube, UR));
+ eposm_source[i] = edge_slice(what_edge_at(cube, UF));
+ eofb_source[i] = what_center_at(cube, F_center)/2;
+ eorl_source[i] = what_center_at(cube, R_center)/2;
+ eoud_source[i] = what_center_at(cube, U_center)/2;
+ coud_source[i] = what_center_at(cube, U_center)/2;
+ cofb_source[i] = what_center_at(cube, F_center)/2;
+ corl_source[i] = what_center_at(cube, R_center)/2;
+ }
+
+ if (read_ttables_file())
+ return;
+
+ fprintf(stderr, "Cannot load %s, generating it\n", "ttables");
+
+ /* Initialize tables */
+ for (m = 0; m < NTRANS; m++) {
+ epcp = (CubeArray){ .ep = eparr, .cp = cparr };
+ cube = apply_alg(rotation_alg(m), (Cube){0});
+ cube_to_arrays(cube, &epcp, pf_epcp);
+ if (m >= NROTATIONS) {
+ apply_permutation(ep_mirror, eparr, 12);
+ apply_permutation(cp_mirror, cparr, 8);
+ }
+
+ for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) {
+ c[0] = admissible_ep((Cube){ .epose = ui }, pf_e);
+ c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s);
+ c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m);
+
+ cube = rotate_via_compose(m,c[epose_source[m]],pf_ep);
+ epose_ttable[m][ui] = cube.epose;
+
+ cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep);
+ eposs_ttable[m][ui] = cube.eposs;
+
+ cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep);
+ eposm_ttable[m][ui] = cube.eposm;
+ }
+ for (ui = 0; ui < POW2TO11; ui++ ) {
+ int_to_sum_zero_array(ui, 2, 12, eoarr);
+ apply_permutation(eparr, eoarr, 12);
+ eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2);
+ }
+ for (ui = 0; ui < POW3TO7; ui++) {
+ int_to_sum_zero_array(ui, 3, 8, coarr);
+ apply_permutation(cparr, coarr, 8);
+ co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3);
+ if (m >= NROTATIONS)
+ co_ttable[m][ui] =
+ invert_digits(co_ttable[m][ui], 3, 7);
+ }
+ for (ui = 0; ui < FACTORIAL8; ui++) {
+ cube = (Cube){ .cp = ui };
+ cube = rotate_via_compose(m, cube, pf_cp);
+ cp_ttable[m][ui] = cube.cp;
+ }
+ for (ui = 0; ui < FACTORIAL6; ui++) {
+ cube = (Cube){ .cpos = ui };
+ cube = rotate_via_compose(m, cube, pf_cpos);
+ cpos_ttable[m][ui] = cube.cpos;
+ }
+ for (mi = 0; mi < NMOVES; mi++) {
+ /* Old version:
+ *
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move(move), aux);
+ mirr = apply_trans(uf_mirror, cube);
+ if (is_solved(cube) || is_solved(mirr))
+ moves_ttable[m][mi] = move;
+ }
+ */
+
+ aux = apply_trans(m, apply_move(mi, (Cube){0}));
+ for (move = 0; move < NMOVES; move++) {
+ cube = apply_move(inverse_move(move), aux);
+ if (is_solved(cube)) {
+ moves_ttable[m][mi] = move;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!write_ttables_file())
+ fprintf(stderr, "Error writing ttables\n");
+}
+
diff --git a/src/trans.h b/src/trans.h
@@ -0,0 +1,13 @@
+#ifndef TRANS_H
+#define TRANS_H
+
+#include "moves.h"
+
+Cube apply_trans(Trans t, Cube cube);
+Trans inverse_trans(Trans t);
+Alg * rotation_alg(Trans i);
+void transform_alg(Trans i, Alg *alg);
+
+void init_trans();
+
+#endif
diff --git a/src/utils.c b/src/utils.c
@@ -1,132 +1,274 @@
#include "utils.h"
-/* Hardcoded factorial of small numbers (n<=12). */
-int factorial[13] = {
- 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600
-};
-
-/* swaps two integers */
-void swap(int *a, int *b) {
- int aux = *a;
- *a = *b;
- *b = aux;
-}
-
-/* Converts the integer a to its representation in base b (first n digits
- * only) and saves the result in r. */
-void int_to_digit_array(int a, int b, int n, int *r) {
- for (int i = 0; i < n; i++) {
- r[i] = a % b;
- a /= b;
- }
-}
-
-/* Converts the array of n digits a to a integer using base b. */
-int digit_array_to_int(int *a, int n, int b) {
- int ret = 0, p = 1;
- for (int i = 0; i < n; i++) {
- ret += a[i] * p;
- p *= b;
- }
- return ret;
-}
-
-/* Converts a permutation on [0..(n-1)] into the integer i which is the index
- * of the permutation in the sorted list of all n! such permutations.
- * Only works for n<=12. */
-int perm_to_index(int *a, int n) {
- int ret = 0;
- for (int i = 0; i < n; i++) {
- int c = 0;
- for (int j = i+1; j < n; j++)
- if (a[i] > a[j])
- c++;
- ret += factorial[n-i-1] * c;
- }
- return ret;
-}
-
-/* Converts a permutation index to the actual permutation as an array
- * (see perm_to_index) and saves the result to r. */
-void index_to_perm(int p, int n, int *r) {
- int a[n];
- for (int j = 0; j < n; j++)
- a[j] = 0; /* picked elements */
- for (int i = 0; i < n; i++) {
- int c = 0, j = 0;
- while (c <= p / factorial[n-i-1]) {
- if (!a[j])
- c++;
- j++;
- }
- r[i] = j-1;
- a[j-1] = 1;
- p %= factorial[n-i-1];
- }
-}
-
-
-int perm_sign_array(int a[], int n) {
- int ret = 0;
- for (int i = 0; i < n; i++)
- for (int j = i+1; j < n; j++)
- if (a[i]>a[j])
- ret++;
- return ret % 2;
-}
-
-int perm_sign_int(int p, int n) {
- int a[n];
- index_to_perm(p, n, a);
- return perm_sign_array(a, n);
-}
-
-
-/* Converts a k-element subset of a set with an element from an array of n
- * elements, of which k are 1 (or just non-zero) and n-k are 0, to its index
- * in the sorted list of all such subsets.
- * Works only for n <= 12. */
-int subset_to_index(int *a, int n, int k) {
- int ret = 0;
- for (int i = 0; i < n; i++) {
- if (k == n-i)
- return ret;
- if (a[i]) {
- ret += factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]);
- k--;
- }
- }
- return ret;
-}
-
-/* Inverse of the above */
-void index_to_subset(int s, int n, int k, int *r) {
- for (int i = 0; i < n; i++) {
- if (k == n-i) {
- for (int j = i; j < n; j++)
- r[j] = 1;
- return;
- }
- int v = factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]);
- if (s >= v) {
- r[i] = 1;
- k--;
- s -= v;
- } else {
- r[i] = 0;
- }
- }
-}
-
-/* Converts the first n-1 digits of a number to an array a of digits in base b;
- * then adds one element to the array, so that the sum of the elements of a is
- * zero modulo b.
- * This is used for determing the edge orientation from an 11-bits integer or
- * the corner orientation from a 7-trits integer. */
-void int_to_sum_zero_array(int x, int b, int n, int *a) {
- int_to_digit_array(x, b, n-1, a);
- int s = 0;
- for (int i = 0; i < n - 1; i++) s = (s + a[i]) % b;
- a[n-1] = (b - s) % b;
+void
+apply_permutation(int *perm, int *set, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ if (!is_perm(perm, n))
+ return;
+
+ for (i = 0; i < n; i++)
+ aux[i] = set[perm[i]];
+
+ memcpy(set, aux, n * sizeof(int));
+ free(aux);
+}
+
+int
+binomial(int n, int k)
+{
+ if (n < 0 || k < 0 || k > n)
+ return 0;
+
+ return factorial(n) / (factorial(k) * factorial(n-k));
+}
+
+int
+digit_array_to_int(int *a, int n, int b)
+{
+ int i, ret = 0, p = 1;
+
+ for (i = 0; i < n; i++, p *= b)
+ ret += a[i] * p;
+
+ return ret;
+}
+
+int
+factorial(int n)
+{
+ int i, ret = 1;
+
+ if (n < 0)
+ return 0;
+
+ for (i = 1; i <= n; i++)
+ ret *= i;
+
+ return ret;
+}
+
+void
+index_to_perm(int p, int n, int *r)
+{
+ int *a = malloc(n * sizeof(int));
+ int i, j, c;
+
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+
+ if (p < 0 || p >= factorial(n))
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ j = 0;
+ while (c <= p / factorial(n-i-1))
+ c += a[j++] ? 0 : 1;
+ r[i] = j-1;
+ a[j-1] = 1;
+ p %= factorial(n-i-1);
+ }
+
+ free(a);
+}
+
+void
+index_to_subset(int s, int n, int k, int *r)
+{
+ int i, j, v;
+
+ if (s < 0 || s >= binomial(n, k)) {
+ for (i = 0; i < n; i++)
+ r[i] = -1;
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i) {
+ for (j = i; j < n; j++)
+ r[j] = 1;
+ return;
+ }
+
+ if (k == 0) {
+ for (j = i; j < n; j++)
+ r[j] = 0;
+ return;
+ }
+
+ v = binomial(n-i-1, k);
+ if (s >= v) {
+ r[i] = 1;
+ k--;
+ s -= v;
+ } else {
+ r[i] = 0;
+ }
+ }
+}
+
+void
+int_to_digit_array(int a, int b, int n, int *r)
+{
+ int i;
+
+ if (b <= 1)
+ for (i = 0; i < n; i++)
+ r[i] = 0;
+ else
+ for (i = 0; i < n; i++, a /= b)
+ r[i] = a % b;
+}
+
+void
+int_to_sum_zero_array(int x, int b, int n, int *a)
+{
+ int i, s = 0;
+
+ if (b <= 1) {
+ for (i = 0; i < n; i++)
+ a[i] = 0;
+ } else {
+ int_to_digit_array(x, b, n-1, a);
+ for (i = 0; i < n - 1; i++)
+ s = (s + a[i]) % b;
+ a[n-1] = (b - s) % b;
+ }
+}
+
+int
+invert_digits(int a, int b, int n)
+{
+ int i, ret, *r = malloc(n * sizeof(int));
+
+ int_to_digit_array(a, b, n, r);
+ for (i = 0; i < n; i++)
+ r[i] = (b-r[i]) % b;
+
+ ret = digit_array_to_int(r, n, b);
+ free(r);
+ return ret;
+}
+
+bool
+is_perm(int *a, int n)
+{
+ int *aux = malloc(n * sizeof(int));
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (a[i] < 0 || a[i] >= n)
+ return false;
+ else
+ aux[a[i]] = 1;
+
+ for (i = 0; i < n; i++)
+ if (!aux[i])
+ return false;
+
+ free(aux);
+
+ return true;
+}
+
+bool
+is_subset(int *a, int n, int k)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < n; i++)
+ sum += a[i] ? 1 : 0;
+
+ return sum == k;
+}
+
+int
+perm_sign(int *a, int n)
+{
+ int i, j, ret = 0;
+
+ if (!is_perm(a,n))
+ return -1;
+
+ for (i = 0; i < n; i++)
+ for (j = i+1; j < n; j++)
+ ret += (a[i] > a[j]) ? 1 : 0;
+
+ return ret % 2;
+}
+
+int
+perm_to_index(int *a, int n)
+{
+ int i, j, c, ret = 0;
+
+ if (!is_perm(a, n))
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ c = 0;
+ for (j = i+1; j < n; j++)
+ c += (a[i] > a[j]) ? 1 : 0;
+ ret += factorial(n-i-1) * c;
+ }
+
+ return ret;
+}
+
+int
+powint(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ if (b == 0)
+ return 1;
+
+ if (b % 2)
+ return a * powint(a, b-1);
+ else
+ return powint(a*a, b/2);
+}
+
+int
+subset_to_index(int *a, int n, int k)
+{
+ int i, ret = 0;
+
+ if (!is_subset(a, n, k))
+ return binomial(n, k);
+
+ for (i = 0; i < n; i++) {
+ if (k == n-i)
+ return ret;
+ if (a[i]) {
+ ret += binomial(n-i-1, k);
+ k--;
+ }
+ }
+
+ return ret;
+}
+
+void
+sum_arrays_mod(int *src, int *dst, int n, int m)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m;
+}
+
+void
+swap(int *a, int *b)
+{
+ int aux;
+
+ aux = *a;
+ *a = *b;
+ *b = aux;
}
diff --git a/src/utils.h b/src/utils.h
@@ -1,58 +1,41 @@
-#define min(a,b) (((a) < (b)) ? (a) : (b))
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-#define abs(a) (((a) > 0) ? (a) : (-(a)))
-
-/* Some useful constants */
-#define pow2to11 2048
-#define pow2to12 4096
-#define pow3to7 2187
-#define pow3to8 6561
-#define pow12to4 20736
-#define factorial4 24
-#define factorial6 720
-#define factorial8 40320
-#define factorial12 479001600
-#define binom12on4 495
-#define binom8on4 70
-
-void swap(int *a, int *b);
-
-/* Hardcoded factorial of small numbers (n<=12). */
-extern int factorial[13];
-
-/* Converts the integer a to its representation in base b (first n digits
- * only) and saves the result in r. */
-void int_to_digit_array(int a, int b, int n, int *r);
-
-/* Converts the array of n digits a to a integer using base b. */
-int digit_array_to_int(int *a, int n, int b);
-
-/* Converts a permutation on [0..(n-1)] into the integer i which is the index
- * of the permutation in the sorted list of all n! such permutations.
- * Only works for n<=12. */
-int perm_to_index(int *a, int n);
-
-/* Converts a permutation index to the actual permutation as an array
- * (see perm_to_index) and saves the result to r. */
-void index_to_perm(int p, int n, int *r);
-
-/* Determine the sign of a permutation, either in integer or array format. */
-int perm_sign_array(int a[], int n);
-int perm_sign_int(int p, int n);
-
-/* Converts a k-element subset of a set with an element from an array of n
- * elements, of which k are 1 and n-k are 0, to its index in the sorted list
- * of all such subsets.
- * Works only for n <= 12. */
-int subset_to_index(int *a, int n, int k);
-
-/* Inverse of the above */
-void index_to_subset(int s, int n, int k, int *r);
-
-/* Converts the first n-1 digits of a number to an array a of digits in base b;
- * then adds one element to the array, so that the sum of the elements of a is
- * zero modulo b.
- * This is used for determing the edge orientation from an 11-bits integer or
- * the corner orientation from a 7-trits integer. */
-void int_to_sum_zero_array(int x, int b, int n, int *a);
-
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define POW2TO6 64ULL
+#define POW2TO11 2048ULL
+#define POW2TO12 4096ULL
+#define POW3TO7 2187ULL
+#define POW3TO8 6561ULL
+#define FACTORIAL4 24ULL
+#define FACTORIAL6 720ULL
+#define FACTORIAL7 5040ULL
+#define FACTORIAL8 40320ULL
+#define FACTORIAL12 479001600ULL
+#define BINOM12ON4 495ULL
+#define BINOM8ON4 70ULL
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+void apply_permutation(int *perm, int *set, int n);
+int binomial(int n, int k);
+int digit_array_to_int(int *a, int n, int b);
+int factorial(int n);
+void index_to_perm(int p, int n, int *r);
+void index_to_subset(int s, int n, int k, int *r);
+void int_to_digit_array(int a, int b, int n, int *r);
+void int_to_sum_zero_array(int x, int b, int n, int *a);
+int invert_digits(int a, int b, int n);
+bool is_perm(int *a, int n);
+bool is_subset(int *a, int n, int k);
+int perm_sign(int *a, int n);
+int perm_to_index(int *a, int n);
+int powint(int a, int b);
+int subset_to_index(int *a, int n, int k);
+void sum_arrays_mod(int *src, int *dst, int n, int m);
+void swap(int *a, int *b);
+
+#endif