前回と前々回ではそれぞれC
swift-gmpint
ご存じのとおり、=Int
は、int
同様、
- A.100%自分で実装
- B.既存のライブラリを活用
たとえばJavaScriptでは事実上A.の方法しか採れませんが、
そんなわけで作ったのがswift-gmpintです。MacPortsでインストールしたGMPを、
Homebrew派の皆さん、/opt/
を/usr/
にすればもしかして動くかもしれません。fork welcome!
Quick Start
前口上は抜きにして早速試してみたいという読者は、
① MacPortsをインストール
② MacPortsからGMPをインストール
% sudo port install gmp
③ GitHubからswift-gmpintをインストール
% git clone https://
github. com/ dankogai/ swift-ュgmpint. git ④ Xcodeでプロジェクトをオープン
% open swift-gmpint/
gmpint. xcodeproj ⑤ プロジェクトを実行
標準出力を確認してみてください。2**1024が179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
であることなどが確認できます。さらにmain.
をあれこれ書き換えてみて、
普通のプロジェクトとの違い
それでは、

通常のプロジェクトとの一番の違いは、-lgmp
と-I/
が追加してあります。ほかは通常のSwiftプロジェクトと変わりません。
次にプロジェクト中のファイルをみてみましょう。
- main.
swift - mpz.
swift - gmp-int-Bridging-Header.
h - mpz.
c
特徴的なのが、mpz.
とgmpint-Bridging-Header.
。
![図2 [Using Swift with Cocoa and Objective-C] 図2 [Using Swift with Cocoa and Objective-C]](/assets/images/dev/serial/01/swift-introduction/0006/thumb/TH800_002.png)
ちなみにgmp-int-Bridging-Header.
は、*
プロジェクト名*-Bridging-Header.
という名前をつけてプロジェクトに手で追加してもOKです

それでは、gmp-int-Bridging-Header.
を見てみましょう
#include <gmp.h>
void gmpint_seti(mpz_t *op, long i);
void gmpint_sets(mpz_t *op, const char *str, int base);
void gmpint_unset(mpz_t *op);
size_t gmpint_strlen(mpz_t *op, int base);
char *gmpint2str(mpz_t *op, int base);
int gmpint_fits_int(mpz_t *op);
long gmpint2int(mpz_t *op);
int gmpint_cmp(mpz_t *op, mpz_t *op2);
void gmpint_negz(mpz_t *rop, mpz_t *op);
void gmpint_absz(mpz_t *rop, mpz_t *op);
void gmpint_lshift(mpz_t *rop, mpz_t *op, mp_bitcnt_t bits);
void gmpint_rshift(mpz_t *rop, mpz_t *op, mp_bitcnt_t bits);
void gmpint_addz(mpz_t *rop, mpz_t *op, mpz_t *op2);
void gmpint_subz(mpz_t *rop, mpz_t *op, mpz_t *op2);
void gmpint_mulz(mpz_t *rop, mpz_t *op, mpz_t *op2);
void gmpint_divmodz(mpz_t *r, mpz_t *q, mpz_t *op, mpz_t *op2);
void gmpint_powui(mpz_t *rop, mpz_t *op, unsigned long exp);
void gmpint_powmodz(mpz_t *rop, mpz_t *op, mpz_t *exp, mpz_t *mod);
これらの関数名が、int
はSwift上でInt32
になりますし、char*
はUnsafePointer<Int8>
になります。Cの型名とSwiftの型名の対応表は
そしてこれらの関数がmpz.
に実装されているのですが、
void gmpint_seti(mpz_t *op, int i) {
mpz_init_set_si(*op, i);
}
見てのとおり、
もうこの時点で、mpz.
です。まずは型をみてみましょう
class GMPInt {
private var mpz = mpz_t()
init(){ gmpint_seti(&mpz, 0)}
init(_ mpz:mpz_t) { self.mpz = mpz }
init(_ s:String, base:Int=10){
s.withCString {
gmpint_sets(&self.mpz, $0, Int32(base))
}
}
// to work around the difference between
// GMP's 32-bit int and OS X's 64-bit int,
// we use string even for ints
convenience init(_ i:Int) { self.init(String(i)) }
deinit {
gmpint_unset(&mpz)
}
func toInt() -> Int? {
return gmpint_fits_int(&mpz) == 0 ?
nil : Int(gmpint2int(&mpz))
}
var asInt: Int? {
return self.toInt()
}
}
まず目につくのが、init()
が2種類あること。GMPInt(42)
もGMP("42")
も受け付けます。文字列からの初期化が必要なのは、GMPInt("012345678901234567890123456789012345678901")
のように、init(_ i:Int)
の実装は、String(i)
で文字列化しています。コメントにあるとおり、
次の特徴は、deinit()
の存在。GMPはCライブラリdeinit()
はまさにこのような場合のために存在します。Swiftがメモリを解放する際、DESTROY
相当のメソッドであると覚えておくとよいかもしれません。余談ですが、
型定義ができたところで、
extension GMPInt: Printable {
func toString(base:Int=10)->String {
let cstr = gmpint2str(&mpz, Int32(base))
let result = String.fromCString(cstr)
free(cstr)
return result!
}
var description:String { return toString() }
}
リスト3でふつうにprintln()
できるようになり……。
extension GMPInt: Equatable, Comparable {}
func <(lhs:GMPInt, rhs:GMPInt)->Bool {
return gmpint_cmp(&lhs.mpz, &rhs.mpz) < 0
} func ==(lhs:GMPInt, rhs:GMPInt)->Bool {
return gmpint_cmp(&lhs.mpz, &rhs.mpz) == 0
}
これで等号不等号が使えるようになりました。
あとは、
/// unary +
prefix func +(op:GMPInt) -> GMPInt { return op }
/// binary +
func +(lhs:GMPInt, rhs:GMPInt) -> GMPInt {
var rop = GMPInt()
gmpint_addz(&rop.mpz, &lhs.mpz, &rhs.mpz)
return rop
}
func +(lhs:GMPInt, rhs:Int) -> GMPInt {
return lhs + GMPInt(rhs)
}
func +(lhs:Int, rhs:GMPInt) -> GMPInt {
return GMPInt(lhs) + rhs
}
見てのとおり、Int
な場合も受け付けるようにして、
まとめ
というわけで任意精度整数がSwiftでも使えるようになったのですが、
23 130 981 gmpint/gmpint-Bridging-Header.h 41 117 867 gmpint/main.swift 64 220 1632 gmpint/mpz.c 233 827 5800 gmpint/mpz.swift 361 1294 9280 total
560 2368 15429 ../js-math-bigint/bigint.js
bigint.
はJavaScriptで必要最低限の任意精度整数演算を実装したものですが、2**1024
を計算したければ(new Math.
などとしなければならないのに対し、GMPInt(2) **1024
で事足りてしまいます。どちらが楽かはご覧のとおりです。
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)