4. ルービックキューブ 解法コマンド (kociemba)

#  解法アルゴリズムの導入


<br>[前回の記事](https://qiita.com/nak435/items/d656ae9eb76e58244a8b)で予告した内容です。


https://github.com/muodov/kociemba

当初は ↑上のコマンドをそのまま利用するつもりでした。ので、
まずは簡単に紹介します。

```terminal:インストール
% pip install kociemba
```


### ・ kociemba 使用例

```terminal:REPL実行
% python
Python 3.11.11 (main, Dec  3 2024, 17:20:40) [Clang 16.0.0 (clang-1600.0.26.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import kociemba
>>> kociemba.solve('DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD')
"D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U"
>>> ^D

```


```terminal:1ライナー実行
% python -c "import kociemba;print(kociemba.solve('DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD'))"
D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U
% 
```


```terminal:コマンド実行
% /opt/homebrew/bin/kociemba DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD
D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U
% 
```


### ・ Swift から Pythonコード を呼び出す方法

次の PythonKit を利用します。

https://github.com/pvieito/PythonKit

`kociemba.solve()`を呼び出す Swiftのコードは、次のようになります。

```swift
import PythonKit
setenv("PYTHON_VERSION", "3.11", 1)
let kociemba = Python.import("kociemba")
let result = kociemba.solve("DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD")
let ans = result.description.split(separator: "\n").last ?? "?"
print(ans)
//D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U
```

# しかし、Swift に移植した

冒頭に書いた通り、当初はそのまま利用していましたが、これだと iPad / iPhone では使えないため、最終的には Swiftに移植しました。

C言語ソースコード を Swift に書き換えるだけですが、今回は C言語 の `前置インクリメント演算子`（`++変数名`）に泣かされました。単独の`++`前置インクリメントなら何も問題ないですが、`if`文の中の複雑な条件式に組み入れられると、かなり面倒な分岐分けが必要となります。_詳しくは、後日、別な記事にする予定です。_

もう一点、`kociemba`固有な実装として、事前に計算した各種の配列情報を キャッシュと称する バイナリファイルに保存して、次回からはキャッシュファイルから復元することで 毎回の事前計算を省くように設計されています（全12ファイル、約4.2MB）。

今回は、キャッシュ済みファイルをプロジェクトに直接バンドルして、バンドルされたバイナリファイルから復元することにしました。こうすると、アプリサイズは大きくなりますが、事前計算ロジックを移植する必要がなくなり、かつ、macOS / iOS どらでも動かすことができます。

# コード

### ・ ファイル構成

swiftの場合、ヘッダーファイルを分けずに一つで書きますが、今回はなるべく同じ構成で移植しました。
_型名や関数名の一部は、swift風に書き換えたところがありますが、いま思えば、そのままにしておいた方がよかったかなと後悔。。。_

```
kociemba                                      swift版
├── include                                   ├── include
│   ├── color.h                               │   ├── color_h.swift
│   ├── coordcube.h                           │   ├── coordcube_h.swift
│   ├── corner.h                              │   ├── corner_h.swift
│   ├── cubiecube.h                           │   ├── cubiecube_h.swift
│   ├── edge.h                                │   ├── edge_h.swift
│   ├── facecube.h                            │   ├── facecube_h.swift
│   ├── facelet.h                             │   ├── facelet_h.swift
│   ├── prunetable_helpers.h                  │   ├── prunetable_helpers_t.swift
│   └── search.h                              │   └── search_h.swift
│                                             └── source
├── coordcube.c                                   ├── coordcube.swift
├── cubiecube.c                                   ├── cubiecube.swift
├── facecube.c                                    ├── facecube.swift
├── prunetable_helpers.c                          ├── prunetable_helpers.swift
├── search.c                                      ├── search.swift
└── solve.c                                       └── solve.swift
```

<details><summary><b><i>swift 版 kociemba のコードを表示する（15ファイル）</i></b></summary>

```swift:color_h.swift
// Names the colors of the cube facelets
enum TColor : Int {
    case U = 0, R, F, D, L, B
    static func > (lhs: Self, rhs: Self) -> Bool { lhs.rawValue > rhs.rawValue}
}

let COLOR_COUNT = 6
```

```swift:coordcube_h.swift
// Representation of the cube on the coordinate level

let N_TWIST     = 2187
let N_FLIP      = 2048
let N_SLICE1    = 495
let N_SLICE2    = 24
let N_PARITY    = 2
let N_URFtoDLF  = 20160
let N_FRtoBR    = 11880
let N_URtoUL    = 1320
let N_UBtoDF    = 1320
let N_URtoDF    = 20160 // Note: Original C comment says 665280 in phase 1, but the constant is 20160. Using the constant value.
let N_URFtoDLB  = 40320 // Note: This constant is defined but not used in the provided C snippet.
let N_URtoBR    = 479001600 // Note: This constant is defined but not used in the provided C snippet.
let N_MOVE      = 18

struct TCoordcube {
    
    // All coordinates are 0 for a solved cube except for UBtoDF, which is 114
    var twist: Int
    var flip: Int
    var parity: Int
    var FRtoBR: Int
    var URFtoDLF: Int
    var URtoUL: Int
    var UBtoDF: Int
    var URtoDF: Int
    
    init() {
        self.twist = 0
        self.flip = 0
        self.parity = 0
        self.FRtoBR = 0
        self.URFtoDLF  = 0
        self.URtoUL = 0
        self.UBtoDF = 0
        self.URtoDF = 0
    }
}
```

```swift:corner_h.swift
// The names of the corner positions of the cube. Corner URF e.g., has an U(p), a R(ight) and a F(ront) facelet
enum TCorner: Int, Comparable {
    case URF = 0, UFL, ULB, UBR, DFR, DLF, DBL, DRB
    static func < (lhs: Self, rhs: Self) -> Bool { lhs.rawValue < rhs.rawValue }
    static func > (lhs: Self, rhs: Self) -> Bool { lhs.rawValue > rhs.rawValue }
}

let CORNER_COUNT = 8
```

```swift:cubiecube_h.swift
//Cube on the cubie level
struct TCubiecube {
    // initialize to Id-Cube
    // corner permutation
    var cp: [TCorner]
    // corner orientation
    var co: [Int]
    // edge permutation
    var ep: [TEdge]
    // edge orientation
    var eo: [Int]
    // Swift requires initialization.
    init() {
        self.cp = .init(repeating: .init(rawValue: 0)!, count: 8)
        self.co = .init(repeating: 0, count: 8)
        self.ep = .init(repeating: .init(rawValue: 0)!, count: 12)
        self.eo = .init(repeating: 0, count: 12)
    }
    init(_ cp: [TCorner], _ co: [Int], _ ep: [TEdge], _ eo: [Int]) {
        self.cp = cp
        self.co = co
        self.ep = ep
        self.eo = eo
    }
}

extension TCubiecube: CustomStringConvertible {
    func namaedList(_ name: String, _ list: [Int]) -> String {
        "\(name): \(list.map(String.init).joined(separator: " "))"
    }
    var description: String {
        var result = "TCubiecube {"
        result += namaedList(" cp", self.cp.map{$0.rawValue})
        result += namaedList(", co", self.co.map{$0})
        result += namaedList(", ep", self.ep.map{$0.rawValue})
        result += namaedList(", eo", self.eo.map{$0})
        result += " }"
        return result
    }
}

enum VerifyResult {
    case ok
    case ng(Int)
}
```

```swift:edge_h.swift
// The names of the edge positions of the cube. Edge UR e.g., has an U(p) and R(ight) facelet.
enum TEdge: Int, Comparable, Equatable {
    case UR = 0, UF, UL, UB, DR, DF, DL, DB, FR, FL, BL, BR
    static func < (lhs: Self, rhs: Self) -> Bool { lhs.rawValue < rhs.rawValue }
    static func > (lhs: Self, rhs: Self) -> Bool { lhs.rawValue > rhs.rawValue }
    static func == (lhs: Self, rhs: Self) -> Bool { lhs.rawValue == rhs.rawValue }
}

let EDGE_COUNT = 12
```

```swift:facecube_h.swift
//Cube on the facelet level
struct TFacecube {
    var f: [TColor]
    init() {
        f = .init(repeating: .init(rawValue: 0)!, count: 54)
    }
    init(_ array: [TColor]) {
        f = array
    }
}

extension TFacecube: CustomStringConvertible {
    var description: String {
        var result = "TFacecube {"
        result += self.f.map{String($0.rawValue)}.joined(separator: " ")
        result += " }"
        return result
    }
}


// Map the corner positions to facelet positions. cornerFacelet[URF.ordinal()][0] e.g. gives the position of the
// facelet in the URF corner position, which defines the orientation.<br>
// cornerFacelet[URF.ordinal()][1] and cornerFacelet[URF.ordinal()][2] give the position of the other two facelets
// of the URF corner (clockwise).
//let cornerFacelet: [[TFacelet]] = .init(repeating: Array(repeating: .u1, count: 3), count: 8)

// Map the edge positions to facelet positions. edgeFacelet[UR.ordinal()][0] e.g. gives the position of the facelet in
// the UR edge position, which defines the orientation.<br>
// edgeFacelet[UR.ordinal()][1] gives the position of the other facelet
//let edgeFacelet: [[TFacelet]] = .init(repeating: Array(repeating: .u1, count: 2), count: 12)

// Map the corner positions to facelet colors.
//let cornerColor: [[TColor]] = .init(repeating: Array(repeating: .U, count: 3), count: 8)

// Map the edge positions to facelet colors.
//let edgeColor: [[TColor]] = Array(repeating: Array(repeating: .U, count: 2), count: 12)
```

```swift:facelet_h.swift
/// <pre>
/// The names of the facelet positions of the cube
///             |************|
///             |*U1**U2**U3*|
///             |************|
///             |*U4**U5**U6*|
///             |************|
///             |*U7**U8**U9*|
///             |************|
/// ************|************|************|************|
/// *L1**L2**L3*|*F1**F2**F3*|*R1**R2**F3*|*B1**B2**B3*|
/// ************|************|************|************|
/// *L4**L5**L6*|*F4**F5**F6*|*R4**R5**R6*|*B4**B5**B6*|
/// ************|************|************|************|
/// *L7**L8**L9*|*F7**F8**F9*|*R7**R8**R9*|*B7**B8**B9*|
/// ************|************|************|************|
///             |************|
///             |*D1**D2**D3*|
///             |************|
///             |*D4**D5**D6*|
///             |************|
///             |*D7**D8**D9*|
///             |************|
/// </pre>
///
///A cube definition string "UBL..." means for example: In position U1 we have the U-color, in position U2 we have the
/// B-color, in position U3 we have the L color etc. according to the order U1, U2, U3, U4, U5, U6, U7, U8, U9, R1, R2,
/// R3, R4, R5, R6, R7, R8, R9, F1, F2, F3, F4, F5, F6, F7, F8, F9, D1, D2, D3, D4, D5, D6, D7, D8, D9, L1, L2, L3, L4,
/// L5, L6, L7, L8, L9, B1, B2, B3, B4, B5, B6, B7, B8, B9 of the enum constants.
enum TFacelet: Int {
    case U1 = 0, U2, U3, U4, U5, U6, U7, U8, U9, R1, R2, R3, R4, R5, R6, R7, R8, R9, F1, F2, F3, F4, F5, F6, F7, F8, F9, D1, D2, D3, D4, D5, D6, D7, D8, D9, L1, L2, L3, L4, L5, L6, L7, L8, L9, B1, B2, B3, B4, B5, B6, B7, B8, B9
}

let FACELET_COUNT = 54
```

```swift:prunetable_helpers_t.swift
/*
 blank
 */
```

```swift:search_h.swift
public struct TSearch {
    var ax: [Int] // The axis of the move
    var po: [Int] // The power of the move
    var flip: [Int] // phase1 coordinates
    var twist: [Int]
    var slice: [Int]
    var parity: [Int] // phase2 coordinates
    var URFtoDLF: [Int]
    var FRtoBR: [Int]
    var URtoUL: [Int]
    var UBtoDF: [Int]
    var URtoDF: [Int]
    var minDistPhase1: [Int] // IDA* distance do goal estimations
    var minDistPhase2: [Int]
    
    // Initialize with default values (all zeros for Int arrays)
    init() {
        self.ax = [Int](repeating: 0, count: 31)
        self.po = [Int](repeating: 0, count: 31)
        self.flip = [Int](repeating: 0, count: 31)
        self.twist = [Int](repeating: 0, count: 31)
        self.slice = [Int](repeating: 0, count: 31)
        self.parity = [Int](repeating: 0, count: 31)
        self.URFtoDLF = [Int](repeating: 0, count: 31)
        self.FRtoBR = [Int](repeating: 0, count: 31)
        self.URtoUL = [Int](repeating: 0, count: 31)
        self.UBtoDF = [Int](repeating: 0, count: 31)
        self.URtoDF = [Int](repeating: 0, count: 31)
        self.minDistPhase1 = [Int](repeating: 0, count: 31)
        self.minDistPhase2 = [Int](repeating: 0, count: 31)
    }
}
```

```swift:coordcube.swift
import Foundation

var twistMove = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_TWIST)
var flipMove = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_FLIP)
var parityMove = [
    [ 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1 ],
    [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ]
]
var FRtoBR_Move = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_FRtoBR)
var URFtoDLF_Move = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_URFtoDLF)
var URtoDF_Move = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_URtoDF)
var URtoUL_Move = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_URtoUL)
var UBtoDF_Move = [[Int]](repeating: [Int](repeating: 0, count: N_MOVE), count: N_UBtoDF)
var MergeURtoULandUBtoDF = [[Int]](repeating: [Int](repeating: 0, count: 336), count: 336)
var Slice_URFtoDLF_Parity_Prun = [Int](repeating: 0, count: N_SLICE2 * N_URFtoDLF * N_PARITY / 2)
var Slice_URtoDF_Parity_Prun = [Int](repeating: 0, count: N_SLICE2 * N_URtoDF * N_PARITY / 2)
var Slice_Twist_Prun = [Int](repeating: 0, count: N_SLICE1 * N_TWIST / 2 + 1)
var Slice_Flip_Prun = [Int](repeating: 0, count: N_SLICE1 * N_FLIP / 2)
var PRUNING_INITED = false

func move(_ coordcube: inout TCoordcube, _ m: Int, _ cache_dir: String) {
    if !PRUNING_INITED {
        initPruning(cache_dir)
    }
    coordcube.twist = twistMove[coordcube.twist][m]
    coordcube.flip = flipMove[coordcube.flip][m]
    coordcube.parity = parityMove[coordcube.parity][m]
    coordcube.FRtoBR = FRtoBR_Move[coordcube.FRtoBR][m]
    coordcube.URFtoDLF = URFtoDLF_Move[coordcube.URFtoDLF][m]
    coordcube.URtoUL = URtoUL_Move[coordcube.URtoUL][m]
    coordcube.UBtoDF = UBtoDF_Move[coordcube.UBtoDF][m]
    if (coordcube.URtoUL < 336 && coordcube.UBtoDF < 336) {// updated only if UR,UF,UL,UB,DR,DF
        // are not in UD-slice
        coordcube.URtoDF = MergeURtoULandUBtoDF[coordcube.URtoUL][coordcube.UBtoDF]
    }
}

func get_coordcube(_ cubiecube: TCubiecube) -> TCoordcube {
    var result = TCoordcube()
    
    result.twist    = getTwist(cubiecube)
    result.flip     = getFlip(cubiecube)
    result.parity   = cornerParity(cubiecube)
    result.FRtoBR   = getFRtoBR(cubiecube)
    result.URFtoDLF = getURFtoDLF(cubiecube)
    result.URtoUL   = getURtoUL(cubiecube)
    result.UBtoDF   = getUBtoDF(cubiecube)
    result.URtoDF   = getURtoDF(cubiecube)// only needed in phase2
    
    return result
}

func initPruning(_ cache_dir: String) {
    if !check_cached_table2("twistMove", &twistMove, cache_dir) { }
    if !check_cached_table2("flipMove", &flipMove, cache_dir) { }
    if !check_cached_table2("FRtoBR_Move", &FRtoBR_Move, cache_dir) { }
    if !check_cached_table2("URFtoDLF_Move", &URFtoDLF_Move, cache_dir) { }
    if !check_cached_table2("URtoDF_Move", &URtoDF_Move, cache_dir) { }
    if !check_cached_table2("URtoUL_Move", &URtoUL_Move, cache_dir) { }
    if !check_cached_table2("UBtoDF_Move", &UBtoDF_Move, cache_dir) { }
    if !check_cached_table2("MergeURtoULandUBtoDF", &MergeURtoULandUBtoDF, cache_dir) { }
    if !check_cached_table1("Slice_URFtoDLF_Parity_Prun", &Slice_URFtoDLF_Parity_Prun, cache_dir) { }
    if !check_cached_table1("Slice_URtoDF_Parity_Prun", &Slice_URtoDF_Parity_Prun, cache_dir) { }
    if !check_cached_table1("Slice_Twist_Prun", &Slice_Twist_Prun, cache_dir) { }
    if !check_cached_table1("Slice_Flip_Prun", &Slice_Flip_Prun, cache_dir) { }
    
    PRUNING_INITED = true
}

@inlinable
@inline(__always)
func setPruning(_ table: inout [Int], _ index: Int, _ value: Int) {
    if (index & 1) == 0 {
        table[index / 2] &= 0xf0 | value
    } else {
        table[index / 2] &= 0x0f | (value << 4)
    }
}

// Extract pruning value
@inlinable
@inline(__always)
func getPruning(_ table: inout [Int], _ index: Int) -> Int {
    var result: Int
    
    if (index & 1) == 0 {
        result = (table[index / 2] & 0x0f)
    } else {
        result = ((table[index / 2] >> 4) & 0x0f)
    }

    return result
}
```

```swift:cubiecube.swift
var moveCube_initialized = false
func get_moveCube() -> [TCubiecube] {
    var moveCube = [TCubiecube]()
    let cpU = [TCorner.UBR, .URF, .UFL, .ULB, .DFR, .DLF, .DBL, .DRB]
    let coU = [Int](repeating: 0, count: 8)
    let epU = [TEdge.UB, .UR, .UF, .UL, .DR, .DF, .DL, .DB, .FR, .FL, .BL, .BR]
    let eoU = [Int](repeating: 0, count: 12)
    let cpR = [TCorner.DFR, .UFL, .ULB, .URF, .DRB, .DLF, .DBL, .UBR]
    let coR = [Int](repeating: 0, count: 8)
    let epR = [TEdge.FR, .UF, .UL, .UB, .BR, .DF, .DL, .DB, .DR, .FL, .BL, .UR]
    let eoR = [Int](repeating: 0, count: 12)
    let cpF = [TCorner.UFL, .DLF, .ULB, .UBR, .URF, .DFR, .DBL, .DRB]
    let coF = [Int](repeating: 0, count: 8)
    let epF = [TEdge.UR, .FL, .UL, .UB, .DR, .FR, .DL, .DB, .UF, .DF, .BL, .BR]
    let eoF = [Int](repeating: 0, count: 12)
    let cpD = [TCorner.URF, .UFL, .ULB, .UBR, .DLF, .DBL, .DRB, .DFR]
    let coD = [Int](repeating: 0, count: 8)
    let epD = [TEdge.UR, .UF, .UL, .UB, .DF, .DL, .DB, .DR, .FR, .FL, .BL, .BR]
    let eoD = [Int](repeating: 0, count: 12)
    let cpL = [TCorner.URF, .ULB, .DBL, .UBR, .DFR, .UFL, .DLF, .DRB]
    let coL = [Int](repeating: 0, count: 8)
    let epL = [TEdge.UR, .UF, .BL, .UB, .DR, .DF, .FL, .DB, .FR, .UL, .DL, .BR]
    let eoL = [Int](repeating: 0, count: 12)
    let cpB = [TCorner.URF, .UFL, .UBR, .DRB, .DFR, .DLF, .ULB, .DBL]
    let coB = [Int](repeating: 0, count: 8)
    let epB = [TEdge.UR, .UF, .UL, .BR, .DR, .DF, .DL, .BL, .FR, .FL, .UB, .DB]
    let eoB = [Int](repeating: 0, count: 12)
    
    if !moveCube_initialized {
        moveCube.append(TCubiecube(cpU, coU, epU, eoU))
        moveCube.append(TCubiecube(cpR, coR, epR, eoR))
        moveCube.append(TCubiecube(cpF, coF, epF, eoF))
        moveCube.append(TCubiecube(cpD, coD, epD, eoD))
        moveCube.append(TCubiecube(cpL, coL, epL, eoL))
        moveCube.append(TCubiecube(cpB, coB, epB, eoB))
    }
    
    return moveCube
}

func get_cubiecube() -> TCubiecube {
    var result = TCubiecube()
    
    let cp = [TCorner.URF, .UFL, .ULB, .UBR, .DFR, .DLF, .DBL, .DRB]
    let co = [Int](repeating: 0, count: 8)
    let ep = [TEdge.UR, .UF, .UL, .UB, .DR, .DF, .DL, .DB, .FR, .FL, .BL, .BR]
    let eo = [Int](repeating: 0, count: 12)
    
    result.cp = cp
    result.co = co
    result.ep = ep
    result.eo = eo
    
    return result
}

func Cnk(_ n: Int, _ k: Int) -> Int {
    var k = k
    if n < k { return 0 }
    if k > n / 2 { k = n - k }
    var s = 1, i = n, j = 1
    while i != n - k {
        s *= i
        s /= j
        
        i -= 1; j += 1
    }
    return s
}

func rotateLeft_corner(_ arr: inout [TCorner], _ l: Int, _ r: Int) {
    // Left rotation of all array elements between l and r
    
    let temp = arr[l]
    for i in l ..< r {
        arr[i] = arr[i + 1]
    }
    arr[r] = temp
}

func rotateRight_corner(_ arr: inout [TCorner], _ l: Int, _ r: Int) {
    // Right rotation of all array elements between l and r
    
    let temp = arr[r]
    for i in stride(from: r, to: l, by: -1) {
        arr[i] = arr[i - 1]
    }
    arr[l] = temp
}


func rotateLeft_edge(_ arr: inout [TEdge], _ l: Int, _ r: Int) {
    // Left rotation of all array elements between l and r
    
    let temp = arr[l]
    for i in l ..< r {
        arr[i] = arr[i + 1]
    }
    arr[r] = temp
}

func rotateRight_edge(_ arr: inout [TEdge], _ l: Int, _ r: Int) {
    // Right rotation of all array elements between l and r
    
    let temp = arr[r]
    for i in stride(from: r, to: l, by: -1) {
        arr[i] = arr[i - 1]
    }
    arr[l] = temp
}


func toFaceCube(_ cubiecube: TCubiecube) -> TFacecube {
    var fcRet = get_facecube()
    for i in 0 ..< CORNER_COUNT {
        let j = cubiecube.cp[i]// cornercubie with index j is at
        // cornerposition with index i
        let ori = cubiecube.co[i]// Orientation of this cubie
        for n in 0 ..< 3 {
            fcRet.f[cornerFacelet[i][(n + ori) % 3].rawValue] = cornerColor[j.rawValue][n]
        }
    }
    for i in 0 ..< EDGE_COUNT {
        let j = cubiecube.ep[i]// edgecubie with index j is at edgeposition
        // with index i
        let ori = cubiecube.eo[i]// Orientation of this cubie
        for n in 0 ..< 2 {
            fcRet.f[edgeFacelet[i][(n + ori) % 2].rawValue] = edgeColor[j.rawValue][n]
        }
    }
    return fcRet
}

func cornerMultiply(_ cubiecube: inout TCubiecube, _ b: TCubiecube) {
    var cPerm = [TCorner](repeating: .URF, count: 8)
    var cOri = [Int](repeating: 0, count: 8)
    for corn in 0 ..< CORNER_COUNT {
        cPerm[corn] = cubiecube.cp[b.cp[corn].rawValue]
        
        let oriA = cubiecube.co[b.cp[corn].rawValue]
        let oriB = b.co[corn]
        var ori = 0
        
        if oriA < 3 && oriB < 3 {// if both cubes are regular cubes...
            ori = oriA + oriB // just do an addition modulo 3 here
            if (ori >= 3) {
                ori -= 3 // the composition is a regular cube
            }
            // +++++++++++++++++++++not used in this implementation +++++++++++++++++++++++++++++++++++
        } else if oriA < 3 && oriB >= 3 {// if cube b is in a mirrored
        // state...
            ori = oriA + oriB
            if (ori >= 6) {
                ori -= 3 // the composition is a mirrored cube
            }
        } else if (oriA >= 3 && oriB < 3) {// if cube a is an a mirrored
        // state...
            ori = oriA - oriB
            if ori < 3 {
                ori += 3 // the composition is a mirrored cube
            }
        } else if oriA >= 3 && oriB >= 3 {// if both cubes are in mirrored
        // states...
            ori = oriA - oriB
            if ori < 0 {
                ori += 3 // the composition is a regular cube
            }
            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        }
        cOri[corn] = ori
    }

    cubiecube.cp = cPerm
    cubiecube.co = cOri
}

func edgeMultiply(_ cubiecube: inout TCubiecube, _ b: TCubiecube) {
    var ePerm = [TEdge](repeating: .UR, count: 12)
    var eOri = [Int](repeating: 0, count: 12)
    
    for edge in 0 ..< EDGE_COUNT {
        ePerm[edge] = cubiecube.ep[b.ep[edge].rawValue]
        eOri[edge] = (b.eo[edge] + cubiecube.eo[b.ep[edge].rawValue]) % 2
    }
    cubiecube.ep = ePerm
    cubiecube.eo = eOri
}

func multiply(_ cubiecube: inout TCubiecube, _ b: TCubiecube) {
    cornerMultiply(&cubiecube, b)
    edgeMultiply(&cubiecube, b)
}

func invCubieCube(_ cubiecube: TCubiecube, _ c: inout TCubiecube) {
    for edge in 0 ..< EDGE_COUNT {
        c.ep[cubiecube.ep[edge].rawValue] = TEdge(rawValue: edge)!
    }
    for edge in 0 ..< EDGE_COUNT {
        c.eo[edge] = cubiecube.eo[c.ep[edge].rawValue]
    }
    for corn in 0 ..< CORNER_COUNT {
        c.cp[cubiecube.cp[corn].rawValue] = TCorner(rawValue: corn)!
    }
    for corn in 0 ..< CORNER_COUNT {
        let ori = cubiecube.co[c.cp[corn].rawValue]
        if ori >= 3 {// Just for completeness. We do not invert mirrored
            // cubes in the program.
            c.co[corn] = ori
        } else {// the standard case
            c.co[corn] = -ori
            if (c.co[corn] < 0) {
                c.co[corn] += 3
            }
        }
    }
}

func getTwist(_ cubiecube: TCubiecube) -> Int {
    var result = 0
    for i in TCorner.URF.rawValue ..< TCorner.DRB.rawValue {
        result = 3 * result + cubiecube.co[i]
    }
    return result
}

func setTwist(_ cubiecube: inout TCubiecube, _ twist: Int) {
    var twistParity = 0, twist = twist
    for i in stride(from: TCorner.DRB.rawValue - 1, through: TCorner.URF.rawValue, by: -1) {
        cubiecube.co[i] = twist % 3
        twistParity += twist % 3
        twist /= 3
    }
    cubiecube.co[TCorner.DRB.rawValue] = (3 - twistParity % 3) % 3
}

func getFlip(_ cubiecube: TCubiecube) -> Int {
    var result = 0
    for i in TEdge.UR.rawValue ..< TEdge.BR.rawValue {
        result = 2 * result + cubiecube.eo[i]
    }
    return result
}

func setFlip(_ cubiecube: inout TCubiecube, _ flip: Int) {
    var flipParity = 0, flip = flip
    for i in stride(from: TEdge.BR.rawValue - 1, through: TEdge.UR.rawValue, by: -1) {
        cubiecube.eo[i] = flip % 2
        flipParity += flip % 2
        flip /= 2
    }
    cubiecube.eo[TEdge.BR.rawValue] = (2 - flipParity % 2) % 2
}

func cornerParity(_ cubiecube: TCubiecube) -> Int {
    var s = 0
    for i in stride(from: TCorner.DRB.rawValue, through: TCorner.URF.rawValue + 1, by: -1) {
        for j in stride(from: i - 1, through: TCorner.URF.rawValue, by: -1) {
            if cubiecube.cp[j] > cubiecube.cp[i] {
                s += 1
            }
        }
    }
    return s % 2
}

func edgeParity(_ cubiecube: TCubiecube) -> Int {
    var s = 0
    for i in stride(from: TEdge.BR.rawValue, through: TEdge.UR.rawValue + 1, by: -1) {
        for j in stride(from: i - 1, through: TEdge.UR.rawValue, by: -1) {
            if cubiecube.ep[j] > cubiecube.ep[i] {
                s += 1
            }
        }
    }
    return s % 2
}
func getFRtoBR(_ cubiecube: TCubiecube) -> Int {
    var x = 0, a = 0, b = 0
    var edge4 = [TEdge](repeating: TEdge.UR, count: 4)
    // compute the index a < (12 choose 4) and the permutation array perm.
    for j in stride(from: TEdge.BR.rawValue, through: TEdge.UR.rawValue, by: -1) {
        if TEdge.FR <= cubiecube.ep[j] && cubiecube.ep[j] <= TEdge.BR {
            a += Cnk(11 - j, x + 1)
            edge4[3 - x] = cubiecube.ep[j]
            x += 1
        }
    }
    
    for j in stride(from: 3, to: 0, by: -1) { // compute the index b < 4! for the
        // permutation in perm
        var k = 0
        while edge4[j].rawValue != j + 8 {
            rotateLeft_edge(&edge4, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return 24 * a + b
}
func setFRtoBR(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var sliceEdge = [TEdge.FR, .FL, .BL, .BR]
    let otherEdge = [TEdge.UR, .UF, .UL, .UB, .DR, .DF, .DL, .DB]
    var b = idx % 24 // Permutation
    var a = idx / 24 // Combination
    for e in 0 ..< EDGE_COUNT {
        cubiecube.ep[e] = .DB // Use UR to invalidate all edges
    }
    
    for j in 1 ..< 4 {// generate permutation from index b
        var k = b % (j + 1)
        b /= j + 1
        while k > 0 {
            rotateRight_edge(&sliceEdge, 0, j)
            k -= 1
        }
    }
    
    var x = 3// generate combination and set slice edges
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if a - Cnk(11 - j, x + 1) >= 0 {
            cubiecube.ep[j] = sliceEdge[3 - x]
            a -= Cnk(11 - j, x + 1)
            x -= 1
        }
    }
    x = 0 // set the remaining edges UR..DB
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if cubiecube.ep[j] == .DB {
            cubiecube.ep[j] = otherEdge[x]
            x += 1
        }
    }
}

func getURFtoDLF(_ cubiecube: TCubiecube) -> Int {
    var a = 0, x = 0, b = 0
    var corner6 = [TCorner](repeating: .URF, count: 6)
    // compute the index a < (8 choose 6) and the corner permutation.
    for j in TCorner.URF.rawValue ... TCorner.DRB.rawValue {
        if cubiecube.cp[j] <= .DLF {
            a += Cnk(j, x + 1)
            corner6[x] = cubiecube.cp[j]
            x += 1
        }
    }
    
    for j in stride(from: 5, to: 0, by: -1) {// compute the index b < 6! for the
        // permutation in corner6
        
        var k = 0
        while corner6[j].rawValue != j {
            rotateLeft_corner(&corner6, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return 720 * a + b
}

func setURFtoDLF(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var x = 0
    var corner6 = [TCorner.URF, .UFL, .ULB, .UBR, .DFR, .DLF]
    let otherCorner = [TCorner.DBL, .DRB]
    var b = idx % 720 // Permutation
    var a = idx / 720 // Combination
    for c in 0 ..< CORNER_COUNT {
        cubiecube.cp[c] = .DRB // Use DRB to invalidate all corners
    }
    
    for j in 1 ..< 6 {// generate permutation from index b
        var k = b % (j + 1)
        b /= j + 1
        while k > 0 {
            rotateRight_corner(&corner6, 0, j)
            k -= 1
        }
    }
    x = 5 // generate combination and set corners
    for j in stride(from: TCorner.DRB.rawValue, through: 0, by: -1) {
        if a - Cnk(j, x + 1) >= 0 {
            cubiecube.cp[j] = corner6[x]
            a -= Cnk(j, x + 1)
            x -= 1
        }
    }
    x = 0
    for j in TCorner.URF.rawValue ... TCorner.DRB.rawValue {
        if cubiecube.cp[j] == .DRB {
            cubiecube.cp[j] = otherCorner[x]
            x += 1
        }
    }
}

func getURtoDF(_ cubiecube: TCubiecube) -> Int {
    var a = 0, x = 0, b = 0
    var edge6 = [TEdge](repeating: .UR, count: 6)
    // compute the index a < (12 choose 6) and the edge permutation.
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if cubiecube.ep[j] <= .DF {
            a += Cnk(j, x + 1)
            edge6[x] = cubiecube.ep[j]
            x += 1
        }
    }
    
    for j in stride(from: 5, to: 0, by: -1) {// compute the index b < 6! for the
        // permutation in edge6
        var k = 0
        while edge6[j].rawValue != j {
            rotateLeft_edge(&edge6, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return 720 * a + b
}

func setURtoDF(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var x = 0
    var edge6 = [TEdge.UR, .UF, .UL, .UB, .DR, .DF]
    let otherEdge = [TEdge.DL, .DB, .FR, .FL, .BL, .BR]
    var b = idx % 720 // Permutation
    var a = idx / 720 // Combination
    
    for e in 0 ..< EDGE_COUNT {
        cubiecube.ep[e] = .BR // Use BR to invalidate all edges
    }
    
    for j in 1 ..< 6 {// generate permutation from index b
        var k = b % (j + 1)
        b /= j + 1
        while k > 0 {
            rotateRight_edge(&edge6, 0, j)
            k -= 1
        }
    }
    x = 5 // generate combination and set edges
    for j in stride(from: TEdge.BR.rawValue, through: 0, by: -1) {
        if a - Cnk(j, x + 1) >= 0 {
            cubiecube.ep[j] = edge6[x]
            a -= Cnk(j, x + 1)
            x -= 1
        }
    }
    x = 0 // set the remaining edges DL..BR
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if cubiecube.ep[j] == .BR {
            cubiecube.ep[j] = otherEdge[x]
            x += 1
        }
    }
}

func getURtoUL(_ cubiecube: TCubiecube) -> Int {
    var a = 0, b = 0, x = 0
    var edge3 = [TEdge](repeating: .UR, count: 3)
    // compute the index a < (12 choose 3) and the edge permutation.
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if cubiecube.ep[j] <= .UL {
            a += Cnk(j, x + 1)
            edge3[x] = cubiecube.ep[j]
            x += 1
        }
    }
    
    for j in stride(from: 2, to: 0, by: -1) {// compute the index b < 3! for the
        // permutation in edge3
        var k = 0
        while edge3[j].rawValue != j {
            rotateLeft_edge(&edge3, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return 6 * a + b
}

func setURtoUL(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var edge3 = [TEdge.UR, .UF, .UL]
    var b = idx % 6 // Permutation
    var a = idx / 6 // Combination
    for e in 0 ..< EDGE_COUNT {
        cubiecube.ep[e] = .BR // Use BR to invalidate all edges
    }
    
    for j in 1 ..< 3 {// generate permutation from index b
        var k = b % (j + 1)
        b /= j + 1
        while k > 0 {
            rotateRight_edge(&edge3, 0, j)
            k -= 1
        }
    }
    var x = 2// generate combination and set edges
    for j in stride(from: TEdge.BR.rawValue, through: 0, by: -1) {
        if a - Cnk(j, x + 1) >= 0 {
            cubiecube.ep[j] = edge3[x]
            a -= Cnk(j, x + 1)
            x -= 1
        }
    }
}

func getUBtoDF(_ cubiecube: TCubiecube) -> Int {
    var a = 0, x = 0, b = 0
    var edge3 = [TEdge](repeating: .UR, count: 3)
    // compute the index a < (12 choose 3) and the edge permutation.
    for j in TEdge.UR.rawValue ... TEdge.BR.rawValue {
        if .UB <= cubiecube.ep[j] && cubiecube.ep[j] <= .DF {
            a += Cnk(j, x + 1)
            edge3[x] = cubiecube.ep[j]
            x += 1
        }
    }
    
    for j in stride(from: 2, to: 0, by: -1) {// compute the index b < 3! for the
        // permutation in edge3
        var k = 0
        while edge3[j].rawValue != TEdge.UB.rawValue + j {
            rotateLeft_edge(&edge3, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return 6 * a + b
}

func setUBtoDF(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var x = 0
    var edge3 = [TEdge.UB, .DR, .DF]
    var b = idx % 6 // Permutation
    var a = idx / 6 // Combination
    for e in 0 ..< EDGE_COUNT {
        cubiecube.ep[e] = .BR// Use BR to invalidate all edges
    }
    
    for j in 1 ..< 3 {// generate permutation from index b
        var k = b % (j + 1)
        b /= j + 1
        while k > 0 {
            rotateRight_edge(&edge3, 0, j)
            k -= 1
        }
    }
    x = 2// generate combination and set edges
    for j in stride(from: TEdge.BR.rawValue, through: 0, by: -1) {
        if a - Cnk(j, x + 1) >= 0 {
            cubiecube.ep[j] = edge3[x]
            a -= Cnk(j, x + 1)
            x -= 1
        }
    }
}

func getURFtoDLB(_ cubiecube: TCubiecube) -> Int {
    var perm = cubiecube.cp
    var b = 0
    for j in stride(from: 7, to: 0, by: -1) {// compute the index b < 8! for the permutation in perm
        var k = 0
        while perm[j].rawValue != j {
            rotateLeft_corner(&perm, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return b
}

func setURFtoDLB(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var idx = idx
    var perm = [TCorner.URF, .UFL, .ULB, .UBR, .DFR, .DLF, .DBL, .DRB]
    var x = 7 // set corners
    for j in 1 ..< 8 {
        var k = idx % (j + 1)
        idx /= j + 1
        while k > 0 {
            rotateRight_corner(&perm, 0, j)
            k -= 1
        }
    }
    for j in stride(from: 7, through: 0, by: -1) {
        cubiecube.cp[j] = perm[x]
        x -= 1
    }
}

func getURtoBR(_ cubiecube: TCubiecube) -> Int {
    var perm = cubiecube.ep
    var b = 0
    for j in stride(from: 11, to: 0, by: -1) {// compute the index b < 12! for the permutation in perm
        var k = 0
        while perm[j].rawValue != j {
            rotateLeft_edge(&perm, 0, j)
            k += 1
        }
        b = (j + 1) * b + k
    }
    return b
}

func setURtoBR(_ cubiecube: inout TCubiecube, _ idx: Int) {
    var idx = idx
    var perm = [TEdge.UR, .UF, .UL, .UB, .DR, .DF, .DL, .DB, .FR, .FL, .BL, .BR]
    var x = 11// set edges
    for j in 1 ..< 12 {
        var k = idx % (j + 1)
        idx /= j + 1
        while k > 0 {
            rotateRight_edge(&perm, 0, j)
            k -= 1
        }
    }
    for j in stride(from: 11, through: 0, by: -1) {
        cubiecube.ep[j] = perm[x]
        x -= 1
    }
}

func verify(_ cubiecube: TCubiecube) -> VerifyResult {
    var edgeCount = [Int](repeating: 0, count: 12)
    var cornerCount = [Int](repeating: 0, count: 8)
    
    for e in 0 ..< EDGE_COUNT {
        edgeCount[cubiecube.ep[e].rawValue] += 1
    }
    if !edgeCount.allSatisfy({ $0 == 1 }) {
        return VerifyResult.ng(-2)
    }
    if cubiecube.eo.sum() % 2 != 0 {
        return VerifyResult.ng(-3)
    }
    
    for c in 0 ..< CORNER_COUNT {
        cornerCount[cubiecube.cp[c].rawValue] += 1
    }
    if !cornerCount.allSatisfy({ $0 == 1 }) {
        return VerifyResult.ng(-4) // missing corners
    }
    if cubiecube.co.sum() % 3 != 0 {
        return VerifyResult.ng(-5) // twisted corner
    }
    
    if ((edgeParity(cubiecube) ^ cornerParity(cubiecube)) != 0) {
        return VerifyResult.ng(-6) // parity error
    }
    
    return VerifyResult.ok // cube ok
}

func getURtoDF_standalone(_ idx1: Int, _ idx2: Int) -> Int {
    var a = get_cubiecube()
    var b = get_cubiecube()
    setURtoUL(&a, idx1)
    setUBtoDF(&b, idx2)
    for i in 0 ..< 8 {
        if a.ep[i] != .BR {
            if b.ep[i] != .BR {// collision
                return -1
            } else {
                b.ep[i] = a.ep[i]
            }
        }
    }
    let result = getURtoDF(b)
    return result
}
```

```swift:facecube.swift
let cornerFacelet: [[TFacelet]] = [ [ .U9, .R1, .F3 ], [ .U7, .F1, .L3 ], [ .U1, .L1, .B3 ], [ .U3, .B1, .R3 ],
                                     [ .D3, .F9, .R7 ], [ .D1, .L9, .F7 ], [ .D7, .B9, .L7 ], [ .D9, .R9, .B7 ] ]

let edgeFacelet: [[TFacelet]] = [ [ .U6, .R2 ], [ .U8, .F2 ], [ .U4, .L2 ], [ .U2, .B2 ], [ .D6, .R8 ], [ .D2, .F8 ],
                                   [ .D4, .L8 ], [ .D8, .B8 ], [ .F6, .R4 ], [ .F4, .L6 ], [ .B6, .L4 ], [ .B4, .R6 ] ]

let cornerColor: [[TColor]] = [ [ .U, .R, .F ], [ .U, .F, .L ], [ .U, .L, .B ], [ .U, .B, .R ], [ .D, .F, .R ], [ .D, .L, .F ],
                                 [ .D, .B, .L ], [ .D, .R, .B ] ]

let edgeColor: [[TColor]] = [ [ .U, .R ], [ .U, .F ], [ .U, .L ], [ .U, .B ], [ .D, .R ], [ .D, .F ], [ .D, .L ], [ .D, .B ],
                               [ .F, .R ], [ .F, .L ], [ .B, .L ], [ .B, .R ] ]

func get_facecube() -> TFacecube {
    let result = TFacecube( [.U, .U, .U, .U, .U, .U, .U, .U, .U, .R, .R, .R, .R, .R, .R, .R, .R, .R, .F, .F, .F, .F, .F, .F, .F, .F, .F, .D, .D, .D, .D, .D, .D, .D, .D, .D, .L, .L, .L, .L, .L, .L, .L, .L, .L, .B, .B, .B, .B, .B, .B, .B, .B, .B] )
    return result
}

func get_facecube_fromstring(_ cubeString: String) -> TFacecube {
    var result = [TColor]()
    for c in cubeString {
        switch c {
            case "U": result.append(.U)
            case "R": result.append(.R)
            case "F": result.append(.F)
            case "D": result.append(.D)
            case "L": result.append(.L)
            case "B": result.append(.B)
            default: break
        }
    }
    return TFacecube(result)
}

func to_String(_ facecube: TFacecube) -> String {
    var result: String = ""
    for f in facecube.f {
        switch(f) {
            case .U: result.append("U")
            case .R: result.append("R")
            case .F: result.append("F")
            case .D: result.append("D")
            case .L: result.append("L")
            case .B: result.append("B")
        }
    }
    return result
}

func toCubieCube(_ facecube: TFacecube) -> TCubiecube {
    var ccRet = TCubiecube()
    for i in 0 ..< 8 {
        ccRet.cp[i] = .URF // invalidate corners
    }
    for i in 0 ..< 12 {
        ccRet.ep[i] = .UR // and edges
    }
    
    for i in 0 ..< CORNER_COUNT {
        // get the colors of the cubie at corner i, starting with U/D
        for ori in 0 ..< 3 {
            if facecube.f[cornerFacelet[i][ori].rawValue].rawValue == TColor.U.rawValue || facecube.f[cornerFacelet[i][ori].rawValue].rawValue == TColor.D.rawValue {
                let col1 = facecube.f[cornerFacelet[i][(ori + 1) % 3].rawValue]
                let col2 = facecube.f[cornerFacelet[i][(ori + 2) % 3].rawValue]
                for j in 0 ..< EDGE_COUNT {
                    if col1 == cornerColor[j][1] && col2 == cornerColor[j][2] {
                        // in cornerposition i we have cornercubie j
                        ccRet.cp[i] = TCorner(rawValue: j)!
                        ccRet.co[i] = ori % 3
                        break
                    }
                }
                break
            }
        }
    }
    
    for i in 0 ..< EDGE_COUNT {
        for j in 0 ..< EDGE_COUNT {
            if facecube.f[edgeFacelet[i][0].rawValue] == edgeColor[j][0]
                && facecube.f[edgeFacelet[i][1].rawValue] == edgeColor[j][1] {
                ccRet.ep[i] = TEdge(rawValue: j)!
                ccRet.eo[i] = 0
                break
            }
            if facecube.f[edgeFacelet[i][0].rawValue] == edgeColor[j][1]
                && facecube.f[edgeFacelet[i][1].rawValue] == edgeColor[j][0] {
                ccRet.ep[i] = TEdge(rawValue: j)!
                ccRet.eo[i] = 1
                break
            }
        }
    }
    return ccRet
}
```

```swift:prunetable_helpers.swift
import Foundation

typealias OneDimension = [Int]
typealias TwoDimension = [[Int]]

func check_cached_table1(_ name: String, _ buf: inout OneDimension, _ cache_dir: String) -> Bool {
    var result = true
    
    if let path = Bundle.main.path(forResource: name, ofType: nil) {
        if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
            if data.count == buf.count {
                buf = data.map { Int($0) }
            } else {
                print("Cache table1 \(name) size unmatch. data: \(data.count), buf: \(buf.count)")
                result = false
            }
        }
    } else {
        print("Cache table1 \(name) was not found. ERROR!")
        result = false
    }
    return result
}

func check_cached_table2(_ name: String, _ buf: inout TwoDimension, _ cache_dir: String) -> Bool {
    var result = true
        
    if let path = Bundle.main.path(forResource: name, ofType: nil) {
        if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {

            let height = buf.count
            let width = data.count / 2 / height
            if data.count / 2 == height * buf[0].count {
                var p = 0
                for h in 0 ..< height {
                    for w in 0 ..< width {
                        buf[h][w] = Int(data[p]) + (Int(data[p + 1]) << 8) //for litte endian
                        p += 2
                    }
                }
            } else {
                print("Cache table2 \(name) size unmatch. data: \(data.count / 2), buf: \(height * buf[0].count)")
                result = false
            }
        }
    } else {
        print("Cache table2 \(name) was not found. ERROR!")
        result = false
    }
    return result
}
```

```swift:search.swift
import Foundation

@inlinable
@inline(__always)
func preAdd<T: BinaryInteger>(_ array: inout [T], _ index: Int, _ value: T) -> T {
    array[index] += value
    return array[index]
}

extension Array where Element: BinaryInteger {
    @inlinable
    @inline(__always)
    func sum() -> Element { self.reduce(0, +) }
}

func solutionToString(_ search: TSearch, _ length: Int, _ depthPhase1: Int) -> String {
    var s = ""
    for i in 0 ..< length {
        switch search.ax[i] {
            case 0: s.append("U")
            case 1: s.append("R")
            case 2: s.append("F")
            case 3: s.append("D")
            case 4: s.append("L")
            case 5: s.append("B")
            default: break
        }
        switch search.po[i] {
            case 1: s.append(" ")
            case 2: s.append("2 ")
            case 3: s.append("' ")
            default: break
        }
        if (i == depthPhase1 - 1) {
            s.append(". ")
        }
    }
    return String(s)
}

func solution(_ facelets: String, maxDepth: Int, timeOut: TimeInterval, useSeparator: Bool, _ cache_dir: String) -> String? {
    
    var search = TSearch()
    var count  = [Int](repeating: 0, count: 6)
    
    if !PRUNING_INITED {
        initPruning(cache_dir)
    }
    
    for c in facelets {
        switch c {
            case "U": count[TColor.U.rawValue] += 1
            case "R": count[TColor.R.rawValue] += 1
            case "F": count[TColor.F.rawValue] += 1
            case "D": count[TColor.D.rawValue] += 1
            case "L": count[TColor.L.rawValue] += 1
            case "B": count[TColor.B.rawValue] += 1
            default: break
        }
    }
    
    if !count.allSatisfy({ $0 == 9 }) {
        return nil
    }
    
    let fc = get_facecube_fromstring(facelets)
    let cc = toCubieCube(fc)
    if case .ng(let error) = verify(cc) {
        print("verify error: \(error)")
        return nil
    }
    
    // // +++++++++++++++++++++++ initialization +++++++++++++++++++++++++++++++++
    let c = get_coordcube(cc)
    
    search.po[0] = 0
    search.ax[0] = 0
    search.flip[0] = c.flip
    search.twist[0] = c.twist
    search.parity[0] = c.parity
    search.slice[0] = c.FRtoBR / 24
    search.URFtoDLF[0] = c.URFtoDLF
    search.FRtoBR[0] = c.FRtoBR
    search.URtoUL[0] = c.URtoUL
    search.UBtoDF[0] = c.UBtoDF
    
    search.minDistPhase1[1] = 1// else failure for depth=1, n=0
    var mv = 0, n = 0
    var busy = false
    var depthPhase1 = 1
    
    let tStart = Date.now
    
    // +++++++++++++++++++ Main loop ++++++++++++++++++++++++++++++++++++++++++
    repeat { // Equivalent to do-while(1)
        repeat { // Equivalent to do-while(busy)
            if (depthPhase1 - n > search.minDistPhase1[n + 1]) && !busy {
                
                if search.ax[n] == 0 || search.ax[n] == 3 { // Initialize next move
                    n += 1
                    search.ax[n] = 1
                } else {
                    n += 1
                    search.ax[n] = 0
                }
                search.po[n] = 1
                
            } else if preAdd(&search.po, n, 1) > 3 {
                repeat { // increment axis
                    if preAdd(&search.ax, n, 1) > 5 {
                        
                        if -tStart.timeIntervalSinceNow > timeOut {
                            return nil
                        }
                        if n == 0 {
                            if depthPhase1 >= maxDepth {
                                return nil
                            } else {
                                depthPhase1 += 1
                                search.ax[n] = 0
                                search.po[n] = 1
                                busy = false
                                break // Break from inner do-while (increment axis)
                            }
                        } else {
                            n -= 1
                            busy = true
                            break // Break from inner do-while (increment axis)
                        }
                    } else {
                        search.po[n] = 1
                        busy = false
                    }
                } while n != 0 && (search.ax[n - 1] == search.ax[n] || search.ax[n - 1] - 3 == search.ax[n])
                
            } else {
                busy = false
            }
        } while busy
        
        // +++++++++++++ compute new coordinates and new minDistPhase1 ++++++++++
        // if minDistPhase1 =0, the H subgroup is reached
        mv = 3 * search.ax[n] + search.po[n] - 1
        search.flip[n + 1] = flipMove[Int(search.flip[n])][mv]
        search.twist[n + 1] = twistMove[Int(search.twist[n])][mv]
        search.slice[n + 1] = FRtoBR_Move[Int(search.slice[n] * 24)][mv] / 24
        search.minDistPhase1[n + 1] = max(
            getPruning(&Slice_Flip_Prun, N_SLICE1 * search.flip[n + 1] + search.slice[n + 1]),
            getPruning(&Slice_Twist_Prun, N_SLICE1 * search.twist[n + 1] + search.slice[n + 1])
        )
        // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //print(String(format:"n:%d depthPhase1:%d", n, depthPhase1))
        if search.minDistPhase1[n + 1] == 0 && n >= depthPhase1 - 5 {
            search.minDistPhase1[n + 1] = 10// instead of 10 any value >5 is possible
            let s = (n == depthPhase1 - 1) ? totalDepth(&search, depthPhase1, maxDepth) : 0
            if n == depthPhase1 - 1 && s >= 0 {
                if s == depthPhase1
                    || (search.ax[depthPhase1 - 1] != search.ax[depthPhase1]
                        && search.ax[depthPhase1 - 1] != search.ax[depthPhase1] + 3) {
                    let result =
                        if useSeparator {
                            solutionToString(search, s, depthPhase1)
                        } else {
                            solutionToString(search, s, -1)
                        }
                    return result
                }
            }
        }
    } while true
}

func totalDepth(_ search: inout TSearch, _ depthPhase1: Int, _ maxDepth: Int) -> Int {
    let maxDepthPhase2 = min(10, maxDepth - depthPhase1)// Allow only max 10 moves in phase2
    for i in 0 ..< depthPhase1 {
        let mv = 3 * search.ax[i] + search.po[i] - 1
        //print(String(format: "%d %d %d %d", i, mv, search.ax[i], search.po[i]))
        search.URFtoDLF[i + 1] = URFtoDLF_Move[search.URFtoDLF[i]][mv]
        search.FRtoBR[i + 1] = FRtoBR_Move[search.FRtoBR[i]][mv]
        search.parity[i + 1] = parityMove[search.parity[i]][mv]
    }
    
    let d1 = getPruning(&Slice_URFtoDLF_Parity_Prun,
                    (N_SLICE2 * search.URFtoDLF[depthPhase1] + search.FRtoBR[depthPhase1]) * 2
                     + search.parity[depthPhase1])
    if d1 > maxDepthPhase2 {
        return -1
    }
    
    for i in 0 ..< depthPhase1 {
        let mv = 3 * search.ax[i] + search.po[i] - 1
        search.URtoUL[i + 1] = URtoUL_Move[Int(search.URtoUL[i])][mv]
        search.UBtoDF[i + 1] = UBtoDF_Move[Int(search.UBtoDF[i])][mv]
    }
    search.URtoDF[depthPhase1] = MergeURtoULandUBtoDF[Int(search.URtoUL[depthPhase1])][Int(search.UBtoDF[depthPhase1])]
    
    let d2 = getPruning(&Slice_URtoDF_Parity_Prun,
                    (N_SLICE2 * search.URtoDF[depthPhase1] + search.FRtoBR[depthPhase1]) * 2
                    + search.parity[depthPhase1])
    if d2 > maxDepthPhase2 {
        return -1
    }
    
    search.minDistPhase2[depthPhase1] = max(d1, d2)
    if search.minDistPhase2[depthPhase1] == 0 { // already solved
        return depthPhase1
    }
    
    // now set up search
    
    var depthPhase2 = 1
    var n = depthPhase1
    var busy = false
    search.po[depthPhase1] = 0
    search.ax[depthPhase1] = 0
    search.minDistPhase2[n + 1] = 1// else failure for depthPhase2=1, n=0
    // +++++++++++++++++++ end initialization ++++++++++++++++++++++++++++++++
    
    repeat { // Equivalent to do-while
        repeat { // Equivalent to do-while(busy)
            if (depthPhase1 + depthPhase2 - n > search.minDistPhase2[n + 1]) && !busy {

                if search.ax[n] == 0 || search.ax[n] == 3 { // Initialize next move
                    n += 1
                    search.ax[n] = 1
                    search.po[n] = 2
                } else {
                    n += 1
                    search.ax[n] = 0
                    search.po[n] = 1
                }
            } else if (search.ax[n] == 0 || search.ax[n] == 3) ? (preAdd(&search.po, n, 1) > 3) : (preAdd(&search.po, n, 2) > 3) {
                repeat { // Equivalent to do-while
                    if preAdd(&search.ax, n, 1) > 5 {
                        if n == depthPhase1 {
                            if depthPhase2 >= maxDepthPhase2 {
                                return -1
                            } else {
                                depthPhase2 += 1
                                search.ax[n] = 0
                                search.po[n] = 1
                                busy = false
                                break // Break from inner do-while (increment axis)
                            }
                        } else {
                            n -= 1
                            busy = true
                            break // Break from inner do-while (increment axis)
                        }
                        
                    } else {
                        if search.ax[n] == 0 || search.ax[n] == 3 {
                            search.po[n] = 1
                        } else {
                            search.po[n] = 2
                        }
                        busy = false
                    }
                } while n != depthPhase1 && (search.ax[n - 1] == search.ax[n] || search.ax[n - 1] - 3 == search.ax[n])

            } else {
                busy = false
            }
        } while busy
        

        // +++++++++++++ compute new coordinates and new minDist ++++++++++
        let mv = 3 * search.ax[n] + search.po[n] - 1
        
        search.URFtoDLF[n + 1] = URFtoDLF_Move[search.URFtoDLF[n]][mv]
        search.FRtoBR[n + 1] = FRtoBR_Move[search.FRtoBR[n]][mv]
        search.parity[n + 1] = parityMove[search.parity[n]][mv]
        search.URtoDF[n + 1] = URtoDF_Move[search.URtoDF[n]][mv]
        
        search.minDistPhase2[n + 1] = max(
            getPruning(&Slice_URtoDF_Parity_Prun, (N_SLICE2 * search.URtoDF[n + 1] + search.FRtoBR[n + 1]) * 2 + search.parity[n + 1]),
            getPruning(&Slice_URFtoDLF_Parity_Prun, (N_SLICE2 * search.URFtoDLF[n + 1] + search.FRtoBR[n + 1]) * 2 + search.parity[n + 1]))
        // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        
    } while search.minDistPhase2[n + 1] != 0

    return depthPhase1 + depthPhase2
}

func patternize(_ facelets: String, _ pattern: String) -> String {
    var fc = TFacecube()
    let start_fc = get_facecube_fromstring(facelets)
    let pattern_fc = get_facecube_fromstring(pattern)
    let start_cc = toCubieCube(start_fc)
    let pattern_cc = toCubieCube(pattern_fc)
    var inv_pattern_cc = get_cubiecube()
    invCubieCube(pattern_cc, &inv_pattern_cc)
    multiply(&inv_pattern_cc, start_cc)
    fc = toFaceCube(inv_pattern_cc)
    return to_String(fc)
}
```

```swift:solve.swift
func kociemba(_ argv1: String, _ argv2: String?) -> String? {
    var facelets = argv1
    if let argv2 {
        facelets = patternize(facelets, argv2)
        //print("facelets: \(facelets)")
    }
    if let result = solution(facelets, maxDepth: 24, timeOut: 1000, useSeparator: false, "cache") {
        return result
    }
    return nil
}
```

</details>


```swift:ContentView（テストアプリ）
import SwiftUI

struct ContentView: View {
    @State var facelets = "DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD"
    @State var pattern = ""
    @State var solution = ""
    @State var message = ""
    var body: some View {
        VStack {
            HStack {
                Text("facelets: ")
                TextField("facelets", text: $facelets)
            }
            HStack {
                Text("pattern: ")
                TextField("option", text: $pattern)
            }
            Button("resolve", action: {
                let start = Date.now
                if let result = kociemba(facelets, pattern.isEmpty ? nil : pattern) {
                    let duration = -start.timeIntervalSinceNow
                    solution = result
                    print(result)
                    message = String(format: "duration: %.3f seconds", duration)
                } else {
                    solution = "Unsolvable cube!"
                    print("Unsolvable cube!")
                }
            })
            .padding(.bottom, 20)
            HStack {
                Text("solution: ")
                Text(solution)
                Spacer()
            }
            HStack {
                Text(message)
                Spacer()
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
}
```


![iphone0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/0150f343-ee1c-4169-9c76-d52e42d1f52d.png)

`iPhone13 mini`の実機で、1回目はキャッシュファイルの読み込みが走るので 0.4秒ほどかかりますが、2回目以降は瞬殺です。

```tterminal
% time kociemba "DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD"
D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U
kociemba  0.02s user 0.01s system 88% cpu 0.032 total
```

Macと変わらない性能。


<br>


---

次回の記事で、展開図と3D表示を同期して回転するようにして、今回の解法コードを組み込んでアプリを完成させます。

<br><br>**以上**


<br>


---


https://qiita.com/nak435/items/00e4d8877532c7d9d287

https://qiita.com/nak435/items/392749423e2d976ef8b8

https://qiita.com/nak435/items/d656ae9eb76e58244a8b