IOCCCコード解析その6

久しぶりに料理をした。ゴーヤチャンプルを作ったよ。ん〜まあまあかな〜豆腐を沢山入れたおかげで美味しかったよ〜健康にいいかな〜と思っておりますよ。
・1991年「Best One Liner」受賞作品(fine.c)

main(a,b){while( (a=getchar())+1)putchar((b=64^a&223)&&b<27?a&96|(b+12)%26+1:a);}
まあ〜こんな感じかな、良くわからないのでちょっと整形してみた。
main(a,b){
while( (a = getchar()) + 1) {
putchar(
((b = 64 ^ a & 223) && b < 27) ? (a & 96 | (b + 12) % 26 + 1) : (a)
);
}
}
まずは〜
main(a,b)
main関数ですね〜引数に a 、b があるけど、これは変数宣言しているって事でいいのかな。main関数内部で宣言するよりもこっちのほうがわかりにくい?良くわからんが特に重要そうじゃないよね。
while( (a = getchar()) + 1) {
標準入力より文字を取得して変数 aに格納している。+1 ってのは入力された文字がNULLであればループが終了してしまうので、それを防ぐ意味合いがあると思うんだが・・・あと終了はctl+Dだね。
putchar(...)
これは〜何かを出力していますね。入力された文字をそのまま表示していては芸がないので何をしているのかな〜と言うわけでコンパイルして実行してみた。あは!これはいわゆる rot13 ってやつですね〜単純な暗号のアルゴリズムですね。まっそれは言いとしてひとつづつ見ていく。

((b = 64 ^ a & 223) && b < 27) ? (a & 96 | (b + 12) % 26 + 1) : (a)
三項演算子です。
ん〜この部分で rot13 の実装をしています。ちなみに rot13 てのは取得したアルファベットを13個隣にづらして暗号化するってやつ。例えば「a」-->「n」、「b」-->「o」ってな感じ。そうなのだよ、何をやっているかわかったし、もういいかな〜と思ったんですけど、もう少し考えてみる。64とか223とか意味不明な数値が多いのでその意味を少し考えてみる。

入力される文字
a ... 97(0x61)
z ... 122(0x7a)
A ... 65(0x41)
Z ... 90(0x5a)

として、まず64との排他的論理和を求めてみる。


64 ^ a(97) = 0100 0000 ^ 0110 0001 = 0010 0001
64 ^ z(122) = 0100 0000 ^ 0111 1010 = 0011 1010
64 ^ A(65) = 0100 0000 ^ 0100 0001 = 0000 0001
64 ^ Z(90) = 0100 0000 ^ 0101 1010 = 0001 1010
ふと気がついたんだがascii codeで英字の大文字と小文字の違いって6bit目が立っているか立っていないかの違いなのか?asciiコード表を見たところそんな感じ。へ〜これって常識?初めて知ったよ。で64との排他的論理和を求める事により7bitを0にしている。何故そのような事をやるのかと言うと〜入力された文字を1〜26の数値に置き換えるためでいいのかな〜?英字かを判断しているのかな?

更に
& 223(1101 1111)を行うことにより6ビット目を0にしている。これによって大文字と小文字のコードが同じになる。


64 ^ a(97) = 0100 0000 ^ 0110 0001 = 0010 0001 & 1101 1111 = 0000 0001(1)
64 ^ z(122) = 0100 0000 ^ 0111 1010 = 0011 1010 & 1101 1111 = 0001 1010(26)
64 ^ A(65) = 0100 0000 ^ 0100 0001 = 0000 0001 & 1101 1111 = 0000 0001(1)
64 ^ Z(90) = 0100 0000 ^ 0101 1010 = 0001 1010 & 1101 1111 = 0001 1010(26)

で〜求めた数値が27以下(英字)であれば、次の処理へ(TRUE側)、そうでなければ、その値をそのまま画面に表示する(FALSE側)

TRUE側の処理、(a & 96 | (b + 12) % 26 + 1)、これも順に見ていく。

a & 96(0110 0000)


a(97) = 0110 0001 & 0110 0000 = 0110 0000
z(122) = 0111 1010 & 0110 0000 = 0110 0000
A(65) = 0100 0001 & 0110 0000 = 0100 0000
Z(90) = 0101 1010 & 0110 0000 = 0100 0000
大文字か小文字かを上位4bitに格納
(b + 12) % 26 + 1
b は 1〜26 の数値、+12する事によりローテート処理を実施。+1は0を防ぐためだと思われる。
ローテート後、大文字か小文字かの特定。

0110 0000 | 0000 1101 = 0110 1101(n)
0110 0000 | 0000 1100 = 0110 1100(m)
0100 0000 | 0000 1101 = 0100 1101(N)
0100 0000 | 0000 1100 = 0101 1100(M)
rot13の出来上がり。

以上なり。そんな感じだと思うんだけどね自信はあんまりないよ〜