憂鬱な午後のひととき


12月31日_
朝6時に目が覚めたので暗くて寒い中9kmほどランニング. 神武天皇陵の付近で滑って転んだ. 幸い酷い怪我ではなかったけど指がかなり腫れた. キーボードを打てない. ジョギングシューズを見ると靴底がパックリと全部取れてる. ひゃー, そんなこともあるんだ. 割合綺麗だったのになあ.
12月30日_
今日から1/3までネットワークから離れます. 来年もよろしくお願いします.
>新幹線で名古屋へ行ってみそかつを食べてみた. おいしかったけどしょっちゅう食べたい感じではないな. 店員の対応は素早くてよい感じ.
さて, 予約していた近鉄特急で実家に帰ろうとしたら電車が全てストップ. 何やら四日市付近で車と衝突事故らしい. それは大変だ. ここ3回連続, 乗ろうとした特急が1時間以上の遅延だったり運休とついてない. 諦めてもう一度新幹線で京都経由での帰省に変更. お金が…. しかも混んでて立ち席. 友達に会う約束もキャンセル. 残念.
12月28日_
ペアリングの定義の続き書いた.
12月26日_
・Pairing 2010でSilvermanの講演が全然分からんという人がいはったので, ずっとやってない数学を思い出しつつ少し説明を始めた(ペアリングの定義). といいつつまだホモロジー代数の初歩の部分. どこまでいけるかな. つっこみ大歓迎(twitter).
12月25日_
↑の説明をひたすら書いてた. 意外と時間がかかる.
12月24日_
・久しぶりにキルフェボンのケーキを食べようと思ってたのに今日は丸ごとしか売ってないらしい. 4000円とか1万円とか高すぎるなあ. 諦める.
12月23日_
朝VAIOの電源を入れても起動しない. BIOS画面までもいかない. 半年前と同じ現象. いくつか見てないドラマが入ってるので困る. で前回と同じく分解して掃除したら復活した. 凄い. でも時々起動に失敗する. 来年のAVXマシンが出るまでもって欲しいなあ.
12月21日_
『魔法使いの弟子たち』(井上夢人)
致死率の高いウイルスに感染して生き残った数人が不思議な力を持つ. 道具立てはわりと平凡だけど引き込まれる. ただ警察の行動があまりにお莫迦で類型的. ドラゴンボール的なインフレで後半とラストはいまいち. 『ガラダの豚』(中島らも)や『新世界より』(貴志祐介)も連想したけど個別にはそちらがいいかなあ.
12月20日_
『プリンセス・トヨトミ』(万城目学)
大阪国 vs. 会計検査院. 大阪の実在の場所ががんがん出てくる壮大なほら話. 中盤どこへ行くのかと思ったけどうまいこと着地した. やわらかな大阪弁が心地よい.
12月19日_
・録画してたドラマの『相棒』を見た. 真面目に資格を取って正社員になろうとしてるのにうまくいかずに家族や恋人に見捨てられ, バイト先でも騙されていく人が出てくる. あまりにせつない. というか普通にありそうで恐ろしい.
・数日前から計算機暗号屋日記などのいくつかのサイトがIEで文字化けしてたのはJISの自動判別が無くなったためだったのか. F5でリロードしても解決しないなあ.
12月18日_
セキュリティ&プログラミングキャンプ2010実施報告会

新宿は3年半振りぐらい. ビルがでかい.
最近将来プログラム書いてて食ってけるのか心配になることも多いので, サイボウズの青野さんの講演「日本のITエンジニアは幸せになれるのか?」と首藤さんの対談に興味があって行ってみた. やっぱり得意な分野を突き進めるのは大事. ただその技術を使う側の人のことを考えるのも同じぐらい大事とのこと.
基礎技術ならその重要性をきちんを説明することかな. まあそう書いてしまうと当たり前みたいだけど.

午後の発表ではみんな型推論好きだなーと思いつつ聞いていた.

フリーディスカッションではこの事業をどう存続していくかという話が出た. 一人一人が周りに地道に知らせていくという意見もあったが, やはりお上(お金を出してくれるところ)に成果をきちんとアピールすること(個別にではなく母体として --- それはたとえばとりあえずは各自の成果やblogへの"公式"ポータルを作るでもいいだろう ---)が必要じゃなかろうか. というかそれはまさに青野さんが仰ってたことに当てはまるのではなかろうか. と, 部外者が勝手に思ってみた.

交流会では名刺を持ってる中学生がいた. 私が初めて持ったのは彼の2倍の年だ. うひゃー.

12月17日_
今月号の『WEB+DB PRESS vol.60 2011』の特集「プログラマが知るべき言語設計の基礎知識」はなかなか面白い. 西尾さんの力作. プログラミング言語は人が作った言語で完璧でも無ければルールも普遍的ではないという考察がよい. あと, 注釈に書かれた様々な引用文献へのリンクは大変だったろうと思う.
12月16日_
実装系の発表の中からいくつかメモ:
An Analysis of Affine Coordinates for Pairing Computation
(Kristin Lauter, Peter L. Montgomery, and Michael Naehrig)
128bit securityだとProjectiveがいいけど, それ以上だとaffineがいい可能性がある. 複数のペアリングを同時に計算する場合も, affineがよくなるそうな.
Designing a Code Generator for Pairing Based Cryptographic Functions
(Luis J. Dominguez Perez and Michael Scott)
パラメータを与えてpairing計算コードを自動生成. 自分も昔からやってみたいと考えているところ. とくに多倍長に外部ライブラリを使うのではなく, ビット長に応じた乗算ルーチン生成とかはあまりされなさそうだし.
Compact hardware for computing the Tate pairing over 128-bit-security supersingular curves
(Nicolas Estibals)
128bit securityのBN曲線だと標数が大きいのでハードウェアでは辛い. かといってηTだと鍵効率が悪い. だからその中間でよいものを探そうという話. F35*97でうまいこといったそうな. 面白い. ソフトもクライアント側はそんなにスペック高くないのでそういう方向の高速化は必要やよね.
A Variant of Miller's Formula and Algorithm
(John Boxall, Nadia El Mrabet, Fabien Laguillaumie, and Duc-Phong Le)
Millerループを計算するためのそもそも関係式をいじって高速化する話. 上位レイヤなので適用範囲が広いのがいい.微調整して改良する話がいろいろ出そうだ.
High Speed Flexible Pairing Cryptoprocessor on FPGA Platform
(Santosh Ghosh, Debdeep Mukhopadhyay, and Dipanwita Roy Chowdhury)
256bit整数乗算が必要な128bit securityのBN曲線上でがんばってFPGAで実装したという話. Virtex-4 50MHz(52kSlices)で16.4msecだそうな. この値がどれぐらいのものなのか,ハードに詳しくないのでよく分からない(同様の計算が130nm CMOS 338MHz 97kGatesで15.8msecともある). ソフトだと0.8msec切ってるのでざっくり20倍. 標数2や3のηTだと逆にハードがソフトの20倍とか(もっと)速いのでいかに多倍長が辛いということか. というかIntelとかAMDのCPUはすごいなあ.
12月15日_
Pairing2010終了. 向こうはずっと雨でどこにも散歩に行けなかった. 帰りの加賀温泉~米原間の特急に乗っていたら途中で線路が火災とかで1時間半ほど停車. お尻が痛い. 米原の乗り換えも悲惨. 結局ドアツードアで6時間半. 疲れたよ.
12月10日_
下の続き.

○ src/zm1.cppのmakeMul4x1()

256bit整数pyと64bit整数xを掛けて320bit整数にする. 筆算方式でやる. ただ掛ける度に加算してるとトータルの加算回数が6回になってしまう. py[i] * xを全て求めてから足すと4回ですむ. その代わり今度は途中のレジスタがたくさん必要になるのでうまくレジスタを入れ換えてやりくりする. 後者の場合はmul + adcのレイテンシも多少隠蔽できるかもしれない.

const Reg64& a = rax;
const Reg64& d = rdx;
mov(a, ptr [py]);
mul(x);                    // [d:a]← a * x
mov(t0, a);
mov(t1, d);
mov(a, ptr [py + 8]);
mul(x);
mov(t, a);
mov(t2, d);
mov(a, ptr [py + 8 * 2]);
mul(x);
xchg(a, x);                // swap(a, x)
mov(t3, d);
mul(qword [py + 8 * 3]);
add(t1, t);
adc(t2, x);
adc(t3, a);
adc(d, 0);

○ src/zm1.cppのvoid makeMontgomery1()

Montgomery乗算の1ライン分を行う. 擬似コードは次の通り: ppはpの逆数で初期化時に一度だけ計算しておく.

c = [c4:c3:c2:c1:c0]
c += x[3..0] * y
q = uint64_t(c0 * pp)
c = (c + q * p) >> 64

二段目以降のみ前回の分のを足す必要があるので切り替える.

if (isFirst) {
    makeMul4x1(c3, c2, c1, c0, px, y, c4);
    mov(c4, d);
    // [c4:c3:c2:c1:c0] = px[3..0] * y
} else {
    makeMul4x1(t3, t2, t1, t0, px, y, t4);
    // [d:t3:t2:t1:t0] = px[3..0] * y
    add_rr(c3, c2, c1, c0, t3, t2, t1, t0);
    adc(c4, d);
}

その続き

mov(rax, pp_);
mul(c0); // q = a
mov(y, a);
makeMul4x1(t3, t2, t1, t0, p, y, t4);
add(c0, t0); // A
mov(c0, 0);  // ***
adc(c1, t1); // B
...

***は何故xor(c0, c0)ではなくmov(c0, 0)なのか. x64では前者の命令長は3byte, 後者は7byteと大きいのに. 答えはxorはcarryをクリアするけどmovはしないから. Aで作ったcarryをBで使いたいのでこのタイミングでクリアするためにxorは使えない. c0の0クリアをadd()より前でできるならxorでよいが無かったのでこうした.

ちなみにnasmでmov rbx, 0とするとなんと10byteにエンコードされる. mov rbx, dword 0とすると7byteに減る. Xbyakではmov(rbx, 0);で後者のエンコードにするようにした.

○ src/zm1.cppのvoid makeNegEntry()

256bit整数xに対して-xを得る. xが0のときは0, それ以外はp - xを求める. この場合, x == 0である確率は極めて小さいと考えられるので分岐予測がヒットするはず. だから条件分岐を使うのがよい.

    inLocalLabel();
    load_rm(z3, z2, z1, z0, x);
    mov(rax, z0);
    or(rax, z1);
    or(rax, z2);
    or(rax, z3);
    jz(".skip");
    mov(rax, (uint64_t)&s_pTbl[1]);
    load_rm(z3, z2, z1, z0, rax);
    sub_rm(z3, z2, z1, z0, x);
L(".skip");
    store_mr(z, z3, z2, z1, z0);
    ret();
    outLocalLabel();

inLocalLabe(), outLocalLabel()はXbyakの独自命令関数. この間にあるピリオドで始まるラベルはこの中でのみ有効となる. makeNegEntry()を複数回呼び出した場合, 複数の場所にある".skip"が混在しないようにするための仕様.

256bit整数xが0かどうかはx[3], x[2], x[1], x[0]の全てorをとってから判断することで分岐を減らす. 常套手段やね. 64bitのアドレッシングでは64bit即値は扱えないので一度raxにポインタを代入して使っている.

○ src/zm1.cppのvoid mul4x4()

256bit整数px[]と256bit整数py[]の積, 512bit整数pz[]を求める. 流石に16個のレジスタでもやりくりは苦しい. それでも同じメモリを複数回readすることはあってもtemporaryメモリは使わないようにした. そのおかげもあってかなり速い. mul命令は16回.

まず128bit([a:b])×128bit([c:d])→256bitではKaratsuba法によるmul3回よりも筆算方式mul4回が速い. なぜならad + bc = (a + b)(c + d) - ac - bdとしてmulを減らす場合, a + b, c + dが途中に必要. これらの値は65(= 64 + 1)bitなので(a + b)(c + d)は65bit×65bit→130bitの計算. その計算に"mul"は1回でよいが, carryをともなる計算が何回か必要になりそのコストはmul1回分よりも大きい.

次の可能性として128bit×128bit→256bitを筆算方式で行い, それを用いてKaratsuba法で256bit×256bit→512bitを求める方法がある. が, やっぱりそれでもコストは大きかった. 結局現時点でのCPUでは16回のmulを使う筆算方式が最速だった.

○ src/zm1.cppのvoid mul5(), dblMul5()

256bit×5, 512bit×5を求める. 前者の場合, (x << 2) + xとして求めたが後者ではmul命令を使った. シフト + 加算で5倍を実装するとtemporary変数が必要になり512bitではそれらをレジスタにおく場所が無い. また速度的にもあまり変わらないようであった. むしろmulの方がいいかもしれない. いずれにせよこれらの関数は全体の中でたいした時間を占めてないのであまり気にしない.

○ src/zm1.cppのvoid modp()

小さい値x(x < 14p)に関するx mod pを求める. 割とよく出てくるのでなんとか速くできないかと考えていたら, 今回選択したp特有の性質だけれども, xが何倍のpであるかをx[4] >> 61で概算できることに気がついた. でその概算値を予めテーブルtblに入れておき引き算する. 概算値の誤差は最大1なので大きめに引いておいて, もし負だったら引き過ぎということでpを足す. その部分のコード:

// x -= tbl[x>>61];
// if (x < 0) x += p;

sub_rm(x3, x2, x1, x0, a + x4);  // [x3:x2:x1:x0] -= tbl[x>>61]
sbb(x4, x4); // x4 = x < 0 ? -1 : 0

load_rm(t3, t2, t1, t0, a + 32); // [t3:t2:t1:t0] ← p
and(t0, x4);                     // x > 0ならt0 ← 0
and(t1, x4);                     // x > 0ならt0 ← 0
and(t2, x4);                     // x > 0ならt0 ← 0
and(t3, x4);                     // つまり[t3:t2:t1:t0] = x < 0 ? p : 0
add_rr(x3, x2, x1, x0, t3, t2, t1, t0); // x[] += t[]

ややトリッキー. sub_rmの時点でcarryが立っていたら, 引きすぎたということでsbb(x4, x4)により0xffffffff, そうでなければ0というマスクを生成する. load_rm()で値を読んで, andをすればcarryが立っているなら値が入って, なければ0になる.

もちろんt0, ..., t3を事前に0初期化しておきcmovcを使ってcarryがあるときのみメモリから値を読んでもいい. マイクロベンチマークだとandの方が気持ち速かった.

○ FizedZpCode()::init()

コード生成初期化部分. 関数ごとにクラスを作るとそれぞれごとにページサイズ分(4096byte)確保されて無駄が多い. だから予め12KBぐらいと多めに取っておいて, 現在のコード位置を取得して関数を生成し, align(16)を呼び出して次の関数を生成するというサイクルを繰り返す. そうすれば関数を詰めて生成できる.

mie::ZmZ_1::add = (void (*)(ZmZ_1& out, const ZmZ_1& x, const ZmZ_1& y))getCurr();
makeAddEntry();
align(16);
mie::ZmZ_1::sub = (void (*)(ZmZ_1& out, const ZmZ_1& x, const ZmZ_1& y))getCurr();
makeSubEntry();
...
12月9日_
来週Pairing 2010に参加&発表するので基本的な実装内容を思い出しておく. ateペアリングの実装でソースは出してるけどwhyは書いてないしね. 日本で暗号実装でソース公開してるところってあんまし無い気がするし, 実装屋さんに多少なりとも参考になればと. ついでにXbyakの細かいtips.

○ src/zm1.cppのmakeAddEntry()

void makeAddEntry()
{
#ifdef XBYAK64_WIN
    mov(ptr [rsp + 8 * 1], rbx);
    mov(ptr [rsp + 8 * 2], rsi);
    mov(ptr [rsp + 8 * 3], rdi);

    const Reg64& z = rcx;
    const Reg64& x = rdx;
    const Reg64& y = r8;
    const Reg64& z2 = r9;
    const Reg64& z1 = r10;
    const Reg64& z0 = r11;

    const Reg64& t0 = rbx;
    const Reg64& t1 = rsi;
    const Reg64& t2 = rdi;
#else
    sub(rsp, 8 * 1);
    mov(ptr [rsp], r12);

    const Reg64& z = rdi;
    const Reg64& x = rsi;
    const Reg64& y = rdx;
    const Reg64& z2 = rcx;
    const Reg64& z1 = r8;
    const Reg64& z0 = r9;

    const Reg64& t0 = r10;
    const Reg64& t1 = r11;
    const Reg64& t2 = r12;
#endif
   ...

これに限らず関数生成コード全般に, win64とlinux64を吸収するための#ifdef XBYAK64_WIN ... #else ... #endifがある. どうみてもかっこ悪い. jitasmみたいに抽象化しようと何度も仕様を検討した. ただjitasmはebpを使ったスタックフレームを作ってるけど私はそういうことはしたくないし, レジスタの使い回しのところでmul命令を使うときと使わないときでrdxを退避するのかどうかraxはつぶしていいのかとか, の指定は必須になり使う側もややこしい(覚えることが増える). 結局諦めてベタベタ書くことにした.

上のコードについてコメント. Win64でrspを動かさずにいきなりrbx, rsi, rdiを保存してるが, これはWindowsでのスタックに書いたように呼び出し元で確保された領域なので大丈夫. Linuxもそういう領域(red zone)はあるみたいだけど, -mno-red-zoneなんてオプションもあるし(挙動未調査)安全側に倒した.

今回はSSEを使わない実装なのでスタック退避の代わりにxmm0とかに保存してるときもある. まあ気持ちの問題.
#そのあたり, いろいろ試したかったというのもラップしなかった理由の一つ.

const Reg64&参照でレジスタの別名をつける. これによりこれ以降z, x, yとみやすいコードで記述できる.

void load_rm(const Reg64& z3, const Reg64& z2, const Reg64& z1, const Reg64& z0,
    const Reg32e& m)
{
    mov(z0, ptr [m + 8 * 0]);
    mov(z1, ptr [m + 8 * 1]);
    mov(z2, ptr [m + 8 * 2]);
    mov(z3, ptr [m + 8 * 3]);
}

mで指定されたアドレスから256bitデータを[z3:z2:z1:z0]のレジスタに読み込む. const Reg32e&はアドレス生成を保持する特別なクラス. このクラスで受けると呼び出すときにレジスタ指定だけでなくて

load_rm(x, z2, z1, z0, x);
load_rm(t3, t2, t1, t0, px + 8 * 4);
load_rm(z3, z2, z1, z0, rbp + rax * 4 + 16);

みたいに書けて便利. add_rm(), sub_rr()なども同様に用意しておく.

さて, makeAddEntry()で作る関数の話. 256bit整数xと256bit整数yを足して256bitの固定素数pで割る. 今回p<N/2, N=2256なのでx + yをした時点で256bitを超えることは無い. これにより最後のcarryを保存しておく必要がないので少し簡単になる.

 z = x + y;
 if (z >= p) z -= p;

if()をどう処理するかで悩む. zがpより大きいかはアセンブリレベルではz[3]のほうから順次比較するか, 引ききってみて最後にcarryがたったかのどちらかで判断することになる. 前者の場合, 実際にzがpより大きいと判断した時点で引き算をすることになる.

試した感じでは, cmovが速いAMD系は後者, jmpが遅いCore2Duoも後者, i7は微妙に前者なときもある?って感じ. 将来Intel系ももっとcmovが速くなってくだろうことを期待して後者のロジックで実装した(まあjmpは汚いというイメージもある).
もちろんJITなのでcpu判別して切り替えてもいい. 今回は有為な差で無かったのでそうしなかった. 擬似コードはこう. 演算は全て256bit整数:

x += y       // x + yを計算してxに代入
tmp ← x     // xの値をtmpに保存
x -= p       // x - pを計算してxに代入
if (carry) { // carryがたつ = x < pだったのでxをpを引く前の値に戻す
    x ← tmp
}

最後のifのところでcmovを使う.

sub_rm(x, z2, z1, z0, rax); // [x:z2:z1:z0] -= ptr rax[3:2:1:0]
cmovc(z0, t0);              // carryがあれば[x:z2:z1:z0] ← [y:t2:t1:t0]
cmovc(z1, t1);
cmovc(z2, t2);
cmovc(x, y);
12月8日_
Xbyakのripを使った64bit相対コードがバグってたので修正. テストコード入れてたつもりが正しく動いてなかった. 2年ほど前に対応して以来一度も動いてないことに. 酷い話だ(自分はripを使ったことがないからなあ).
それとは違うけど調べててnasmとyasmの$の扱いが違うのに気が付いた.
; nasm
call $ + 5
E800000000

; Xbyak
call (getCode() + 5 );
E800000000

; yasm
call $ + 5
E8FBFFFFFF
$やgetCode()は現在値のアドレスを取得する命令だけど, それをcallの命令を構築する前に確定するのか, 後に確定するのかという話. nasmとyasmは殆ど互換性があるだけに$を含んでるとはまりそうだなあ.
12月7日_
『クローズアップ現代 解消できるか“デバイスラグ”
メモ取ってないのでうろ覚えだけど2時間の相談料が200万以上とか, 治験に億ぐらいのお金かかるために優秀な中小企業の参加が難しいらしい. アメリカに比べて国の審査官の数も一桁近く違う(59 vs 400ぐらい?)し審査に時間がかかる. こういうのとか, 技術立国でいきたいのに科学予算が足りないとか聞くたびに思うのは一体日本は何に予算をかけてるのだろうというということ.
12月6日_
先週録画してたドラマの『相棒』を見た. まとり(麻薬取締官)と警察の関係がよく分からなくて(別組織なのか!), 何度も途中で止めてセリフを聞き直したり, webで調べたり. ただひたすら真実を追求しようとする右京と反対する周りのもの. とても重たい内容で見応えがあった.
12月5日_
昨日の会でC++な人が何人かいたので, その人たち向けにちょっとしたクイズとして入れた(けどまともに話せなかったm(__)m)部分を少し抜き出してみる. 256bit整数を
struct uint256_t {
    uint64_t data_[4];
    static void (*add)(uint256_t&, const uint256_t&, const uint256_t&);
};
としてつくった場合に(昨日はaddをXbyakで生成したけど本質じゃないので略), やっぱりa += bとかしたいので次のようなテクニックを使う.
template<class T>
struct Addable {
  T& operator+=(const T& rhs) {
    T::add(static_cast<T&>(*this), static_cast<T&>(*this), rhs);
    return static_cast<T&>(*this);
  }
  friend T operator+(const T& a, const T& b) {
    T c; T::add(c, a, b); return c;
  }
};
こういうtemplateを用意しておけば, CRTPを使って
struct uint256_t : public Addable<uint256_t> {...};
とすることでadd()を持つクラスから余計なコストを払わずに自動的にa += bを持つクラスを生成できる(これもメタプログラミングと言えるかな).
で, 問題はadd()と同様にsub()とSubableを用意して
struct uint256_t : public Addable<uint256_t>, Subable<uint256_t> {...};
とすると何故か遅くなったり, 場合によっては動かなくなったりすることがある. それは何故か? というのがクイズでした.
答えというかヒントはsizeof(uint256_t)を見てみよなのですが, 一つのときは大丈夫なのに二つつけると32byteより大きくなってる.
// 32bitVCでの結果
struct uint256_t {};の場合sizeoは32
struct uint256_t : Addable<A> {}; の場合sizeofは32
struct uint256_t : Addable<A>, Subable<A> {}; の場合sizeofは40
これはC++の規格上増えても増えなくてもよいので仕方がないようだ. でも余計に増えた8byteのせいでメモリレイアウトがくるって&uint256_t == uint256_t.data_でなくなってしまうかもしれない. 単一継承なら普通問題ないのでこれは次のようなEmptyクラスを用意して回避するのが常套手段.
template<class T> struct Empty {};
template<class T, class E = Empty<T>>struct Addable : E {上と同じ};
template<class T, class E = Empty<T>>struct Subable : E {...};

struct uint256_t : public Addable<uint256_t,Subable<uint256_t>> {...};
こうするとsizeofは32のまま. まあうっかり二つで継承してしまっても動くことも多いので気づかないかもしれないのだけど, asmな関数と連係するときはメモリレイアウトが狂うと終わりなので大事なんだということが言いたかった. 詳しくはboost::operatorsの注意書き参照.
12月4日_
今日のメタプログラミングの会のプレゼン資料に掛けた時間は, 今までのプレゼンで一番時間を掛けたやつの多分数倍はあったのに, 一番ぐだぐだだった. 聴衆の方, 申し訳なかったです.
12月3日_
憂鬱なプログラマのためのオブジェクト指向開発講座』は社会人1年目の研修のテキストだったな. 衝撃的だったのは本の内容ではなく(というか恥ずかしながらあんまし記憶がない), 同期の人の質問「インベーダーゲームって何ですか」だった.
12月2日_
朝日新聞, 「子ども国会」の感想,
>「国会のようなヤジがなくてよかった. 大人げないもん」
子どもに言われちゃ世話無い.

御意見は光成滋生<herumi@nifty.com>までお願いします