C言語で電卓を作ろう

C言語で簡単な数式を読んで計算するプログラムを作ります。

数式内で使える演算子は「+」「-」「*」の3つで、括弧は使えません。

使える数は0以上の整数で、負の数は使えません。

 

優先順位は「*」が高く、「+」と「-」が低くなるようにします。「2 + 3 * 4」は 20 ではなく 14 になります。

 

3つの変数 a、b、c を用意し、a と c を 0、b を 1 で初期化しておきます。

受け取った数式を最初から順番に読んでいって、「*」があったら b に a をかけ、「+」「-」があったら c に a * b を足します。これらの演算子を読んだ後、a に 0 を代入し、bに「+」なら 1、「-」なら -1 を代入します。数字を読み込んだ場合は a に 10 をかけて足します。

 

これを文字列の終わりまで繰り返したら、a * b + c が計算の答えです。

 

コードはこちら

github.com

 

+と-と*しか無いからこそ実現できるシンプルさです。

でも変数を1つ増やせば「/」も追加できないことはなさそう。

ただ、括弧を使いたいときにこのやり方は使えないので他のやり方を考える必要があります。

X

Arch LinuxGUI環境を構築した。GUI環境は欲しい時にだけ使いたくて、普段の作業はコンソールでやるつもりだったので、startxコマンドでXを起動→$mod+EでXを終了ができるようにした。ウィンドウマネージャはi3を選んだ。

 

xorg-server, xorg-apps, xorg-xinitをインストール。この時点でstartxとするとXが立ち上がり、即座に終了する。

 

i3, dmenu, xtermをインストール。さらにフォントを扱うfontconfig, xorg-font-utils も必要だが、これはXフォントを何か1つインストールすると手に入る(公式リポジトリにある otf-ipafont など)。

 

/etc/X11/xinit/xinitircを~にコピーして編集する。最後のtwmとかxclockとかxtermとかの行は向こうが勝手に走らせているだけなので消す(そもそもtwmとxclockはインストールしてない)。そしてexec i3と書き加える。

 

この状態でstartxとするとi3が起動する。言われるがままにmodキーなどを設定する。$mod+Enterでxtermを起動する。psでi3を探して殺し、コンソール環境に戻る。すると/.config/i3にconfigという設定ファイルが作られているので、これを編集して$mod+Shift+eのショートカットが書かれた行を bindsym $mod+Shift+e exit と書き換える。この状態でstartxとすると$mod+Shift+eで即座にXが閉じるようになっている。

 

キーボードレイアウトは/etc/X11/xorg.conf.dにファイルを作って指定する。00-keyboard.confの1つだけを書いた。

Section "InpurClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XKbLayout" "us"
Option "XKbVariant" "dvp"
EndSection

 

設定は完了した。firefoxをインストールして挙動を確認したりする。あと僕は/.config/i3/configの最後にbindsym $mod+Escape exec i3lock -c000000と書き加えて、即座に画面ロックができるようにしたりした。

Arch Linux

今年の5月7日にWindowsを捨ててArch Linuxに切り替えてからしばらく経ったので、今の所どういう感じで使っているのかをメモしておきたいと思います。

 

最初はBIOS, MBRだったけど8/26にUEFI, GPTでやり直した。パーティションは/boot(512M),  swap(1G), /(残り464.3G)。メモリ4Gもあるんだからswapとか作るだけ無駄だった。次気をつける

 

ブートローダはsystemd-boot。espは/boot。esp/loader/entriesにはarch.confとarch-fallback.confの2つを書いて、timeoutとか面倒だったのでesp/loader/loader.confにはdefaultだけ書いといた。

 

キーマップはDvorak派。/etc/vconsole.confには

KEYMAP=dvorak-programmer.map
FONT=Lat2-Terminus16

と書いた。ちなみにテンキーがないので数字入力するのちょっと面倒。

 

ここからしばらく有線接続を使う。dhcpcdをsystemd startしたら普通にping通った。普段は部屋でスタンドアローン、ネット環境が欲しいときだけ家のリビングでイーサネットみたいなのを数カ月間続けた。

 

git, wgetをインストール。

 

pacman使ってたら周りの人が他のを使うことを勧められた。たくさんあってどれを選べばいいのか分からなかったので常務くんイチオシのyayをインストール。yay自体が公式リポジトリに無いのでgitで入れた。

 

vim派なのでvimを入れた。

C++書くのでboostを入れた。

haskell書くのでghcを入れた。

 

音を鳴らしたかったのでalsa-utilsを入れた。頑張ったら音が鳴った。/etc/modprobe.d/alsa-base.confと/etc/asound.confを頑張って書いて良い感じにした。結構大変だった。

 

コンソール上で画像を表示したかったのでfbidaを入れた。そのままだとfbiで画像が表示できなかった。解決策は何らかのXフォントをインストールして/usr/share/fontsに入れることだった。Notoフォントを入れた。

 

さて。この辺りでなんかGUI環境が欲しくなった。

でもそんなにGUIばっかりに頼りたくはなくて、好きなときにCLIに戻れるようにしたかった。

そこで、Xをインストールするもののディスプレイマネージャは入れず、GUI環境が欲しいときにstartxコマンドを打つことにした。ウィンドウマネージャはi3。

xorg-server、xorg-apps、xorg-init を入れる。この時点でstartxを打つとXが起動して即座に終了する。

Dvorak派なので/etc/X11/xorg.conf.dにあった00-keyboard.confを書き換えた。ここではdvorak-programmerは"dvp"という名前だった。

i3、dmenuを入れて普通にやった。

 

突然WiFiが使いたくなったのでnetworkmanagerを入れてnmtuiを使ってWiFiに繋いだ。

 

日本語が入力したくなったのでfcitx mozcを入れた。

 

動画再生にmplayerを入れた。

画像編集にmyPaintを入れた。

動画編集にflowbladeを入れた。

 

以上、大体こんな感じ。

ネタ切れ(・ω・`)

最近ブログに書くネタが無くてですね

 

最後に更新してからもう半年が経とうとしているんです。切れたんですよネタが。ホント、書くことがない。

 

もういくら考えてもネタが思い付かなくて。

 

それでですね、ついに苦肉の策に出ようと思ったんですよ。

 

「最近ブログに書くネタが無くてですね」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。

 

でもそれだけじゃつまらない。単に一度メタ化するだけじゃ、ネタが無いのと同じですよ。

 

そこでこうすることにしたんです。

 

「「最近ブログに書くネタが無くてですね」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。

 

さらに、「「「最近ブログに書くネタが無くてですね」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。」と書くと3重にメタ化することもできます。

 

ここで気付くわけですね、「さらに、「「「最近ブログに書くネタが無くてですね」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。」と書くと3重にメタ化することもできます。」と書いた時点で既に4重のメタ化が起こっていると。

 

ペアノの公理ってご存じですか?あれに似ていますね。つまり任意の自然数nに対してn重のメタ化が可能となります。

 

おや?私は「任意の自然数nに対してn重のメタ化が可能となります。」と書いてしまいました。これをここに書くと、いったい何重のメタ化になるんでしょう。

 

自然数の範囲を超えてしまいましたね。じゃあこれを、「どんな自然数よりも大きい数」としてωと名付けましょう。

 

さて、ω重のメタ化ができました。

 

ん?「ω重のメタ化ができました」?これは何重のメタ化ですか?

 

ωの次の数。ω+1です。私はω+1重のメタ化を行ったわけです。

 

はい、たった今「私はω+1重のメタ化を行ったわけです。」と書くことでω+2重のメタ化が行われましたね。

 

そして「はい、たった今「私はω+1重のメタ化を行ったわけです。」と書くことでω+2重のメタ化が行われましたね。」と書いたのでメタ化はω+3重です。

 

これを先程と同様に繰り返すと、自然数nに対しω+n重のメタ化が可能です。

 

……んん?「自然数nに対しω+n重のメタ化が可能です」???

 

そうですね。ω+ω、即ちω✕2です。

 

ω✕2です」はω✕2+1。「「ω✕2です」はω✕2+1。」はω✕2+2。「「「ω✕2です」はω✕2+1。」はω✕2+2。」はω✕2+3。これを繰り返せばω✕2+nが作れます。そして「ω✕2+nが作れます。」はω✕3です。

 

これを繰り返していくと、ωと任意の自然数nの積ω✕nが作れます。

 

おや???「ω✕nが作れます」?????(しつこいですよ)

 

はい、ω×ωですね。

 

これが順序数の世界です。

 

ω^ωも作ってみてください。

 

 

ではまた(・ω・`)

みんな大好き,ポインタ/配列/参照 ~多次元配列まとめ~

汎整数型(論理型(bool),文字型(charなど),整数型(intやlongなど))と浮動小数点型を合わせて算術型といい,算術型とvoid型を合わせて基本型といいます。基本型からは「ポインタ型」「配列型」「参照型」の3種類の組込み型が得られますが,この記事ではその中でも特に(単なる基本型へのポインタ型,配列型,参照型を除いた)2重以上のポインタ型,配列型,参照型の扱い方について説明します。constについては説明しません。templateについては説明しません。変数の型だけを見るので,変数名は全てハンガリアン記法の接頭辞のみとします。

まずCにあるポインタ型と配列型から先に説明します。

int i;

iはint型です。関数hoge(int)を宣言しておくと,hoge(i)でhoge(int)が呼び出されます。

intに*,[1],[2]を付けるとそれぞれ次の型が得られます。

int *pi, a1i[1], a2i[2];

piはint*型,a1iはint[1]型,a2iはint[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int*)を宣言しておくと,hoge(pi),hoge(a1i),hoge(a2i)の全てにおいてhoge(int*)が呼び出されるということです。

int*に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int **ppi, *a1pi[1], *a2pi[2];

ppiはint**型,a1piはint*[1]型,a2piはint*[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int**)を宣言しておくと,hoge(ppi),hoge(a1pi),hoge(a2pi)の全てにおいてhoge(int**)が呼び出されるということです。

int[1]に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int (*pa1i)[1], a1a1i[1][1], a2a1i[2][1];

pa1iはint(*)[1]型,a1a1iはint[1][1]型,a2a1iはint[2][1]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[1])を宣言しておくと,hoge(pa1i),hoge(a1a1i),hoge(a2a1i)の全てにおいてhoge(int(*)[1])が呼び出されるということです。hoge(int**)に渡したいときはキャストします。

int[2]に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int (*pa2i)[2], a1a2i[1][2], a2a2i[2][2];

pa2iはint(*)[2]型,a1a2iはint[1][2]型,a2a2iはint[2][2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[2])を宣言しておくと,hoge(pa2i),hoge(a1a2i),hoge(a2a2i)の全てにおいてhoge(int(*)[2])が呼び出されるということです。

変数Aの型がTであるとき,&AはT*型です。即ちint(**[2])[3]型変数a3ppa2iのアドレス&a3ppa2iはint(**(*)[2])[3]型です。

変数Bの型がT*であるとき,*BはT型です。即ちint(*(**)[4])[2]型変数a2pa4ppiの参照*a2pa4ppiはint(*(*)[4])[2]型です。

変数を配列型にキャストすることはできません。reinterpret_castとかそういう問題ではなく,言語仕様上絶対に不可能です。

int a0i[0]はint[0]型です。

sizeof iが4,sizeof piが8であった場合,sizeof a1iは4×1=4,sizeof a2iは4×2=8,sizeof ppiは8,sizeof a1piは8×1=8,sizeof a2piは8×2=16,sizeof pa1iは8,sizeof a1a1iは4×1×1=4,sizeof a2a1iは4×1×2=8,sizeof pa2iは8,sizeof a1a2iは4×2×1=8,sizeof a2a2iは4×2×2=16です。配列+整数のsizeof,即ちsizeof(a2i+1)やsizeof (a2a1i+0)などは全てsizeof pi(=8)に等しくなります。sizeof *a2a1iは*a2a1iがint[1]型なのでsizeof a1i(=4)に等しくなります。sizeof *(a2a1i+0)も同じくsizeof a1iに等しくなります。a2a1i[0]は*(a2a1i+0)の糖衣構文なので,sizeof a2a1i[0]も同じくsizeof a1iに等しくなります。

C++にはポインタ,配列に加え,参照というものがあります。

関数hoge_cpp(int&)をhoge_cpp(i)で,関数hoge_cpp(int&&)をhoge_cpp(i+0)で,関数hoge_cpp(int*&)をhoge_cpp(pi)で,関数hoge_cpp(int*&&)をhoge_cpp(pi+0)やhoge_cpp(a1i)やhoge_cpp(a1i+0)で,関数hoge_cpp(int(&)[1])をhoge_cpp(a1i)で,関数hoge_cpp(int(&)[2])をhoge_cpp(a2i)で呼び出せます。hoge_cpp(a1i)でhoge_cpp(int(&)[2])を呼び出すことはできないので,テンプレートを使って配列の大きさごとに関数を作れば(使うたびに関数が増えますが)関数内で配列の大きさを知ることができます。

とりあえずこの記事はここまでです。部分的に情報密度が高くなってしまいましたが,その一部でもあなたの役に立てば幸いです。

プログラマーじゃなくても分かる,Krileのクエリの書き方

2018/9/28追記

Krile は死んだ。

 

 

twitterを本気でやるならやっぱりKrile Starryeyesを使うのが一番。

セットアップの仕方は以下のサイトに書かれています。

Krile StarryEyesのセットアップの仕方 - -

Krileではタブというものがあり,「自分がフォローしている人のツイートを表示するタブ」「自分宛てのリプを表示するタブ」「自分の名前が含まれるツイートを全て表示するタブ」など,一つ一つのタブに表示するツイートを指定することができます。

しかし指定するための「クエリ」の書き方が分からない…!!という人も多いのではないでしょうか。

実はクエリの書き方は次のサイトに全て書かれています。

KQ_Overview · karno/StarryEyes Wiki · GitHub

しかしこれ…文体がプログラマー向けなんですよね。

まぁクエリを書くのはプログラミングと同じなので仕方ない。

でもKrileを使う人がみんなプログラマーというわけではありません。なのでここでは,プログラマーでない人のためにKrileのクエリの書き方を説明したいと思います。

(おっと…プログラミングが小学校で義務教育化されるんだっけ?まぁそうなったらこういう記事の需要も無くなっていくのでしょうね…)

クエリの構成

Krileのクエリは前半部分と後半部分に分かれています。前半が「from」で始まる「ソースクエリ」,後半が「where」で始まる「フィルタクエリ」です。例えばクエリが「from local where text contains "love"」なら「from local」がソースクエリで,「where text contains "love"」がフィルタクエリです。

ソースクエリではツイートを取得する範囲を指定し,そのうちフィルタクエリで指定した条件を満たすものだけがタブに表示されます。

例えばソースクエリでは「タイムラインに流れてくる全てのツイート」「検索でヒットした全てのツイート」「特定の人の全てのツイート」などが指定できて,フィルタクエリでは「本文中に~を含む」「~RT以上されている」「リスト~に登録されていてIDに数字0を含まない人のうち~人以上にいいねされている」などが細かく指定できます。

ソースクエリ

fromの後に書ける単語がいくつか決まっています。

local (allと書いても同じ)

「from local」とすると,Krileを使っている間に一度でも保存されたツイート全てが対象になります。タイムラインやリプライなど自分に関連しているツイートならFF内外は関係ないのですが,twitter上のツイートが全て検索できるわけではありません。

「from local: ~」のように,コロン「:」で区切ってその後にKrileのタブ名を指定すると,そのタブ内にある全てのツイートが対象になります。*1

 home

「from home」とすると,タイムライン(フォローしている人のツイート)が対象になります。

複数のアカウントを使っている場合は,「from home: "57tggx"」のように,コロン「:」で区切った後「"」2つで囲った部分にIDを書くと,そのアカウントのタイムラインだけが対象になります。

 mention (mentions, reply, repliesも全て同じ)

自分宛の返信が対象になります。複数のアカウントを使っている場合はhomeと同じように「from mention: "ID"」でアカウントを指定します。

message (messages, dm, dmsも全て同じ)

自分宛のDMと自分が送ったDMが対象になります。複数のアカウントを使っている場合はhomeと同じように「from message: "ID"」でアカウントを指定します。

list

指定したリストのツイートが対象になります。「/」で区切って「from list:"自分のID/リスト名"」と書きます。他の人の作ったリストの場合は,自分の持っているアカウントから一つ選んで,「from list:"自分のID/リスト作成者のID/リスト名"」とします。

search (findも同じ)

「from search:"~"」とするとtwitter検索でヒットした全てのツイートが対象になります。普通のtwitter検索とほぼ同じですが,「"そんなの聞いてない"」のような完全一致検索をするときは中の「"」を「\"」(円マーク+「"」)に書き換えて「from search:" \"そんなの聞いてない\" "」とします。

track (streamも同じ)

「from track:"~"」でもツイート検索ができます。こちらはストリーム検索といって,検索結果に新しいツイートが自動で読み込まれ続けるため,サーバに負荷がかかりません。

conv (conversation, talk, treeも同じ)

twitterでは,全てのツイートにID番号が振られています。「from conv:"01234567890123456789"」のようにIDを指定すると,そのツイートとそれに対する全ての返信(リプライツリー)が対象になります。

user

特定の誰かに張り付くならこれ。「from user:"57tggx"」でIDを指定するか,「from user:"#808612487507513344"」(「#」を忘れないこと)で固有IDを指定します。

フィルタクエリ

フィルタクエリを省略すると,取得した全てのツイートがそのまま表示されます。

フィルタクエリには色々な文が書けます。

文の例とその意味,同じと見なせる単語は次の通りです:

text="334" 本文が「334」と一致する textとbody, =と==
text->"334" 本文に「334」が含まれる ->とcontains
text startswith "334" 本文が「334」で始まる startswithとstartwith
text endswith "334" 本文が「334」で終わる endswithとendwith
text->"windows" 本文に「windows」が含まれる(大文字小文字を区別しない)  
caseful text->"windows" 本文に「windows」が含まれる(大文字小文字を区別する)  
favs=3 ふぁぼ数が3 favsとfavorites, favorer, favorers
favs>3 ふぁぼ数が3より多い  
rts>3 RT数が10より多い rtsとretweets, retweeters
rts>favs RT数がふぁぼ数より多い  
rts*rts>favs*2+5 RT数の2乗がふぁぼ数の2倍+5より多い  
rt リツイートである*2 rtとretweet, isretweet, is_retweet
user=@57tggx ツイート主が@57tggx  
user="57tggx" ツイート主のIDが57tggx*3 userとuser.screen_name
user->"tg" ツイート主のIDに「tg」が含まれる  
user=808612487507513344 ツイート主の固有IDが808612487507513344*4 userとuser.id
user.status>100 ツイート主のツイート数が100より多い*5 .statusと.statuses, .statuscount, .status_count, .statusescount, .statuses_count
user.following>100 ツイート主のフォロー数が100より多い .followingと.friend, .friends, .followings, .friendscount, .friends_count, .followingscount, .followings_count
user.follower>100 ツイート主のフォロワー数が100より多い .followerと.followers, .followerscount, .followers_count
user.fav>100 ツイート主のふぁぼ数が100より多い .favと.favs, .favcount, .favorite, .favorites, .favscount, .favs_count, .favoritescount, .favorites_count
user.list>100 ツイート主のリスト被登録数が100より多い .listと.listed, .listcount, .list_count, .listedcount, .listed_count
user.screenname="57tggx" ツイート主のスクリーンネームが「57tggx」 .screennameと.screen_name
user.name->"ビタリスト" ツイート主のハンドルネームに「ビタリスト」が含まれる .nameと.username
user.bio->"数ぽよ" ツイート主のプロフィールに「数ぽよ」が含まれる .bioと.desc, .description
user.loc="どこでもない" ツイート主の設定所在地が「どこでもない」 .locと.location
user.lang="en" ツイート主の設定言語が「en」(=英語) .langと.language
user.protected 鍵アカウント .protectedと.isprotected, .is_protected
via->"twitter web client" twitter web clientから投稿されている*6 viaとfrom, source, client

数字に対して使える記号は次の通りです:「+」(加),「-」(減),「*」(乗),「/」(除),「=」(イコール, 「==」も同じ),「<」(小なり),「<=」(小なりイコール),「>」(大なり),「>=」(大なりイコール),「!=」(ノットイコール)。

複数の条件を重ねるときは「&」「|」を使います。例えば「user.following > 100 | user.follower > 100」は「フォロー数が100より多い」「フォロワー数が100より多い」の少なくとも一方を満たしているという意味で,「user.protected & user.status<=100」は「鍵アカウント」「ツイート数が100以下」の両方を満たしているという意味です。「&&」は「&」,「||」は「|」と同じ意味です。

条件を否定するには「!」を使います。「!user.protected」は「鍵アカウントでない」という意味です。

優先順位は括弧で指定します。「!(text->"57tggx" & user.name!="57tggx")」は「「「text->"57tggx"」かつ「user.name!="57tggx"」」でない」という意味になります。

 ここまでで大体のことはできると思います。とりあえずこの記事はこれで終わりですが,興味があったらこの先を自分で調べてみて下さい。

*1:複数のタブに同じ名前を付けることができますが,そのときは指定した名前を持つタブのうち1つのみが対象となります

*2:Krileではリツイートが元ツイートと別に扱われます。フィルタにrtを指定すると,元ツイートは表示されずリツイートのみが表示されます。

*3:「@57tggx」と「=」で結ばれたときの「user」と,「"57tggx"」と「=」で結ばれたときの「user」は微妙に違う意味になります。前者は「ユーザオブジェクト」,後者は「文字列」です。userはもともとユーザオブジェクトですが,文字列と比較しようとしたときだけIDを表す文字列になります

*4:数値と比較しようとすると,今度は固有IDを表す数値になります

*5:ユーザオブジェクトの後に「.~」と続けることでそのアカウントに関する情報が得られます

*6:正確には「via名の中に"twitter web client"という文字列が含まれている」ですが,「->」を「=」に変えるとなぜか上手くいきません

謎の書きかけ小説メモ

私は既にもう何時間も砂漠を歩き続けていた。足元をかさかさと歩く虫のような虚数や,時折生えている枯れそうな行列にももう慣れた。ずっと前にオアシスで汲んだ集合が水筒の中にあと少し残っていたはず…背負ったリュックを地面に下ろして開くと,関数に吹かれて飛んできた変数がリュックの中に入り込んだ。水筒の中身はあと僅かだった。集合…包含関係…平面図示,境界含む,含まない…。集合を節約しつつ一通りの証明を終え,私は一時的に喉を満たした。

私が迷い込んでしまったこの世界は,なにもかもが数学らしい。間違いのないように一つ一つ証明を確かめながら行動しないといけないため,何をするにも頭が疲れてしまう。というか私はもう気が狂いそうだ。だから一刻も早くこの世界から抜け出すために,私は今歩いているのだ。

私は歩き続けた…そして遂に,この世界の出口に繋がっているとされる巨塔,自然数にたどり着いたのだった。

私は自然数の入り口の前に立った。地面と同じ高さに1階があり,任意の階の上に「上の階」がある。2つの階が異なるならば,その「上の階」も異なる。1階の上には2階,2階の上には3階があり,それが永遠に続いていた。自然数は高く,上の方は当然雲に隠れて見えないのだが,近くで見ると意外と横にも大きかった。高さ加算無限の塔を支えるのだから当然だろう。

私は乗法単位元の定義を確認した上で,石の扉をゆっくりと開けた。

1階。ずっと一人で砂漠を彷徨っていた私は,そこに人がいるのに驚いた。壁にもたれかかって煙草をふかしていた,もう何日も風呂に入っていなさそうな男たちが,そろって私を見た。

「あんた,どこから来た」

「ええと,なんか迷い込んじゃって…気付いたらここに…」

「ふうん?」

彼らはそれ以上私に興味を示さなかった。

私は数1のもつ性質をおさらいしながら,薄暗い空間をまっすぐ進んでいった。任意の整数は1の倍数…即ち1を約数にもつ。1は素数ではない…1が素数だと定義しても何のメリットも得られないだろう,素因数分解の一意性が失われてしまうのが最大の欠点だろうか。1のa乗はaに関わらず1…aは整数,実数,複素数,いや今後どんな数学的概念を考えるとしても,そこに累乗という名の付いた演算を定義するならばおそらく1のa乗は1とするだろう。

しばらく歩き続けると,私の前に大きな壁が立ちはだかった。壁沿いに歩くと何かあるはずだ。右に行くか左に行くか少し迷ったが,どちらでも大して変わらないはずだと思い,右に行くことにした。そうして壁伝いにまたしばらく歩き,ついに壁の向こうに繋がるドアを見つけた。

ドアを開けると,その向こうにはまぶしい世界が広がっていた。老若男女たくさんの人々が楽しそうに笑って話していた。

「平方のmodをとるとさーww」

「あれ左辺は2の倍数じゃん?」

人種もさまざまだった。白人,黄色人,黒人が何の抵抗も無く語り合っていた。数学には国籍も人種も関係ない…平和な世界の一端が垣間見えた気がした。

そのとき,おいしそうないい匂いが漂ってきて,私はもう何日も集合しか口にしていないことに気付いた。空いていた近くの椅子に座って,テーブルの上の料理に伸ばした手が止まった…ええと,なんというか得体の知れないものだった。なんだコレ…累乗と割り算を含む式…が,整数となるような…組を全て求めよ…?私はその料理に全く手が付けられなかった。その横に添えてある小さな実だけでも食べようとして口に入れたがしかし…n以下でnと互いに素な正整数の個数…が,nと互いに素な正整数aの右肩に指数として乗っかっていて…nを法として1と合同…?全く,歯が立たなかった。何か他に,食べられそうなものは無いか…。辺りを見回しても,どのテーブルに置いてある料理も見たことの無いものばかり。私がおろおろしていると,一人の女性がやってきて私がさっき食べようとした固い実を手にとり,

「ん,たまにこの味が懐かしくなるのよねー」

そう言っておいしそうにかみ砕いた。私の唖然とした視線に気付いた彼女に

「どうしたの?」

と尋ねられ,私はしどろもどろになりながらやっと答えた。

「あ,あの…今のそれ,どうやって…ったんですか…?」

「ん,今のって…ああ,ファイ関数のやつ?」

「ファイ…関数…」

n以下でnと互いに素な正整数の個数…何に使うんだかよく分からないようなその関数には,ファイという名が付いていた。

「あなたここに来たばかりなのね。いいわ,教えてあげる。オイラーの定理と言う重要な定理よ」

定理1. (オイラーの定理)

nとaを互いに素な正の整数とするとき, a^φ(n)≡1 (mod n) (ただしφ(n)はn以下でnと互いに素な正整数の個数)が成り立つ。