マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

それでも TeX で「ガウス素数の模様」したい人のための何か (2)

【歪みなく宣伝】
2012/12/01 〜 2012/12/25

TeX & LaTeX Advent Calendar

こちらは平常(ry

*  *  *

前回の続き)

TeX で「ガウス素数の模様」してみた

このプログラムで gptexture()\gptexture)の引数に 39 を指定した場合、絶対値 39 以下のガウス整数が「模様の描画」の対象になるので、全体の模様は (−39,−39) から (+39,+39) の範囲に収まることになる。元の Lua のプログラムではこれを 79 行 79 桁のテキストで表現しているが、TeX では次のような図として出力することにする。

\begin{picture}(79,79)(-39,-39)
%......
% 1-4i がガウス素数であるなら次の命令を実行
\put(1,-4){\rule{\unitlength}{\unitlength}}
%......
\end{picture}

ここで図の描画で用いる \unitlength の値を \gptunitlength という寸法値マクロ(\renewcomand で設定する)で設定できるようにした。

gptexture1.luaset()reset()cur() にあたるマクロは次のように成る。

\def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}}
\def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}}
\def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}}

\tcgp@set\tcgp@reset は通常通りの書き換えになっている((ここでは名前参照のマクロに代入される値が定数(0 や 1)なので「nameedef」は不要で \@namedef で済む。))が、\tcgp@cur は少し異様である。cur() は値を返す関数であるから、本則に従うと「手続きへの変換」が必要になるところであろう。ここでは \tcgp@cur を(Lua や C 言語の)「関数」ではなく、単純に TeX の本来の意味での「マクロ」である(つまり単純に文字列の置き換えをするもの)と捉えてほしい。つまり、\tcgp@cur\@nameuse{...} に展開されるマクロであるから、TeX プログラム中の「値」を表すコードとして \@nameuse{...} があるなら、その代わりに \tcgp@cur を書いてもよいはずである。((「マクロ」にならない「関数」との違いについて少し補足する。例えば、\def\ansA{42} というマクロがあると、コード中の「42」という記述を \ansA に置き換えることができる(かも知れない)。これに対して、\def\ansB{\count@=42 \the\count@} のようなマクロは確かに実行すると \ansA と同じく「42」という出力を行うが、これはコード中の「42」という記述の代わりにはならない。ただ実際にはこの推論についても「値を表すコード」(= 必要に応じてマクロが展開される文脈)という留保条件がつく。))

同様な考えで、絶対値(math.abs())を表すコード(前回に紹介した)を「マクロ」\tcgp@abs にすることができる。

\def\tcgp@abs#1{\ifnum#1<0 -\fi #1}

すなわち、\tcgp@abs\tcgp@x は「\ifnum\tcgp@x<0 -\fi \tcgp@x」に展開される。

最後に not の扱いの話をする。if 文であれば、not を処理するのは容易で、単に then と else の部分を入れ替えればよい。

if not (a == 0) then b = a end
↓
\ifnum \tcgp@a=0 \else \tcgp@b=\tcgp@a \fi

これに対して、while 文(\@whilenum)の場合はそのような方法が通用せず、従って、「元の式の否定と同値になる数値比較の式」を何とかして捻り出す必要がある。ここで TeX の if 文が三項演算子((Lua のイディオムでは判り難いのでここでは敢えて C 言語の書き方「(cond ? yes : no)」を採った。))として使えることを思い出すと、「数値比較の結果の真偽値を一度 0/1 の整数に変換してから 0 と比較する」ことで所望の式が得られる。

while not (a == 0) do ... end
↓
while ((a == 0) ? 1 : 0) == 0 do ... end
↓
\@whilenum{\ifnum\tcgp@a=0 1\else0 \fi =0 }\do{ ... }

この定型のコードもマクロ \tcgp@not として定義しよう。((実はこの定義だと #1 の直後の空白文字トークンの扱いが微妙で、例えば \tcgp@{0=\tcgp@a} のように用いると「前の数値」によって吸収されず残ってしまう。ところが \ifnum...\fi の部分自体が比較式の左辺の数値の部分に相当しているので、「1」という数字を読む前にある空白トークンは結局無視されることになる。))

\def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 }
% これで \@whilenum{\tcgp@not{\tcgp@a=0}}\do{ ... }

これで gptexture1.luaTeX プログラムに書き直す準備が整った。

完成したパッケージのコードは以下の通り。パッケージ名は tcgptexture、名前空間は tcgp。公開命令は \gptunitlength\gptexture の 2 つである。

% tcgptexture.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{tcgptexture}

%% 変数定義
%(整数値)
\newcount\tcgp@t
\newcount\tcgp@n
\newcount\tcgp@rn
\newcount\tcgp@x
\newcount\tcgp@y
\newcount\tcgp@px
\newcount\tcgp@py
\newcount\tcgp@qx
\newcount\tcgp@qy
%(配列型)
%\tcgp@ct/*

%%(公開) \gptunitlength{<寸法>}
% 1つの点(四角)の大きさ.
\newcommand*{\gptunitlength}{2.5pt}

%%(公開) \gptexture{<>}
% 絶対値がn以下の範囲での模様を出力する.
\newcommand*{\gptexture}[1]{%
  \begingroup
    \setlength{\unitlength}{\gptunitlength}%
    \tcgp@n=#1
    \ifnum\tcgp@n<1 \tcgp@n=1 \fi
    \tcgp@gpt@setrn
    \tcgp@t=\tcgp@n \multiply\tcgp@t2 \advance\tcgp@t1
    \begin{picture}(\tcgp@t,\tcgp@t)(-\tcgp@n,-\tcgp@n)%
      \tcgp@gpt@init
      \tcgp@gpt@sieve
      \tcgp@gpt@show
    \end{picture}
  \endgroup
}

%% 補助的なマクロ
\def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}}
\def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}}
\def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}}
\def\tcgp@abs#1{\ifnum#1<0 -\fi #1}
\def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 }

%% \tcgp@pixel
\def\tcgp@pixel#1#2{%
  \put(#1,#2){\rule{\unitlength}{\unitlength}}%
}

%% \tcgp@gpt@setrn
\def\tcgp@gpt@setrn{%
  \tcgp@rn=0 \tcgp@t=0
  \@whilenum\tcgp@not{\tcgp@t>\tcgp@n}\do{%
    \advance\tcgp@rn1 \tcgp@t=\tcgp@rn \multiply\tcgp@t\tcgp@rn
  }%
  \advance\tcgp@rn-1
}

%% \tcgp@gpt@init
\def\tcgp@gpt@init{%
  \tcgp@x=-1
  \@whilenum \tcgp@x<\tcgp@n \do{%
    \advance\tcgp@x1 \tcgp@y=-1
    \@whilenum \tcgp@y<\tcgp@n \do{%
      \advance\tcgp@y1 \tcgp@set{\tcgp@x}{\tcgp@y}%
    }%
  }%
  \tcgp@reset{0}{0}\tcgp@reset{1}{0}\tcgp@reset{0}{1}%
}

%% \tcgp@gpt@sieve
\def\tcgp@gpt@sieve{%
  \tcgp@x=0
  \@whilenum \tcgp@x<\tcgp@rn \do{%
    \advance\tcgp@x1 \tcgp@y=-1
    \@whilenum \tcgp@y<\tcgp@x \do{%
      \advance\tcgp@y1
      \ifnum \tcgp@cur{\tcgp@x}{\tcgp@y}>0
        \tcgp@px=\tcgp@x \tcgp@py=\tcgp@y
        \@whilenum\tcgp@not{\tcgp@px>\tcgp@n}\do{%
          \tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1
          \@whilenum \tcgp@t>0 \do{%
            \tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}%
            \advance\tcgp@qx-\tcgp@y \advance\tcgp@qy\tcgp@x
            \ifnum \tcgp@qx<0 \tcgp@t=0 \fi
            \ifnum \tcgp@qy>\tcgp@n \tcgp@t=0 \fi
          }%
          \tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1
          \@whilenum \tcgp@t>0 \do{%
            \tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}%
            \advance\tcgp@qx\tcgp@y \advance\tcgp@qy-\tcgp@x
            \ifnum \tcgp@qx>\tcgp@n \tcgp@t=0 \fi
            \ifnum \tcgp@qy<0 \tcgp@t=0 \fi
          }%
          \advance\tcgp@px\tcgp@x \advance\tcgp@py\tcgp@y
        }%
        \tcgp@set{\tcgp@x}{\tcgp@y}\tcgp@set{\tcgp@y}{\tcgp@x}%
      \fi
    }%
  }%
}

%% \tcgp@gpt@show
\def\tcgp@gpt@show{%
  \tcgp@x=-\tcgp@n \advance\tcgp@x-1
  \@whilenum \tcgp@x<\tcgp@n \do{%
    \advance\tcgp@x1 \tcgp@y=-\tcgp@n \advance\tcgp@y-1
    \@whilenum \tcgp@y<\tcgp@n \do{%
      \advance\tcgp@y1
      \tcgp@px=\tcgp@abs\tcgp@x \tcgp@py=\tcgp@abs\tcgp@y
      \ifnum \tcgp@cur{\tcgp@px}{\tcgp@py}>0
        \tcgp@qx=\tcgp@x \multiply\tcgp@qx\tcgp@x
        \tcgp@qy=\tcgp@y \multiply\tcgp@qy\tcgp@y \advance\tcgp@qx\tcgp@qy
        \tcgp@qy=\tcgp@n \multiply\tcgp@qy\tcgp@n
        \ifnum\tcgp@not{\tcgp@qx>\tcgp@qy}%
          \tcgp@pixel{\tcgp@x}{\tcgp@y}%
        \fi
      \fi
    }%
  }%
}

\endinput

テスト用文書は以下の通り。

[test-tcgptexture.tex]
\documentclass[a4paper]{article}
\usepackage[scale=0.9]{geometry}
\usepackage{tcgptexture}
\begin{document}
\begin{center}
\gptexture{99}
\end{center}
\end{document}

ここまでの内容をマスターしたら、もう「TeX でマンデルブロ集合」も怖くない!?