Swift - introducing Int2X, and PONS will be back

弾です。ご無沙汰しております。Swift 2から更新が止まっていた [PONS] を Swift 4 と Swift Package Manager 向けに作り直しているのですが、寄り道的にこんなパッケージを作成してみました。

* https://github.com/dankogai/swift-int2x

[PONS]: https://qiita.com/dankogai/items/7af3565d7657785211dc

## Swift Integers are Now Protocol Oriented

[PONS]の目標は Swift の数値型を Protocol Oriented にすることでしたが、これは[SE-0104]の採用により実現しました。PONSの目標は達成されたのですが、皮肉にもそのおかげで PONS 自体は現在の Swift とは非互換になってしまいました。

[SE-0104]: https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md

しかしそれは想定の範囲。この業界ではよくあることです。しかし[PONS]が提供していた任意精度浮動小数点型(BigFloat)や複素数型Complexまで失われてしまったのはいささか残念だったので、やはりPONSを復活させようと決めたのですが、今度は方針を180度変えています。

* 今まで単一パッケージ、単一リポジトリだったものを型ごとに分ける
* 型どおしの依存性は Swift Package Manager で解決する

これこそが本来のパッケージのあるべき姿で、かつてのPONSがモノリシックだったのはプロトコル自体を自前で用意しオーケストレートしなければならないためのやむ得ぬ措置でした。

ここでも Protocol Oriented であることが活きてきます。モジュールが組み合わさることはプロトコルが保証してくれるのです。実際新PONSでは任意精度整数は自前ではなく[attaswift/BigInt]を採用していますが、これも`BigInt`が Swift 4 標準の[SignedInteger]や[BinaryInteger]に準拠しているからで、それらプロトコルに準拠しているのであれば他のモジュールでもいいわけです。

[attaswift/BigInt]: https://github.com/attaswift/BigInt
[SignedInteger]: https://developer.apple.com/documentation/swift/signedinteger
[BinaryInteger]: https://developer.apple.com/documentation/swift/binaryinteger

とりあえず現在まで

* https://github.com/dankogai/swift-bignum

任意精度浮動小数点型(`BigFloat`)と任意精度有理数型(`BigRat`)。ドキュメントがまだですがほぼ完成しております。内部で`BigInt`を使ってます。

* https://github.com/dankogai/swift-complex

複素数型(`Complex`)。PONSとは別に復活させました。`Complex<Double>`だけではなく`Complex<BigFloat>`とかも使えるのは実証済み。

* https://github.com/dankogai/swift-interval

区間演算型([interval arithmetic])。それ自体`FloatingPoint`なので、この型を`Double`などの代わりに使うだけで区間演算ができます。そしてこれもまた`Interval<Double>`だけではなく`Interval<BigFloat>`としても使えますし、さらに`Complex<Interval<BigFloat>>`としても使えることを確認しております。

[interval arithmetic]: https://en.wikipedia.org/wiki/Interval_arithmetic

この区間演算型にちょっと注目してみてください。これは`FloatingPoint`を要素として持つ`FloatingPoint`型です。つまりあるプロトコルに準拠する型を要素として持つ型がそのプロトコルに準拠するという再帰型プロトコル(recursive protocol)というのは可能なわけです。

そして実は、可能なだけではなく役にも立ちます。整数型でこれをやれば、倍幅の整数がいとも簡単に作れるのです。`UInt64`から`UInt128`、`UInt128`から`UInt256`といった具合に。

[Int2X]はまさにそれをやるためのパッケージです。

[Int2X]: https://github.com/dankogai/swift-int2x

```swift
import Int2X
typealias U128 = UInt2X<UInt64> // Yes.  That's it!
typealias I128 = Int2X<UInt64>  // ditto for signed integers
```

実際には128から1024まであらかじめ以下のように定義してあります。

```swift
public typealias UInt128   = UInt2X<UInt64>
public typealias UInt256   = UInt2X<UInt128>
public typealias UInt512   = UInt2X<UInt256>
public typealias UInt1024  = UInt2X<UInt512>
public typealias Int128    = Int2X<UInt64>
public typealias Int256    = Int2X<UInt128>
public typealias Int512    = Int2X<UInt256>
public typealias Int1024   = Int2X<UInt512>
```

あらかじめ定義してあるのは1024までですが、これは[Accelerate]がサポートしているのが1024bitまでということと、それ以上大きくしても`BigInt`より高速にならずメモリー効率も悪いということでそうしているだけで、当然ですが`UInt1024`で`UInt2048`を作ることも問題なくできます。

```sh
 1> import Int2X 
  2> typealias UInt2048 = UInt2X<UInt1024>  
  3> (1...300).map{UInt2048($0)}.reduce(UInt2048(1),*).toString(radix:16) 
$R0: String = "26ca8439285c67e3b9e32731120f78b66f85612a4dd1df6c1d2c69a2d7685f85e75b82bf36e22dcb6ca5044f43fef0b3316049241c821e1ec1c63f95685931f87152ced4befc5ddf7b76719a636f68e7054bf28b57d54d40975ab14d65900681c49044d1725031edbf3c1c5c595b871d6864c371b0cc54ef19d42ad02833bcaf5fb1671b523a143b132c7e1971f7bf5ca77505c960f14b330e6c90dc2539431329ef78a1e9f26b2ead7d28a622e6b586bcee22bd0a495442c6a1235588988252cbd4d36975560fb8e7e5c8cf06f29aeb68659c5cb4cf8d011375b00000000000000000000000000000000000000000000000000000000000000000000000000"
```

[Accelerate]: https://qiita.com/dankogai/items/835c3a7cbe1a87c73cfd

で、前述の通り Accelerate が 使える macOS と iOS では使うようにしてあります(手動でオンオフも可能)。

予定としては、新[PONS]はこれらを統合する「スーパーモジュール」として生まれ変わる予定です。といっても実際`Package.swift`でこれらを引っ張ってきてまとめるだけなのでそちらは大したことはないのですが。

とりあえず以上です。

Enjoy!

Dan the Protocol-Oriented Swift Programmer