[Piet]ヴィジュネル暗号プログラム(その1)

前回に引き続き暗号化プログラムの作成をします。 今回扱うのはヴィジュネル暗号です。

ヴィジュネル暗号とは

今回も前回同様、英語(アルファベット)の文章を暗号化することが前提です。

前回のカエサル暗号は、平文(暗号化前の文章)の各文字を、アルファベット順にある文字数だけずらして暗号文を作りました。

この暗号には弱点があり、つまりずらす文字数のパターンが25通りしかありません。したがって、25通り総当たりで文字をずらしてみれば、あっさり平文が見つかることになります。

この弱点を改善したのが「ヴィジュネル暗号」です。 ヴィジュネル暗号は平文を1文字暗号化するごとに、ずらす文字数を変えていきます。

たとえば、「I have a pen.」という平文を暗号化します。 暗号化は"I"→"h"→"a"→"v"→"e"→...の順に行いますが、 ずらし方を、たとえば3文字、14文字、6文字の順に変えていくとします。

  • "I"は3文字ずらすとして、"L"に
  • "h"は14文字ずらして"v"に
  • "a"は6文字ずらして"g"に
  • "v"は再び3文字ずらして"y"に
  • ...

1文字だけ見ればシーザー暗号と変わらず25通りの変換しかできませんが、 3文字置きにずらし方を変えていくと、25の3乗の15625通りの暗号化ができるようになります。 さらに、3文字置きではなく、5文字置き、10文字置きなど、 ずらし方は自由ですので、暗号化のパターンはさらに増えます。

ずらす文字数を数字で覚えておくのは大変なので、しばしばアルファベットで記憶する方法がとられます。 ずらす文字数が0文字なら”a”、1文字なら"b"のように数値を変換すると、 上の例の場合はずらす文字数が"dog"のようにまとめられ、簡単に記憶することができます。

このように、暗号化のヒントになる"dog"のような文字列を暗号化の「鍵」と言います。

先ほどの例で全ての文字を暗号化すると、次のようになります。

平文 鍵文字 ずらす文字数 暗号文
I d 3 L
h o 14 v
a g 6 g
v d 3 y
e o 14 s
a g 6 g
p d 3 s
e o 14 s
n g 6 t

結局"I have a pen."は"L vgys g sst."と暗号化されます。

ヴィジュネル暗号を実装する

仕様

それではPietで実装していきます。仕様は次のようにしました。

  • 入力:「平文.鍵.」のように、平文と鍵をピリオド( . )で区切って入力
    • 平文:使用可能な文字は、半角英数字(ピリオド除く)
    • 鍵:使用可能な文字は半角英字のみ

上の例なら"I have a pen.dog."と入力すれば暗号化できます。

入力に関する補足(クリックして開く)

  1. 平文にピリオドは使用不可(平文終了の目印になるため)
  2. 平文の英字以外はそのまま出力し、鍵文字を更新しない
  3. 平文には大文字・小文字いずれも使用可能
  4. 鍵には大文字・小文字いずれも使用可能

2については、 たとえば上記の"I have a pen"のスペースを消したり英字以外の記号を入れて "#Ihave!a pen"とすると、"#Lvgys!g sst"となります。 記号はそのまま出力されますが、 "have"が"vgys"に、"pen"が"sst"に変換されるなど、平文のアルファベット部分の暗号化の結果は変わりません。

3については、 入力を"I HAVE A PEN"とすると、出力は"L VGYS G SST"となります。 入力を大文字にすると出力も大文字になりますが、暗号文は変化しません。

4については、 鍵の"dog"を"DOG"や"Dog"にしても、"I have a pen"は"L vgys g sst"に暗号化されます。 (大文字・小文字も変わりません。)

処理の流れ

処理の流れはおおよそ次のようにしました。

  1. 平文と鍵を入力する
  2. ★ループ開始地点
  3. 平文から暗号化する文字を取り出す(全て暗号化済みなら終了)
  4. 鍵から暗号化に使う文字を取り出す
  5. 平文の文字が英字以外ならそのまま出力して★ループ開始地点へ戻る
  6. 鍵の文字がアルファベット順で何番目か調べる(K番目とする)
  7. 平文の文字をK文字アルファベット順にずらして出力する
  8. 暗号化に使う鍵文字の位置を1文字ずらす
  9. ★ループ開始地点に戻る

この処理をPietソースコード生成用のコードで記述すると、次のようになります。 命令397個と、かなり長くなったので、興味のある方は開いてみてください。

処理コード(クリックして開く)

goto:inputPlain
#------------------------------
:fInput
push1 dup sub push3 push1 roll

:fInput_loop
inc push3 push2 roll dup push4 push1 roll
if:fInput_isOK:fInput_push

:fInput_push
dup push3 push2 roll dup push4 push1 roll push5 add push1 roll
push4 push3 roll push1 add push4 push1 roll

:fInput_isEnd
push2 push2 push1 roll
goto:fIsPeriod

:fInput_isEnd_retIsPeriod
pop if:retInput:
pop goto:fInput_loop

:fInput_isOK
push1 push2 push1 roll goto:fIsAlpha

:fInput_isOK_retIsAlpha
pop if:fInput_push:
push1 push2 push1 roll goto:fIsPeriod

:fInput_isOK_retIsPeriod
pop if:fInput_push:
pop goto:fInput_loop

:retInput
pop
push2 push1 roll pop
dup push1 add push2 push1 roll
dup push1 dup sub sub if::inputPlain_retInput
dup push1 sub if::inputKey_retInput
end

#----
:fIsAlpha
:fIsAlpha_isUC
dup dup push2 push3 push4 add mul
dup push1 sub push2 push3 add mul push1 sub push2 push1 roll
push1 add push2 push3 mul mul push1 add push4 push1 roll
gt push3 push1 roll gt
add push2 sub if:fIsAlpha_isLC:fIsAlpha_UC

:fIsAlpha_isLC
dup dup push2 dup dup dup dup mul mul mul mul dup push3 mul push2 push1 roll
push2 dup mul mul push5 sub push4 push1 roll
gt push3 push1 roll gt
add push2 sub if:fIsAlpha_notAlpha:fIsAlpha_LC

:fIsAlpha_notAlpha
push1 push1 sub goto:retIsAlpha

:fIsAlpha_UC
push2 push1 sub goto:retIsAlpha

:fIsAlpha_LC
push1 push2 sub goto:retIsAlpha

:retIsAlpha
push3 push2 roll
dup push1 sub if::fInput_isOK_retIsAlpha
dup push2 sub if::fCalcBase_retIsAlpha
end

#-------
:fIsPeriod
dup push1 push3 dup dup push2 add mul mul add sub not push3 push2 roll
dup push1 sub if::fInput_isOK_retIsPeriod
dup push2 sub if::fInput_isEnd_retIsPeriod
end

#--------
:fCalcBase
push2 push2 push1 roll goto:fIsAlpha

:fCalcBase_retIsAlpha
pop dup if::fCalcBase_notAlpha
push3 dup mul dup mul push4 dup mul push3 push2 roll mul sub
goto:retCalcBase

:fCalcBase_notAlpha
pop push1 dup sub goto:retCalcBase

:retCalcBase
push3 push2 roll
dup push1 sub if::ENC_key_retCalcBase
dup push2 sub if::ENC_plain_retCalcBase
end

#------------------------------
:inputPlain
push1 dup sub dup goto:fInput

:inputPlain_retInput
pop

:inputKey
push1 push2 push1 roll
goto:fInput

:inputKey_retInput
pop push4 push1 roll push1 sub push4 push2 roll pop
goto:ENC_loop

#-----
:ENC_loop
push3 push2 roll dup if::ENC_clearKey
dup push1 sub push4 push1 roll
push3 push2 roll dup
push4 push1 roll dup
push3 push1 roll add
push4 add push1 push2 sub roll
push2 push1 roll
push4 add push1 push2 sub roll dup
push2 push3 mul push1 roll

:ENC_key
push1 push2 push1 roll goto:fCalcBase

:ENC_key_retCalcBase
pop sub push2 push1 roll

:ENC_plain
push2 push2 push1 roll goto:fCalcBase

:ENC_plain_retCalcBase
pop dup if::ENC_loop_notAlpha
dup push3 push1 roll sub push3 push2 roll
add push2 dup dup dup mul mul push3 mul add mod add
outc goto:ENC_loop

:ENC_loop_notAlpha
pop outc pop push4 push3 roll
push3 push2 roll dup push4 push1 roll
push3 add push1 roll goto:ENC_loop

# -----
:ENC_clearKey
push3 push2 roll

:ENC_clearKey_loop
dup if::ENC_clearPlain
dup push3 add push1 push2 sub roll
pop push1 sub goto:ENC_clearKey_loop

:ENC_clearPlain
pop

:ENC_clearPlain_loop
dup if::ENC_clear_done
push3 push2 roll
pop push1 sub goto:ENC_clearPlain_loop

:ENC_clear_done
pop pop
end

Pietソースコード生成

先ほどのコードをもとに、Pietソースコードを生成してみます。

ヴィジュネル暗号ソースコード(2倍に拡大)

出力画像サイズ427 × 470(上の画像は2倍に拡大しているので854x940)でした。 今までに比べてかなり大きな部類です。

npietで試してみると次のようになります。 暗号化する文章ですが、Wikipediaに具体例があったので、 動作確認がてら正しく暗号化されるかやってみます。

平文 :THEOLDMANANDTHESEA
鍵  :athensparisstlouis
暗号文:talsyvbaeifvmssmms
...

Wikipedia, ヴィジュネル暗号, https://ja.wikipedia.org/wiki/%E3%83%B4%E3%82%A3%E3%82%B8%E3%83%A5%E3%83%8D%E3%83%AB%E6%9A%97%E5%8F%B7] (as of June 2, 2022, 10:27 GMT).

この場合、入力は「THEOLDMANANDTHESEA. athensparisstlouis.」となります。

./npiet VigenereCipherx2.png -cs 2
? THEOLDMANANDTHESEA. athensparisstlouis.
? ? (中略) ? TALSYVBAEIFVMSSMMS.

仕様の都合で大文字・小文字が異なりますが、確かに正しく暗号化できているようです。

忘れもの

そして、いま気づいたのですが、このプログラムに復号化処理(=暗号文を平文に戻す処理)を入れるのを忘れていました・・・(´・ω・`)

この修正にはちょっと手間がかかりそうです。気が向いたら修正します。

プログラムの解説

さて、今回コードが長く処理も複雑です。 文字だけで解説するのは難しいので、図で説明しようと思っているのですが、 図を作るのに手こずっています。

そこで今回は解説まではせず、全体のフローの流れだけを示して、 とりあえず複雑な流れなんだなー、と実感していただくところで終えようと思います。

先ほどのプログラムの中にコロン「:」で始まる行があります。 大雑把にいうと、それがひとまとまりの処理の開始地点を表しています。 このコロンのブロック同士の流れを図にすると、 次のようになります。

ヴィジュネル暗号の処理の流れ

処理は頂上の「:1」というブロックから始まり、 中央右の丸ブロック「:ENC_clear_done」で完了します。

その間、キー入力受付、アルファベット判定、ピリオド判定、暗号化計算など、 さまざまな処理を行なっています。 図の準備ができたら別記事で解説しますので、もう少々お待ちください。

ではまた。

[Piet]カエサル暗号プログラム

今回はカエサル暗号を作るPietプログラムを作ってみました。

カエサル暗号とは

以下、英語(アルファベット)の文を暗号化する前提で説明します。

カエサル暗号とは、平文(暗号化前の文章)の各文字を、アルファベット順に一定の文字数だけずらして暗号文を作る手法です。

1文字ずらす場合は、平文に出てくる"A"を"B"に、"B"を"C"に、…、"Z"を"A"に、それぞれ変換します。

2文字ずらす場合は、平文に出てくる"A"を"C"に、"B"を"D"に、…、"Y"を"A"に、"Z"を"B"に、それぞれ変換します。

たとえば、"HELLO FOX"という平文を、カエサル暗号で暗号化することを考えます。暗号化の際にアルファベット順に5文字ずらすとします。5文字の場合は、"H"は"M"に、"E"は"J"に、…と変換されるので、暗号文は、"MJQQT KTC"となります。

カエサル暗号はわかりやすいですが、簡単に解読されやすい暗号でもあります。 アルファベットは26文字しかないため、例えば、1文字ずらす場合と27文字ずらす場合では同じ暗号化結果が出ます。 従って暗号化のパターンは、ずらす文字数が1文字、2文字、…、25文字の25通りしかありません。 (26文字ずらすと、ずらした結果が平文と一致するので、暗号になりません。) 何文字ずらしたかわからなくても、最悪25通り全ての可能性を調べ上げれば解読できるのです。

カエサル暗号を実装する

暗号化のフローは次のようになります。

  1. 平文を入力する(ピリオドが入力されたら終了)
  2. 暗号化のときにずらす文字数を入力する
  3. 暗号化ループ開始
  4. 全ての平文が暗号化されていたら終了
  5. 未暗号の平文のうち、先頭1文字を取り出す
  6. 英字なら暗号化して出力、そうでないならそのまま出力
  7. 暗号化ループ先頭に戻る

この処理をPietソースコード生成用のコードで書くと次のようになります。

push1 dup sub

:CaesarCipher_input_loop
inc dup push3 push2 roll push1 add push2 push1 roll goto:CaesarCipher_input_isperiod

:CaesarCipher_input_isperiod
dup push2 dup dup dup dup push1 add mul mul mul push1 sub mul sub not if:CaesarCipher_input_key:
pop goto:CaesarCipher_input_loop

:CaesarCipher_input_key
pop inn

:CaesarCipher_check_key
dup push1 dup sub gt if:CaesarCipher_encription:
push2 dup dup dup push3 mul mul mul add add goto:CaesarCipher_check_key

:CaesarCipher_encription
dup outn push2 push1 roll

:CaesarCipher_encription_loop
dup if::CaesarCipher_end
dup push3 push1 roll push2 add push1 push2 sub roll
dup push3 push2 roll dup push4 push3 roll goto:isalpha

:CaesarCipher_encription_isalpha
dup if:CaesarCipher_encription_case_alpha:
pop outc pop push2 push1 roll pop
goto:CaesarCipher_encription_next

:CaesarCipher_encription_case_alpha
push2 dup mul dup mul mul push3
dup mul dup mul push2 push1 roll sub dup
push4 push2 roll add push2 push1 roll sub
push2 dup dup dup push3 mul mul mul add mod add
outc push2 push1 roll pop goto:CaesarCipher_encription_next

:CaesarCipher_encription_next
push2 push1 roll push1 sub goto:CaesarCipher_encription_loop

:CaesarCipher_end
pop pop end

#-------
:isalpha

:isalpha_upper
dup dup push2 push3 push4 add mul
dup push1 sub push2 push3 add mul push1 sub
push2 push1 roll push1 add push2 push3 mul mul push1 add
push4 push1 roll

:isalpha_upper_judge
gt push3 push1 roll gt add push2 sub if:isalpha_lower:isalpha_UC

:isalpha_lower
dup dup push2 dup dup dup dup mul mul mul mul dup push3 mul
push2 push1 roll push2 dup mul mul push5 sub push4 push1 roll

:isalpha_lower_judge
gt push3 push1 roll gt add push2 sub if:isalpha_not_alpha:isalpha_LC

:isalpha_not_alpha
push1 push1 sub goto:isalpha_end

:isalpha_UC
push2 push1 sub goto:isalpha_end

:isalpha_LC
push1 push2 sub goto:isalpha_end

:isalpha_end
goto:CaesarCipher_encription_isalpha

ちょっと長いですが解説します。 処理は大まかに「ラベル」ごとに分割できます。「ラベル」とはコロン「:」で始まる文字列のことで、goto命令やif命令のジャンプ先になります。 例えば、先頭付近にある「:CaesarCipher_input_loop」や「:CaesarCipher_input_isperiod」はラベルです。

各ラベルの処理内容は次のとおりです。No.には先ほどの処理フローの対応する項目番号を記入しています。

No. ラベル名 説明
:1 開始地点。スタック初期化
1 :CaesarCipher_input_loop 平文入力ループ開始。入力された文字を受け取る
:CaesarCipher_input_isperiod 入力された平文の文字がピリオドか判定する(※1)
2 :CaesarCipher_input_key 暗号化時にずらす文字数を入力する
:CaesarCipher_check_key ずらす文字数が負の数なら、正の数になるまで26を足す
3 :CaesarCipher_encription 暗号化開始準備
3~5 :CaesarCipher_encription_loop 暗号化ループ開始。未暗号化の平文があるか判定(※2)
6 :isalphaで始まるラベルたち 次に暗号化する文字が英字か判定する
:CaesarCipher_encription_isalpha 処理分岐(※3)
:CaesarCipher_encription_case_alpha 暗号化して出力し「:CaesarCipher_encription_next」に進む
7 :CaesarCipher_encription_next 暗号化ループ終わり。スタックを調整し「:CaesarCipher_encription_loop」に戻る
:CaesarCipher_end プログラム終了

分岐詳細:

  • ※1:
    • ピリオド以外なら平文入力を継続(「:CaesarCipher_input_loop」に戻る)
    • ピリオドなら次の処理へ(「:CaesarCipher_input_key」に進む)
  • ※2:
    • なければ終了(「:CaesarCipher_end」に進む)
    • あれば次に暗号化する文字を準備して「:isalpha」に進む
  • ※3:
    • 英字なら「:CaesarCipher_encription_case_alpha」に進む
    • 英字でないなら、そのまま出力して「:CaesarCipher_encription_next」に戻る

また、ラベル間の処理の遷移を図にすると次のようになります。

カエサル暗号処理のラベル間の遷移図

いくつかのラベルは内部に条件分岐やジャンプ命令を含んでおり、内部でもループが発生しうるため、 薄い灰色の波線ブロックでその様子を表現しています。先ほどの説明と合わせると、プログラムがどのような順に処理をしているのか、なんとなくイメージがつくのではないかと思います。

出力結果

このプログラムをGridPietGeneratorに入力してPietソースコードを自動生成すると次のようになります。

ソースコード(原寸大)

画像サイズは223 × 206でした。

npietで動作確認してみます。

./npiet -tpic CaesarCipher_raw.png
? AbCDefG:HijklMn/OpqrStu vWXyZ.3
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 3DeFGhiJ:KlmnoPq/RstuVwx yZAbC.

私の環境だとこのような結果になりました。 なぜか暗号化後の文章の先頭にずらす文字数の3が出ています (そういう仕様だったかもしれませんが…)が、それ以外を比較すると

AbCDefG:HijklMn/OpqrStu vWXyZ.
DeFGhiJ:KlmnoPq/RstuVwx yZAbC.

となります。英字部分のみ3文字ずれて暗号化されていることがわかります。 大文字・小文字の対応が取れていることや、英字以外の文字がそのまま出力されていることもわかります。

これで暗号化し放題です。

遊んでみる

「hello, world!」を8文字ずらして暗号化してみます。

./npiet -tpic CaesarCipher_raw.png
? hello, world!.8
? ? ? ? ? ? ? ? ? ? ? ? ? ? 8pmttw, ewztl!.

「pmttw, ewztl!.」と暗号化されました。

この暗号文を受け取った人は、8文字逆方向にずらせば元の平文を復元できます。 何文字ずらすかあらかじめ取り決めておく必要があります。

逆方向にずらす場合はずらす文字列として-8を入力すればOKです。

./npiet -tpic CaesarCipher_raw.png
? pmttw, ewztl!.-8
? ? ? ? ? ? ? ? ? ? ? ? ? ? 18hello, world!.

無事平文が出てきました。やったね!

終わりに

今回はカエサル暗号をPietにしてみました。 今までより格段に複雑なので、雰囲気を感じ取っていただければ良いと思います。

次回はさらに複雑なビジュネル暗号を扱ってみます。

それでは、eqq kag msmuz!.

[Piet作ってみた]円周率の近似値表示プログラムの解説

こんにちは。先日1年半ぶりに投稿して、円周率の近似値を表示するプログラムを作ってみました。

ymos-hobby-programing.hatenablog.com

今回はこのプログラムの仕組みを解説をしていきたいと思います。

ソースコードおさらい

円周率の近似値を表示するプログラムをもう一度見てみます。

円周率の近似値を計算する海岸風景

メッシュが荒すぎて海岸風景に見えないところはご愛嬌です。

作り方おさらい

このプログラムはPietソースコードを、海岸風景の写真(をPiet風にリメイクしたもの)に埋め込むことで作られています。埋め込む際には、円周率プログラムの処理部分と、ただの背景部分がごちゃ混ぜにならないようにする必要があるため、人手でやるのは至難の技です。

そこで、

  1. Pietソースコードの生成
  2. 背景画像へのPietソースコード埋め込み

を自動的に行うプログラムを作っています。

github.com github.com

今回は、背景として次のような海岸風景の画像を使い、

背景の元画像

これに円周率の近似値計算と表示処理をするPietソースコードを自動生成して埋め込んでいます。

円周率の近似値計算と表示処理をするPietソースコード

興味があればgithubからソースを落として遊んでみてください。詳細は過去記事でも説明しています。

ymos-hobby-programing.hatenablog.com

ymos-hobby-programing.hatenablog.com

処理の概要

円周率の近似といっても、大したことはしていません。355/113という分数が円周率の近似値として使われるそうなので、単純に355÷113という割り算の結果を表示しただけです。なんてことはない。

処理の流れ

とはいえPietでは整数しか扱えませんので、小学校で教わる割り算の筆算を、自力でPietプログラムに落とし込むことになります。 割り算の筆算の仕方、覚えてますか?電卓信者の私は思い出すのに数分かかりました。

割り算の筆算は次のようにするとおもいます。

  1. 割られる数(355)の左隣に割る数(113)を書き、その間に”)”を書き、割られる数の上に横線を引く
  2. 商(=割られる数の中に割る数がいくつ入っているか)を計算する
    (割る数113の3倍が339、4倍が452なので、商は3)
  3. 割られる数の棒の上、355の右端の5の上に、商”3”を書く(この時だけ小数点を打つ)
  4. 割る数113に商3をかけた339を、割られる数355の下に書き、引き算する(答えは16)
  5. 引き算の答え16の末尾に0をつけた160を、新たに割られる数とみなす
  6. 商(=割られる数の中に割る数がいくつ入っているか)を計算する
    (割る数113の1倍が113、2倍が226なので、商は1です)
  7. 割られる数の棒の上、先ほどの商”3”の右隣に、次の商”1”を書く
  8. 割る数113に商1をかけた113を、割られる数160の下に書き、引き算する(答えは47)
  9. 引き算の答え47の末尾に0をつけた470を、新たに割られる数とみなす
  10. 以下繰り返す

よくみると6は2と同じ処理であり、そのほかも同じ処理を繰り返しています。 また、5などに出てくる「末尾に0をつける」とは、「10倍する」のと同じことです。 これらを意識して処理の流れを書き直すと次のようになります。

  1. 割られる数(最初は355)と割る数(最初は113)を準備する
  2. 割られる数を割る数で割った商(整数)を求める
  3. 商を表示する(最初だけ小数点を表示する)
  4. 割られる数から「割る数×商」を引き算する
  5. 引き算の結果(=余り)を10倍したものを新たな割られる数とする
  6. 2に戻る

今回作ったPietプログラムの処理の流れもおおよそ上の通りです。 ただ、このままだと無限ループするので、 小数以下指定した桁まで表示したら処理を止めるようにします。

Pietソースコード生成

それではまずPietソースコードを自動生成します。 自動生成のためには、処理の流れを専用のプログラミング言語で記述する必要があります。

専用の言語についても過去記事で紹介はしています。Pietの命令を文字起こしして処理順序を書いたものになりますが、難易度はやや高めです。

ymos-hobby-programing.hatenablog.com

ymos-hobby-programing.hatenablog.com

この言語で割り算の筆算処理を記述すると次のようになります。

#割られる数の初期値(355)入力[処理1]
push5 dup push7 mul push2 mul push1 add mul
#割る数の初期値(113)入力[処理1]
:bumbo push3 push2 push1 push2 dup mul add dup dup push1 add add mul mul add
# ループ回数(10回)と小数点を打つかどうかのフラグを初期化
:shori push1 push10 push4 push2 roll
#---ループ開始---
# 計算に使う数値をメモリ上で複製
:dispfracloop dup dup push4 push3 roll dup push4 push3 roll
#商の計算と出力[処理2〜3]
div outn push2 push1 roll
#余りの計算[処理4〜5]
mod push2 push5 mul mul push2 push1 roll
#小数点表示判定[処理3]
push4 push3 roll dup if::pccheck
#小数点表示(初回のみ)
push1 sub push7 push7 mul push3 sub outc 
# ループ終了判定
:pccheck push4 push1 roll push3 push2 roll push1 sub dup if::end
# ループ継続処理[処理6]
push3 push1 roll goto:dispfracloop
##---ループ終了---
# 終了時の割られる数と割る数を表示(デバッグ)
:end push4 push8 mul outc pop outn push4 push8 mul outc outn pop end

このファイルをPietソースコード自動生成プログラムに入力すると、先ほど示したPietソースコード画像が出力されますが、どこでどのような処理をしているのかパッとみただけでは分かりません。

Pietソースコードの解析

Pietソースコードの自動生成プログラムのログを解読すると、どこでどのような処理をしているか分かります。それを図にしてみると次のようになります。

ソースコード図解

プログラムは左上の「割られる数の初期化」から始まって、図中の矢印の順に処理内容が変化していき、最終的に右上の「デバッグ表示」に到達して終了します。途中、赤矢印で示した部分はループ処理です。赤点線は、初回のみ通るルートで、小数点を表示しています。

ちなみに、自動生成されたPietソースコードは、画像フチで処理を行い、中心部分はほとんどソースコードがありません。そのため、画像にソースコードを埋め込んでも、元の画像が崩れにくくなります。

一方で、画像中央には細かい色付きセルが散らばっています。これはPietインタプリタの移動方向を制御するためのもので、上図の矢印の順に処理が進むように配置されています。その結果、Pietインタプリタは画像のフチだけでなく、画像全体を縦横無尽に移動することになります。

実行結果

このPietソースコードを実行すると、円周率の近似値355/113=3.141592920と、ループ終了時の割る数113と割られる数400を表示して終了します。

3.141592920 113 400

最後に

前回紹介した円周率表示プログラムの解説をしました。やっぱり解説するとなると、それなりにブログ書くのに時間かかりますね。

さて、ふとPietで暗号化プログラムが作れないかと思い立ちまして、先日から試しておりました。結果、なんとかうまくいったので次回以降紹介したいと思います。暗号といっても古典的(?)なもので、カエサル暗号やビジュネル暗号などのことです。それぞれの暗号がどのようなものか解説もしながら、次回以降、説明していきたいと思います。

ではまた。