Most self-aware

もっとも自己認識的

受賞者:Volker Diels-Grabsch

引用元:https://www.ioccc.org/2019/diels-grabsch2/prog.c

審査員・作者による説明:https://www.ioccc.org/2019/diels-grabsch2/hint.html

動作

自分自身のSHA-512を出力する。

$ gcc -o prog prog.c

$ ./prog
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68

$ sha512sum prog.c
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68  prog.c

解説

コードのSHA-512を計算して単純にコード自身に埋め込むと、SHA-512が変わってしまうので、一見するとむずかしい問題設定。 そのため、2^512の空間をブルートフォースして作ったとか、名無しの権兵衛が作ったSHA-512衝突アルゴリズムを使ったなどと冗談が書いてある。

これは本質的にはQuine。 Quineのテクニックによって自分自身のソースコードを再構築し、そのまま出力する代わりにそのSHA-512を計算して出力する。 ただしだいぶ発展的な形になっている。

まず、最下行の先頭の"までのSHA-512計算を事前にやっておき、計算状態をダンプする。 このダンプが、最下行の文字列リテラルにエンコードされて含まれている。 このプログラムはダンプされた計算状態から計算を再開し、最後の文字列リテラルの中身と、終端の" ; \nの3文字分を進めて、最終的なSHA-512を出力するようになっているとのこと。 フルのSHA-512を実装するのではなく、1ブロック分の計算に特化している。

苦労した点としては、SHA-512を計算するための定数が80個あること。 これをコードにもたせると巨大すぎるが、これらの英数は最初の80個の素数の3乗根を元に作られているので、自力で計算することにした。 ただし64ビットの精度で求める必要があり、double型は52か53ビット程度しか精度がないので使えない。 また、32ビット環境でも動くようにしたいということもあり、24ビットずつ3つの整数に分割して計算したとのこと。

Quineを使うという基本的なアイデアに留まらず、この課題に特化して作りこんであり、結果的に簡潔にまとまっていてとても好印象。