この記事はエムスリー Advent Calendar 2025 11日目の記事です。
AI・機械学習チームの中村伊吹(@inakam00)です。
早速ですが、こちらをご覧ください。
package main;import(b"encoding/base64";f"fmt";s"strings");func main() {r:=s.ReplaceAll(s.ReplaceAll(
p," ",""),"\n","");u:=s.SplitN(r,"::M3::",2);d:=b.StdEncoding.DecodeString;t,_:=d(u[0]);m,_:=d(u[1])
;q:=f.Sprintf(string(t),r);i:=0;for n:=0;n<2600;n++{if n>0&&n%100==0{f.Println()}; x:=m[n/8];b:=(x>>
(7-uint(n%8)))&1;if b==1{f.Print(" ")}else{f.Printf("%c",q[i]);i++}};f.Println();};const p=`cGFja2Fn
ZSBtYWluO2 ltcG9ydChiImVuY29kaW5n L2Jhc2U2NCI7ZiJmbX QiO3Mic3RyaW5
ncyIpO2Z1bmM gbWFpbigpIHtyOj1zLlJl cGxhY2VBbGwocy5SZXB sYWNlQWxsKH
AsIiAiLCIiKSwiX G4iLCIiKTt1Oj1zLlNw bGl0TihyLCI6Ok0zOjo iLDIpO 2Q6PWIuU3R
kRW5jb2RpbmcuRG Vjb2RlU3RyaW5nO3 QsXzo9ZCh1WzBdKTttL F86PWQodV sxXSk7cTo9
Zi5TcHJpbnRmKHN 0cmluZyh0KSxyKT tpOj0wO2ZvciBuOj0wO248MjYwMDtuK yt7aWYgbj4
wJiZuJSUxMDA9PT B7Zi5QcmludGx uKCl9OyB4Oj1tW24vOF07Yjo9KHg+Pi g3LXVpbnQob
iUlOCkpKSYxO2lm IGI9PTF7Zi5Q cmludCgiICIpfWVsc2V7Zi5QcmludG YoIiUlYyIscV
tpXSk7aSsrfX07Z i 5QcmludGxu KC k7fTtjb25zdCBwPWAlc2AgICAg ICAgICAgICAgIC
AgICAgICAgICAgI CA gICAgICAg ICA gICAgICAgICAgICAgICAgIC AgICAgICAgIC
AgICAgICAgICAg ICAg ICAgICA gIC AgICAgICAgICAgICAgIC AgICAgICAg
ICAgICAgICAgIC AgICA gICA gICA gICAgICAgICAgICAgICAgICAgICAg ICAgICAgI
CAgICAgICAgICA gICAgI CA gICAg ICAgICAgICAgICAgICAgICAgICAgICA gICAgICA
gICAgICAgICAgI CAgICAg I CAgICA gICAgICAgICAgICAgICA=::M3::AAAAA AAAAAAAA
AAAAAAAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAP/4AA Af/gAAf/
gAAAP/gAAD/wA AH//gAAAH /AAAf8AAB 8D/AAAAf/AAD/wAAHAH8AAAB/8AAf/ AAAAAfwA
AAH/4AD/8AAAA B+AAAAf/wA P/wAAAAPwA AAB7/gB5/AAAAP8A AAAHn+APH+A AAD/8AAAA
8P8B8f4AAB//8 AAADwf8Ph/g AAAA/4AAAPA /58H+AAAAA/wAAA 8B/vgf4AAA AB/AAAHgH/
8A/wAAAA H8AAAeA P/gD/AA AAA/wAAB4A f8AP8AAOAD+A
AAHgA/gA /wAB4Afw AAP/8B+A //+AH//8AAA/ /wDwD//4AH/+AAA
AAAAAAAAAAAAAAAAAAAAAABIgSAAAAAAAAAAAAIAAAAIAAAAAA==M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3
========================================= We are hiring !! =========================================
================================== https://jobs.m3.com/engineer/ ==================================`
これは実際に動作するGo言語のコードで、これを手元のローカル環境やGo Playgroundで実行すると、全く同じコードが出力されます。
そう、これはQuine(クワイン)と呼ばれる「自分自身のソースコードを出力するプログラム」です。しかも、ソースコード自体がm3のロゴの形をしています。
Quineとは
Quineとは、外部入力なしに自分自身のソースコードを出力するプログラムのことです。ファイルを読み込んで出力するのはズルなので、プログラム内部に自身の完全な記述を持っている必要があります。*1
エムスリーでは過去に様々な言語でQuineを作成してきました。カンファレンスではクリアファイルやTシャツにそれぞれが印刷されているので、目にされたことがある方もいるでしょう。
- エムスリーが難読プログラミングオタクに送るノベルティ、Python Quineクリアファイルの作り方 - エムスリーテックブログ
- エムスリーがKotlin Loverに贈るノベルティ、Kotlin Quineクリアファイルを作りました - エムスリーテックブログ
- DartでQuineをダーッと書きました - エムスリーテックブログ
- 引数でアスキーアートが変化するSwift Quineの仕組みをフローチャートで解説 - エムスリーテックブログ
- OCamlでQuineを作った - エムスリーテックブログ
そして今回、Go言語で挑戦しました。
Go言語でQuineを作るときの罠
AI・機械学習チームでは高速にプロダクトをリリースしつつ柔軟かつ適材適所に技術選定できる文化があり、その中でもPythonとGoは採用される率が高めです。(僕がAIチームで知っている技術スタックまとめ - エムスリーテックブログ)
Python、Swift、Kotlin、Dartと色々作られていて、いつも使っているGoでもできるでしょう...と軽い気持ちで始めたのですが、せっかくなので大変だった部分について解説していきます。
evalがない
PythonやJavaScriptにはeval関数があり、コードを文字列として定義しておき実行中に再解釈させることができます。
OCamlでQuineを作った - エムスリーテックブログではOCamlでevalに相当するものを実装しているように、アスキーアートQuineにおけるevalは非常に助かる関数です。evalがあると空白を削除してコードを実行するというテクニックが利用できるため、空白の挿入箇所が比較的自由になり、Quineとしての難度が下がります。しかし、Goでは利用できません。
(数式を評価するevalは存在しますが、ここで求めているような動的にコードを実行する関数は存在しません)
m3の形をしたQuineでは上4行と下3行に空白が存在しないため、Goのようなevalがない言語では基本的にはこの行に実行コードを押し込む形をとります。*2 コードを記述できる部分が少なくなることから、evalがないアスキーアートQuineは難易度が跳ね上がります。
Python版と同様にプログラム自身をBase64エンコードして埋め込む戦略を今回はとるため、空白のある中間行はほとんどBase64文字列の定義箇所となります。
区切りをうまく調整する必要がある
evalがないことに関係しますが、ReplaceAll(...)を利用する場合にはReplaceAllという文字列の途中で改行を入れられません。また関数の場合には(の後で改行するなど、改行位置を適切にコントロールしなければなりません。
Goではif-else文の場合に}とelseが同じ行に存在する必要があるため、この位置調整も大変でした。
if b == 1 { f.Print(" ") } // <--- コンパイラによってここに自動でセミコロンが入り、このコードはコンパイルエラーになる else { f.Printf("%c", q[i]) }
Quineを作成する
ここからは実際の制作過程です。
Base64エンコードして埋め込む戦略を実行するプログラムを作成する
先に出ている記事の多くの戦略と同様に、プログラム自身とマスク情報をBase64エンコードして埋め込む戦略を取ります。*3
完成したQuineの基本形をフォーマットしてコメントを追加すると次のようになっています。後半に定義されるconst pはプログラム自身+マスク情報+冗長な文字列のBase64文字列のため、処理部分は前半だけで完結しています。
package main import ( b "encoding/base64" f "fmt" s "strings" ) func main() { r := s.ReplaceAll(s.ReplaceAll( p, " ", ""), "\n", "") // 文字列の空白を削除 u := s.SplitN(r, "::M3::", 2) // Base64文字列をプログラムとマスク情報に分割 // それぞれのBase64文字列をデコード d := b.StdEncoding.DecodeString t, _ := d(u[0]) m, _ := d(u[1]) q := f.Sprintf(string(t), r) i := 0 for n := 0; n < 2600; n++ { if n > 0 && n%100 == 0 { f.Println() // 100文字ごとに改行する } // マスク情報を取り出す // 8bitずつ入っているフラグを取り出す x := m[n/8] b := (x >> (7 - uint(n%8))) & 1 if b == 1 { f.Print(" ") // マスクが1だったら空白 } else { f.Printf("%c", q[i]) // マスクが0の時は文字を出力 i++ } } f.Println() } const p=`%s` // プログラム自身+マスク情報のBase64文字列がここに入る
Base64エンコードするためのジェネレータを作る
100文字ごとに改行してもGoのコードとして成り立つことを確認し、基本的なQuineコードが完成したら、コードやマスク情報をBase64にエンコードします。
次のようなm3ロゴをマスク情報として準備します。1となっている部分が空白になる想定です。
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000111111111111100000000000000000000001111111111110000000000000000001111111111110000000000000 0000000000001111111111100000000000000000000011111111110000000000000000000111111111111111100000000000 0000000000000001111111110000000000000000000111111111000000000000000000011111000000111111110000000000 0000000000000001111111111100000000000000001111111111000000000000000000011100000000011111110000000000 0000000000000001111111111100000000000000011111111111000000000000000000000000000000011111110000000000 0000000000000001111111111110000000000000111111111111000000000000000000000000000000011111100000000000 0000000000000001111111111111000000000000111111111111000000000000000000000000000000111111000000000000 0000000000000001111011111111100000000001111001111111000000000000000000000000001111111100000000000000 0000000000000001111001111111100000000011110001111111100000000000000000000000111111111111000000000000 0000000000000011110000111111110000000111110001111111100000000000000000000111111111111111110000000000 0000000000000011110000011111111100001111100001111111100000000000000000000000000000111111111000000000 0000000000000011110000001111111110011111000001111111100000000000000000000000000000001111111100000000 0000000000000011110000000111111110111110000001111111100000000000000000000000000000000111111100000000 0000000000000111100000000111111111111100000000111111110000000000000000000000000000000111111100000000 0000000000000111100000000011111111111000000000111111110000000000000000000000000000001111111100000000 0000000000000111100000000001111111110000000000111111110000000000000000111000000000001111111000000000 0000000000000111100000000000111111100000000000111111110000000000000001111000000000011111110000000000 0000000011111111111111000000011111100000001111111111111111100000000001111111111111111111000000000000 0000000011111111111111000000001111000000001111111111111111100000000000011111111111111000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
ここのポイントはGoのコード部分にもfunc main()やReplaceAll(p," ","")など、動作させる上で省略できない空白があり、必要な空白部分も1にしなくてはいけません。
幸いなことに処理を実行するコード部分はBase64文字列の変数定義より前のため、エンコードされたBase64文字列の長さなどで処理部分の空白位置が変わることはありません。したがってマスクが1になる箇所は事前に計算可能で、これをマスクに反映させます。
Pythonで記述したジェネレータは次のようになります。*4*5
#!/usr/bin/env python3 from base64 import b64encode from pathlib import Path MASK_FILE = Path("m3_mask.txt") OUT_FILE = Path("main.go") DELIM = "::M3::" WIDTH = 100 def load_mask(): lines = MASK_FILE.read_text().splitlines() bits = [] for line in lines: bits.extend(0 if c == "0" else 1 for c in line.strip()) return bits def pack_bits(bits): packed = [] for i in range(0, len(bits), 8): chunk = bits[i : i + 8] while len(chunk) < 8: chunk.append(0) byte = 0 for b in chunk: byte = (byte << 1) | b packed.append(byte) return b64encode(bytes(packed)).decode() def apply_required_spaces(bits, full, rows=4): """上部4行のGo構文で必須な空白をマスクに反映""" limit = min(len(bits), WIDTH * rows) bits = bits[:] for pos in range(limit): bits[pos] = 0 return bits def build_template(mask_b64: str, pad: int): base = ( 'package main;import(b"encoding/base64";f"fmt";s"strings");' "func main() {r:=s.ReplaceAll(s.ReplaceAll(p,\" \",\"\"),\"\\n\",\"\");" 'u:=s.SplitN(r,"::M3::",2);d:=b.StdEncoding.DecodeString;' "t,_:=d(u[0]);m,_:=d(u[1]);q:=f.Sprintf(string(t),r);i:=0;" "for n:=0;n<2600;n++{if n>0&&n%%100==0{f.Println()}; x:=m[n/8];" "b:=(x>>(7-uint(n%%8)))&1;if b==1{f.Print(\" \")}" "else{f.Printf(\"%%c\",q[i]);i++}};f.Println();};" "const p=`%s`" ) tmpl = base + (" " * pad) code_b64 = b64encode(tmpl.encode()).decode() payload = code_b64 + DELIM + mask_b64 full = tmpl.replace("%s", payload, 1).replace("%%", "%") return tmpl, full def find_padding(bits, mask_b64, max_pad=20000): zeros = len(bits) - sum(bits) for pad in range(max_pad): _, full = build_template(mask_b64, pad) if len(full) == zeros: return pad, full raise RuntimeError(f"pad not found up to {max_pad}, zeros={zeros}") def shape_source(bits, full): out = [] idx = 0 for pos, bit in enumerate(bits): if bit == 1: out.append(" ") else: out.append(full[idx]) idx += 1 if (pos + 1) % WIDTH == 0: out.append("\n") if idx != len(full): raise RuntimeError("mask/code length mismatch") return "".join(out) def main(): bits = load_mask() # Pass1: Goのコード実行部分における空白をマスクに適用する mask_b64 = pack_bits(bits) _, full0 = build_template(mask_b64, 0) bits = apply_required_spaces(bits, full0, rows=4) # Pass2: 更新されたマスクで文字数を合わせるために挿入する空白数を計算 mask_b64 = pack_bits(bits) pad, full = find_padding(bits, mask_b64) # Pass3: 再度微調整する(不要な場合が多い) bits = apply_required_spaces(bits, full, rows=4) mask_b64 = pack_bits(bits) pad, full = find_padding(bits, mask_b64) shaped = shape_source(bits, full) OUT_FILE.write_text(shaped) print(f"pad={pad} chars, code_len={len(full)}, zeros={len(bits)-sum(bits)}") print(f"wrote {OUT_FILE} ({len(shaped)} chars including newlines)") if __name__ == "__main__": main()
base64エンコードされた文字列に任意の文字列を追加する
このジェネレータを実行すると次のような出力になります。形はほぼ見えており、これでもクワインになっていますが、このままだとマスクに対して270文字ほど不足しています。
package main;import(b"encoding/base64";f"fmt";s"strings");func main() {r:=s.ReplaceAll(s.ReplaceAll(
p," ",""),"\n","");u:=s.SplitN(r,"::M3::",2);d:=b.StdEncoding.DecodeString;t,_:=d(u[0]);m,_:=d(u[1])
;q:=f.Sprintf(string(t),r);i:=0;for n:=0;n<2600;n++{if n>0&&n%100==0{f.Println()}; x:=m[n/8];b:=(x>>
(7-uint(n%8)))&1;if b==1{f.Print(" ")}else{f.Printf("%c",q[i]);i++}};f.Println();};const p=`cGFja2Fn
ZSBtYWluO2 ltcG9ydChiImVuY29kaW5n L2Jhc2U2NCI7ZiJmbX QiO3Mic3RyaW5
ncyIpO2Z1bmM gbWFpbigpIHtyOj1zLlJl cGxhY2VBbGwocy5SZXB sYWNlQWxsKH
AsIiAiLCIiKSwiX G4iLCIiKTt1Oj1zLlNw bGl0TihyLCI6Ok0zOjo iLDIpO 2Q6PWIuU3R
kRW5jb2RpbmcuRG Vjb2RlU3RyaW5nO3 QsXzo9ZCh1WzBdKTttL F86PWQodV sxXSk7cTo9
Zi5TcHJpbnRmKHN 0cmluZyh0KSxyKT tpOj0wO2ZvciBuOj0wO248MjYwMDtuK yt7aWYgbj4
wJiZuJSUxMDA9PT B7Zi5QcmludGx uKCl9OyB4Oj1tW24vOF07Yjo9KHg+Pi g3LXVpbnQob
iUlOCkpKSYxO2lm IGI9PTF7Zi5Q cmludCgiICIpfWVsc2V7Zi5QcmludG YoIiUlYyIscV
tpXSk7aSsrfX07Z i 5QcmludGxu KC k7fTtjb25zdCBwPWAlc2AgICAg ICAgICAgICAgIC
AgICAgICAgICAgI CA gICAgICAg ICA gICAgICAgICAgICAgICAgIC AgICAgICAgIC
AgICAgICAgICAg ICAg ICAgICA gIC AgICAgICAgICAgICAgIC AgICAgICAg
ICAgICAgICAgIC AgICA gICA gICA gICAgICAgICAgICAgICAgICAgICAg ICAgICAgI
CAgICAgICAgICA gICAgI CA gICAg ICAgICAgICAgICAgICAgICAgICAgICA gICAgICA
gICAgICAgICAgI CAgICAg I CAgICA gICAgICAgICAgICAgICA=::M3::AAAAA AAAAAAAA
AAAAAAAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAP/4AA Af/gAAf/
gAAAP/gAAD/wA AH//gAAAH /AAAf8AAB 8D/AAAAf/AAD/wAAHAH8AAAB/8AAf/ AAAAAfwA
AAH/4AD/8AAAA B+AAAAf/wA P/wAAAAPwA AAB7/gB5/AAAAP8A AAAHn+APH+A AAD/8AAAA
8P8B8f4AAB//8 AAADwf8Ph/g AAAA/4AAAPA /58H+AAAAA/wAAA 8B/vgf4AAA AB/AAAHgH/
8A/wAAAA H8AAAeA P/gD/AA AAA/wAAB4A f8AP8AAOAD+A
AAHgA/gA /wAB4Afw AAP/8B+A //+AH//8AAA/ /wDwD//4AH/+AAA
AAAAAAAAAAAAAAAAAAAAAABIgSAAAAAAAAAAAAIAAAAIAAAAAA==`
他の言語であれば末尾から何らかの処理をメソッドチェインで追加していき調整できますが、Go言語は思想上標準で用意されているメソッドチェインは多くありません。*6
そこで今回はBase64の末尾に文字列を追加する形で調整します。
==が出現した時点でBase64文字列は終了判定となりますが、実はその後に任意の文字列が追加されていてもデコードが可能です。
StdEncoding.DecodeStringの仕様として、任意の文字列が追加されているとデコード時にエラーが返ってきますが、同時にデコードできた部分(つまり==までの部分)の結果は関数から返ってきます。
デコード時にエラーを処理せずに握りつぶし、その結果だけを利用すれば、末尾に追加した任意の文字列は無視しつつマスク情報をデコードできます。
このテクニックを使いつつ、マスクを手動で調整し再生成した結果が冒頭のクワインとなります。*7
package main;import(b"encoding/base64";f"fmt";s"strings");func main() {r:=s.ReplaceAll(s.ReplaceAll(
p," ",""),"\n","");u:=s.SplitN(r,"::M3::",2);d:=b.StdEncoding.DecodeString;t,_:=d(u[0]);m,_:=d(u[1])
;q:=f.Sprintf(string(t),r);i:=0;for n:=0;n<2600;n++{if n>0&&n%100==0{f.Println()}; x:=m[n/8];b:=(x>>
(7-uint(n%8)))&1;if b==1{f.Print(" ")}else{f.Printf("%c",q[i]);i++}};f.Println();};const p=`cGFja2Fn
ZSBtYWluO2 ltcG9ydChiImVuY29kaW5n L2Jhc2U2NCI7ZiJmbX QiO3Mic3RyaW5
ncyIpO2Z1bmM gbWFpbigpIHtyOj1zLlJl cGxhY2VBbGwocy5SZXB sYWNlQWxsKH
AsIiAiLCIiKSwiX G4iLCIiKTt1Oj1zLlNw bGl0TihyLCI6Ok0zOjo iLDIpO 2Q6PWIuU3R
kRW5jb2RpbmcuRG Vjb2RlU3RyaW5nO3 QsXzo9ZCh1WzBdKTttL F86PWQodV sxXSk7cTo9
Zi5TcHJpbnRmKHN 0cmluZyh0KSxyKT tpOj0wO2ZvciBuOj0wO248MjYwMDtuK yt7aWYgbj4
wJiZuJSUxMDA9PT B7Zi5QcmludGx uKCl9OyB4Oj1tW24vOF07Yjo9KHg+Pi g3LXVpbnQob
iUlOCkpKSYxO2lm IGI9PTF7Zi5Q cmludCgiICIpfWVsc2V7Zi5QcmludG YoIiUlYyIscV
tpXSk7aSsrfX07Z i 5QcmludGxu KC k7fTtjb25zdCBwPWAlc2AgICAg ICAgICAgICAgIC
AgICAgICAgICAgI CA gICAgICAg ICA gICAgICAgICAgICAgICAgIC AgICAgICAgIC
AgICAgICAgICAg ICAg ICAgICA gIC AgICAgICAgICAgICAgIC AgICAgICAg
ICAgICAgICAgIC AgICA gICA gICA gICAgICAgICAgICAgICAgICAgICAg ICAgICAgI
CAgICAgICAgICA gICAgI CA gICAg ICAgICAgICAgICAgICAgICAgICAgICA gICAgICA
gICAgICAgICAgI CAgICAg I CAgICA gICAgICAgICAgICAgICA=::M3::AAAAA AAAAAAAA
AAAAAAAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAP/4AA Af/gAAf/
gAAAP/gAAD/wA AH//gAAAH /AAAf8AAB 8D/AAAAf/AAD/wAAHAH8AAAB/8AAf/ AAAAAfwA
AAH/4AD/8AAAA B+AAAAf/wA P/wAAAAPwA AAB7/gB5/AAAAP8A AAAHn+APH+A AAD/8AAAA
8P8B8f4AAB//8 AAADwf8Ph/g AAAA/4AAAPA /58H+AAAAA/wAAA 8B/vgf4AAA AB/AAAHgH/
8A/wAAAA H8AAAeA P/gD/AA AAA/wAAB4A f8AP8AAOAD+A
AAHgA/gA /wAB4Afw AAP/8B+A //+AH//8AAA/ /wDwD//4AH/+AAA
AAAAAAAAAAAAAAAAAAAAAABIgSAAAAAAAAAAAAIAAAAIAAAAAA==M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3M3
========================================= We are hiring !! =========================================
================================== https://jobs.m3.com/engineer/ ==================================`
終わりに
最初はどのように作るんだろうと思いながら、AIの力も借りつつ作成を進めていき、最終的にGo言語で実現できました。 次はどの言語でクワインが作られるのでしょうか。乞うご期待。
We are hiring!!
エムスリーAI・機械学習チームでは、言語仕様を面白く扱いながらプロダクトへ活用もしていくエンジニアを募集しています。 新卒・中途それぞれの採用だけでなく、カジュアル面談やインターンも常時募集しています。
エンジニア採用ページはこちら
カジュアル面談もお気軽にどうぞ
インターンも常時募集しています
*1:とはいえ、ファイルを読み込むタイプのQuineもあるみたいです
*2:Swift版やKotlin版でも同じような構成になっています
*3:詳しい解説はエムスリーが難読プログラミングオタクに送るノベルティ、Python Quineクリアファイルの作り方 - エムスリーテックブログをご覧ください
*4:ジェネレータはGoじゃないんかいというツッコミは無しです
*5:やや冗長なコードなのですが、処理を保ったまま簡潔化ができなかったため実際に生成に利用したコードをそのまま提供します
*6:エラーハンドリングの仕様とメソッドチェインの相性が良くないため、Functional Option Patternが好まれます。
*7:We are hiringなどの部分に空白を入れたかったため、その部分を手動で1にしたマスクを準備し再生成しました