たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

BashとかからLINQ使いたくてShelLINQってコマンド作った

BashとかからLINQ使いたくてShelLINQってコマンド作った

ウヒャァ使いやすい

LINQすき

コマンドラインでの作業をしているとき、シェルスクリプトを書いているときに突然LINQが使いたくなる人に向けて作ったコマンドです
docs.microsoft.com

似たような機能を持つ既存のものとしてはawkdatamashがあります
https://ja.wikipedia.org/wiki/AWKja.wikipedia.org
https://www.gnu.org/software/datamash/www.gnu.org

正直awkで十分にできますがぼくはLINQのが使いたかったのです

ShelLINQ

logo

github.com

インストールのしかた

git cloneしてやるだけです

git clone https://github.com/xztaityozx/shellinq
./shellinq/install.sh

aliasを貼ったり補完スクリプトbashrcに書き込むスクリプトを同梱してるので実行すれば使えるようになります

つかいかた

GitHubのREADME.mdと同じことを書くのでそっち見てもらってもいいです

shellinq [TYPEs] [method query] ... [-o [FORMAT]]

[TYPEs]は入力データの型です。int型の場合は[int]と指定します。指定しない場合はint,double,stringのどれかからShelLINQが予想します。
ls -lコマンドのように複数のカラムがある場合は[type1,type2,...,typeN]というように順番に指定します。これも指定しない場合はShelLINQがすべて予想します。

[method query]はクエリ部分です。methodにはSelectWhereのようにLINQが提供するメソッド名を指定します。Qにはメソッドに与える引数部分を指定します。
例えば

Where (x => x% 2 == 0)

where 'x => x% 2 == 0'

と書きます。
queryではawkのようにカラムの指定に$が使えます。
例えばwhere '$1%2==0'と書けばShelLINQはwhere '_=>_%2==0'と解釈します。
$0を使うとすべてのカラムが指定できます。

[-o [FORMAT]]は出力フォーマットをC#のコードで指定するときに使います。 具体的には処理されたデータを使ったforeachループのブロック部を指定します。
取り出されたアイテムは変数itemに格納されているのでそれを使用するといいでしょう。

Input

% seq 30|shellinq select '$1%2==0' -o 'Console.WriteLine(item?"even":"odd");'

output

odd
even
odd
even
.
.
.

デモ

3の倍数を抽出

seq 30 | shellinq where '$1%3==0'

demo1

3の倍数を抽出して30加算

seq 30 | shellinq where '$1%3==0' select '$1+30'

demo2

ls -lの出力を複数カラム纏めて出力

ls -l | sed '1d' | shellinq select 'new Tuple<string,int>($1,$5)'

demo3

これから

複数カラムを選択する場合が今のところかなり面倒なので何とかしたいと思っています

ls -l | sed '1d' | shellinq select '$1,$5'

ぐらいで書けると楽にできそうです

あとは入力をファイル指定出来るようにもしたいですね

雑記

ここからは雑記です
もともと雑記ですけど・・・

実装

ShelLINQは受け取ったクエリと標準入力を元に C#のソースコードを吐き出してmcsでビルド しています
なので処理の実態部分はC#です
それゆえにawk比べると速度はあまり出ません

ソースコードの生成には主に置換を使っています
例えば$1は内部クラスのメンバ変数Item1に置換されます
クエリ内に$1が表れるとItem1に置換されメソッドチェーンに連結されます
なので複雑なコードには対処できないと思います・・・(スイマセン)

C#であるためデータのに注意する必要があります
デモ3のようにstringintと書いてやらないとエラーで死にます
この辺は今後の改良で対処したいと思っています

出力について

上記まででわかる通り置換によってソースコードを作っているため期待通りの出力が得られないことがあると思います

例えばGroupByメソッドを使う場合です
img1

期待しているのと違う出力のほうが出がちです
Selectなどでうまく書いてやる必要がありますがここはLINQの知識が必要なので頑張らないとダメです
この辺の解決のために-oオプションを付けていますがstring.Joinなどで結合するようにすることを予定しています

まとめ

正直LINQが書けて超楽しいです
やることはawkで済む場合のことが多いですけど
良かったらみんなさんも使ってみてください

それではいいShelLINQライフを!

うんこがSL走らせるらしい

夜はさっむい

一年中こんな気候ならいいのに

なんかメールをのぞいたらはてなブログ

そろそろ更新しませんかだってさ
なので最近アツいシェル芸で遊んだお話を書きます

難読化シェル芸という闇

raintrees.net

シェル芸界隈に少し前に現れた究極のジャンル 難読化シェル芸
ここ最近はシェル芸で遊ぶときはこの難読化でわくわくしてます

難読化シェル芸ちょっと研究した

難読化を発見したkanata氏に感銘を受けたのでぼくも少しやってみました

その結果 日本語base64難読化シェル芸 とかいう闇を一つ深めたものを生んでしまいました

これをLT大会で話しました
その時のスライドがこれ~~
www.slideshare.net

詳細はスライドに書いてあるんですがまぁここでも

日本語base64難読化シェル芸とは

基本

日本語をbase64エンコードすることで目的のコマンドを得る手法
例えば「あ」をbase64エンコードすると 44GCCg== が得られる

$ echo "" | base64
44GCCg==

またこれを

$ echo "" | base64 | sed -E 's@(44|g==)@@g'|awk '{print tolower($1)}'
gcc

こうすることでgccコマンドが得られる うーんこれはなかなか

これは実はある法則がある
例えば「ら」「り」「る」をbase64に通すとそれぞれ 44KJ44KK44KL が得られる
つまり4文字目がアルファベット順になってる(実際は3文字目もだけど)

これを使ってコマンド列をレッツメイク

$ echo "そずねぞ" | base64 | grep -o '[a-z]' | xargs | sed 's@ @@g'
date

やったー!

他にもありまぁす

base64を複数通すことで目的のコマンド列をcutコマンドを使って取り出す手法
これはkanata師のls --helpからdateを取り出すやつを元にやってみた(上記サイト参照)

それがスライド13ページにあるやつ

$ echo "うんこ"|base64|base64|base64|cut -c 4,27|awk '{print tolower($1)}'
sl

うんこがSLを走らせる
まさに 産業革命

ちなみに目grepがつらい人たちのために

$ echo "もじれつ"|fold -w1|cat -n|grep ほしいやつ|awk '{print $1}'

これで何文字目かわかるのでcutしまくろう

これからの展望

なんかうんこを使うのが通例でトレンドなようで、様々なコマンドがうんこから錬成されてるみたいですね・・・ここまでくるとうんこに無限の可能性があるように感じてきます
どうやらシーザー暗号とかopensslを利用する手もあるようなのでそっちも試してみたいですね

日本語base64難読化シェル芸の方はさらなる隠蔽を目指してコマンド全体を覆いつくせるように模索しているところです

何かわかったらシェル芸勉強会のLTで話してみたいですね

競プロとかのテストケースを自動化したい

蜘蛛ってこわい

家蜘蛛っていいやつなのは知ってるけどデカいと怖い

テストケースを自動化したい

いまさら競プロを始めた隊長です
いまはABCを中心にやっています

基本的に
問題読む

コード書く(長考)
↓  ↑
問題のページに書いてあるテストケースを試す

提出

って流れですけどこの テストケースを試すとこが正直ダルい
そう思いませんか!?

なのでVisualStudioの単体テスト機能を使ってこれを自動化したいんですけど、ggってもあんまり出てこないので書きました。

自動化する

これをテストしたぃょ・・・

public class Calc{
    public void Solve(){
        var item=int.Parse(Console.ReadLine());
        Console.WriteLine(item);
        return;
    }
}

適当なテキストファイルにテストケースの入力と出力を一行開けて並べるだけで全部チェックしてほしぃょ・・・

123

123

456

456

↑のテキストファイルをtestcase.txtという名前でプロジェクトに追加して、以下の単体テストを実行すると・・・????

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Text;
using System.IO;

namespace CS_AtCoder.Tests {
    [TestClass()]
    public class CalcTests {
        private static bool[] active = new bool[4] { false, false, false, false };
        private static StringBuilder[] expected = new StringBuilder[4];
        private static StringBuilder[] Stdin = new StringBuilder[4];
        private const string testcasePath = @"..\..\test_case.txt";
        Calc Target = new Calc();
        [ClassInitialize()]
        public static void ClassInit(TestContext tc) {
            int cnt = 0;
            string s;
            StreamReader sr = new StreamReader(testcasePath);
            while (sr.Peek() >= 0 && cnt < 4) {
                var sb = new StringBuilder();
                while ((s = sr.ReadLine()) != "" && sr.Peek() >= 0) {
                    sb.Append(s);
                }
                Stdin[cnt] = sb;
                StringBuilder expect = new StringBuilder();
                if (sr.Peek()<0) break;
                while (sr.Peek() >= 0 && (s = sr.ReadLine()) != "") {
                    expect.Append(s);
                }
                expected[cnt] = expect;
                active[cnt] = true;
                cnt++;
            }
            sr.Close();
        }

        private Tuple<StringWriter, StringReader, StringBuilder> MakeRedirect(int index) {
            var sb = new StringBuilder();
            var sr = new StringReader(Stdin[index].ToString());
            var sw = new StringWriter(sb);
            Console.SetIn(sr);
            Console.SetOut(sw);
            return new Tuple<StringWriter, StringReader, StringBuilder>(sw, sr, sb);
        }

        //↓と同じようなのを好きなだけ並べる
        [TestMethod]
        public void Case0() {
            int index = 0;
            if (!active[index]) { Assert.IsTrue(true);return; }
            var item = MakeRedirect(index);
            Target.Solve();
            item.Item1.Close();
            item.Item2.Close();
            var actual = item.Item3.ToString().Replace("\r\n", "");
            Assert.AreEqual(expected[index].ToString(), actual);
        }
    }
}

unittest1

やったあぁぁぁ

やってること

やってることはただ単にStringBuilderに入力と出力を格納し、入力の方をStringReaderとしてstdinに割り当てる。
もう一つStringBuilderを用意し、それをStringWriterとしてstdoutに割り当てる。これをほしい出力を格納したStringBuilderAssert.AreEqualしてあげるだけです。

ちなみにテストケースは5つ以上できません。
まあでもだいたい5つもないしな~みたいなw
余ったテストメソッドはIsTrueでOKだします

もう少しいい書き方もあるだろうけど、これで動くのでこれでいいです。 テストケースをコピペするのもなんか自動化できないかな~とか思いますけどまぁこれでいいです。

よーし!めっちゃはかどりそうです!

続・世界一わかりやすい絶対パス

マスクしてると眠たくなる

解る?

改良した

ちょいと前に書いた記事でこういうのがありました
xztaityozx.hatenablog.com

この記事で公開したシェルスクリプトはちょっと アレ だったのでシェル芸っぽく書きなおしました

#!/bin/bash

dir=${1:-`pwd`}
dir=`echo $dir|sed 's/\/$//'`

length=$(echo $dir|sed -e 's@[^/]@@g'|wc -c)

echo $dir|
sed 's/\///;s/\//\n/g'|
awk -v end=$length '
  BEGIN{
    idx=0
  }
  {
    if(idx==end-2){
      print "\\\\e[1;33m/"$1
    }else if(idx<=1){
      print "\\\\e[1;30m/"$1
    }else if(idx>=end-4){
      print "\\\\e[1;34m/"$1
    }else if(idx==2){
      print "\\\\e[1;30m/.."
    }
    idx++
  }
'|
while read LINE;do
  echo -en $LINE
done
echo -e "\e[39m"

出来るだけ配列と変数を削ってパイプでつなげました。

ついでに使いやすさを考えて$1が空ならpwdを使うようにしておきました
処理としてはsedで切り分けてawkでテキスト整形してwhile read LINEで連結しただけですね
動作速度とかはどうなんだろ・・・まぁ普通に使う分には全然問題ないですね
awkってホント何でもできて怖いなって思いました

見た目とかは前回の記事と同じになっているはずです
どちらかというとこっちのが安定してるので書き換えてよかったですね

それではよい 絶対パスライフを!

AAの画像作ってくれるSlackBotつくった

Pythonむずい

勉強が足りない

AAは面白いが崩れる

AAって面白いですよね。

dic.nicovideo.jp

でも2chの専ブラとかでないと本来の見た目でなかったり、そもそも崩壊していたり・・・。

slackbot1
ブーム君のAA

Slackでもそれは例外でないです。
この間某Slackで「いよいよ明日は~」というコピペをもじった告知がありました。
slackbot2
↑こういうやつです。

画像の通り、返信には「今日と明日だよ・・・」と返すのがそれっぽいと思うんですが・・・(押しつけ)
これをそのままSlackに張り付けると
slackbot3
こんなふうにちょっとずれます。
これはこのままでも見れないこともないですけど、大きなAAになるとめちゃめちゃになります。
特にスマホとかだと確定でめちゃめちゃになります。
slackbot4
めちゃめちゃになったAAの例

AAは モナーフォント で見せるようになっている(?)ので普通のフォントでは崩れるのは当然なのですが・・・

IPAモナーフォント

崩れてしまうと何がなんだか分からずに白けてしまうので何とかしたい・・・
しかし全員にこのフォントにするように言うのはなんか違いますよね。

文字列がダメなら画像にしちゃう

画像にAAを張り付けてUpすれば全員が同じ崩れていないAAを見ることができます。
でも普通の人はそんな手間なことしませんし、そんなことしてたら会話の流れが過ぎ去っていきます。オタクの会話はスピードがはやいですから。

ここはSlackBot AAGenBot とでも名付けて、コイツにAAを画像化するのをやってもらいましょう。
slackbot5
AAGenBotのイメージ1

こうすればAAをコピペするだけでAAがずれることなくみんな見れます (実際はテキストが張り付けられるのでタイムラインがガーッと流れたりしますが・・・)

他にも特定の発言に対してそれっぽいAAを張り付ける機能も欲しいです。
slackbot6
AAGenBotのイメージ2

こうすればいちいちAAをコピペせずともBotが勝手にAAを貼ってくれます(邪魔だという意見もありそうです)

つくってワクワク

使った言語はPythonBashシェルスクリプトです。
SlackAPIを使ってメッセージの送受信と画像の生成をするのがPythonのお仕事で、それっぽいAAの画像を探したり、AA用のテキストファイルを用意したりするのをBashシェルスクリプトでやります。
Pythonはやったことなかったのでワクワクしてます

だいたいの流れはこうです
slackbot7

SlackAPIの利用にはpython-slackclientを使いました
github.com
Slackのサイトからテキトーにクリックしたら見つかったのでこれにしました(頭わるい感)
Pythonで画像を生成するにはPillowを使いました
https://pillow.readthedocs.io/en/4.1.x/pillow.readthedocs.io
なんかPILとか呼ばれてたやつ?っていうかそれのforkらしい?(詳しく調べてない)

slackbot8

つくります

できました

github.com
色々説明は面倒なのでこれをみてね

py/SlackMessageGetter.pyでメッセージを受信(送信もするけどね)して機能分け
py/aagen.pyでAAを生成 sh/search.shsh/aaImageSearcher.shで発言に合いそうな画像をさがします。二つに分けたのはsh/search.shの方をパイプに挟みたかったからです。 このsh/search.sh形態素解析とか結構謎深い検索方法をしているので興味があれば見てみてください。
sh/FileUploader.shcurl使って画像をアップロードします。なぜか前述のpython-slackclientではうpできませんでした・・・

またsh/FileUploader.shではある程度Botの画像うpを自重するようにしてます。

  1. 30秒以内に画像をうpしてたら自重
  2. 候補の画像を30分以内にうpしてたら自重

これで自重できてるのかは・・・わからん・・・な

インストールのしかた

必要なものをapt-getとかpip3します。python3がなければそれもいれてね

# mecab  ファイルサーチにつかっちゃったw
sudo apt-get install mecab
# もとPIL
sudo pip3 install Pillow
# Slackのライブラリ
sudo pip3 slackclient
  • メモ:普段あんまり使わなさそうなコマンドとか
  • jpegtopnm
  • pnmquant
  • ppmtosixel
  • これらは生成した画像をターミナルに表示するのに使っています。Sixelに対応しているターミナルでないとダメなのでいらない場合はsh/aaTextRedirector.shの25行目をコメントアウトしてOKです

普通にgit cloneします

git clone https://github.com/xztaityozx/AAGenBot
cd AAGenBot

AAGenBotはシェル変数からTokenやユーザ名、フォントへのパスを読み取るのでexportします

export SLACK_API_TOKEN="xxxx-xxxxxxxxxxxxxxxxxxxxxxxx" #APIトークン
export AAGENBOT_NAME="<@Uxxxxxxxxxx>" #Uから始まるBotのID?名前?みたいなやつ、<@ >で囲むこと
export AAGENBOT_MASTER_NAME="Uyyyyyyyyyy" #Uから始まる自分の名前?ID?。Botを管理する人の名前?ID?
export monafontPath="/home/user/.fonts/ipagp-mona.ttf" #AA作るときに使うフォント 絶対パスで書かないと見つからない?

~/.bashrcとかに書いておくと楽ちんです

シェルスクリプトに実行権限がなかったらchmodしておきます。

chmod +x sh/*

これだけだよー(^o^)/

起動方法

AAGenBotディレクトリ以下で

sh/Starter.sh

とするだけだよー(^o^)/

つかいかた

使いたいチャンネルにAAGenBotを参加させます。

生成

AAGenBotに対して生成してほしいAAの名前とAA本体を発言します
フォーマットは

@aagenbot [AAの名前]
[AA本体]

とします
slackbot9

こんなふうに結果がうpされます

ちなみにAAの名前はそのままファイル名に使われるので*/が入ると癇癪というかエラーを起こします。

呼び出し

AAGenBotに対して呼び出したい画像の名前をcallキーワードの後に付けて言うとうpしてくれます
slackbot10
こんな感じですね。
名前が間違ってるとうんともすんとも言いません(不親切)

発言に反応

これはAAGenBotに特に何か言うということはないです。
ユーザー同士の会話の中を勝手に読み取って適度にAAを張り付けます。
slackbot11
こういった感じですね

slackbot14
こんなふうに複雑な文章でもそれっぽいのを見つけてきます。
これはなんかアルゴリズムを使ったとかじゃないです。シェル芸を使ってます。(sh/search.shを見ればわかると思います。)
前述のとおり 自重システム がが備わっているので会話の流れが汚染されることは無いと思います()

終了

AAGenBotのMASTER(Botを管理するやつ)限定でSlackからAAGenBotを終了できます。
デバッグ用だったんですけど残しました。
slackbot12
こうです。煽ってるわけではないです

まとめ

Pythonはじめてでしたけどなかなか楽しかったです。またなんか作りたいですね・・・
最初は全部Pythonでやるつもりだったんですが
シェルスクリプトのが楽やわ!w
ってなったところはシェルスクリプトにしました。その結果Pythonシェルスクリプトが交互する意味の解らない構造になりましたがまあいいでしょう

まだ某Slack内で使ってないのでどれほど実用に堪えるのかわからないのですが、書く記事もちょうどなかったので書きました。
まだ生成失敗したときとか呼び出し失敗とか、エラーをユーザー達に伝えるような部分できてないのでその辺をもうちょっとだけメンテしてから某Slack内で公開したいです。どきどきする・・・

ちなみにこのAAGenBot副作用があります。
slackbot13
ALLFilesのところがめちゃめちゃになる

それでは良い AAライフ を!

参考

既知のバグ

  • 画像をUpしたあともう一度sh/FileUploader.shが呼ばれる。1行目からエラーが出て終わる
  • 下の方がはみ出る。謎

果物も食えないこんな世の中じゃ

ポイズン

最近はコタツを付けると熱いが消すと寒いので、雨が降ってほしいと願うばかりである。花粉も落ちるし。

更新をサボってたわけじゃないですよ。記事を推敲しまくってたんです(嘘)

果物を食うとだいたいのどが痒くなる

これわかる人いますか。他にもモヤシや火の通ってない豆腐などでも喉がかゆ~くなるやつです。
(僕の場合ですが)具体的に言うと のどちんこ のすぐ手前のあたり 軟口蓋 周辺がモーレツに痒くなります。(いまモーレツ宇宙海賊が予測変換に出てきて懐かしくなりました)

モヤシの場合は少しややこしくて、太めのモヤシ(緑豆や大豆モヤシ)はダメですが細めのモヤシ(黒豆モヤシ)はOKなんです。
他には高野豆腐もダメですね。
生の果物だけかと思ったら実は結構いろはすももでも来てるんですよね・・・
irohasu

だいたい花粉症のせい

僕は白樺の花粉症持ちなんですが、どうやらコイツがダメっぽいですね。
www.newsdigest.de

さらにブタクサもOUTなのでポピュラーな果物はほぼ死滅して、食えるのはブドウ、ミカンぐらいです。(バナナはなんかOK)
火を通せばだいたいのモノが食えるようになりますが・・・

わざわざ火なんか通してられるか!

という話です

かといって

かまわん!食ってやる!!

と食ってしまえば当然痒くなるので泣く泣く我慢しています。

しかし果物がダメなのはわかりましたが、大豆系がダメなのがよくわからなかったのでggりました
allabout.co.jp

どうやらここによると僕は クラス2の食物アレルギニスト のようですね  そしてその2ページ目

花粉症との関係についてですが、シラカンバ、ハンノキなどのカバノキ科の植物の花粉には、PR-10というアレルゲンタンパクが含まれています。このPR-10 と似たアレルゲンが大豆にも含まれているため(大豆の アレルゲンタンパクは「Glym4」)に、カバノキ科の花粉症患者の中には、豆乳等を飲んだ時にアレルギーを発症するケースが見られるのです。

Ah…
なるほど納得です(納豆は大丈夫)

モモが食べたい

僕の場合は 食べられへん!!! というほどではないので食べることはできますが
無理に食うことで 、これが悪化してしまうと今度はまだ発症していない食べ物にも反応してしまうかもしれない ので控えています。ジャガイモなんかが発症したらえらいことですからね

花粉症がある人は一度ggってこういうのがないか確認してみた方がいいかと思います。

!!????!??????! !!?????!??!!?? !!?????!?!?!!! !!????!????!!! !!?????!!!!!?? !!?????!?!?!!! !!????!????!!! !!?????!!!!!?? !!?????!?!!??! !!????!???!?!!

!!????!!?!!!?? !!????!?!??!?? !!????!?!!!??! !!????!!!?!!?! !!????!?!??!?? !!????!!??!??! !!?????!??!!?? !!?????!!!!?!! !!?????!?!?!!! !!?????!???!??

!???!?!????!!??? !??!!!?!???!?!! !!?????!!?!??? !!?????!??!?!! !???!?!?!?!?!!?! !!?????!!!!!!? !!?????!?!!?!! !!?????!?!!!!! !!?????!???!??

!!?????!?!??!! !!?????!!?!!!? !!??!?!!????!!! !!!!?!?!!!????? !!?????!??!!?? !???!?!?!?!?!!?! !!????!??????! !!?????!!??!!? !!?????!???!?? !!????!???!?!! !!?????!!?!??? !!?????!???!?? !!?????!???!!? !!?????!?!??!! !!?????!!?!??? !!?????!!?!!!!

!!?????!?!??!! !!?????!!?!!!? !!??!?!!????!!! !!!!?!?!!!????? !!?????!??!!?? !???!?!?!?!?!!?! !!????!??????! !!?????!!??!!? !!?????!???!?? !!????!???!?!! !!?????!!?!??? !!?????!???!?? !!?????!???!!? !!?????!?!??!! !!?????!!?!??? !!?????!!?!!!! !!???????????! !?!?!?????!!?!! !!?????!!?!!!! !!?????!!?!??? !!?????!!???!! !!?????!!??!!? !!????!?????!? !!??!!?!????!!! !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!!?!!?! !!!!!!!!???!!!!!

!!????!?????!? !!?????!!????! !!????!???!!?! !!????!??!??!! !!???????????! !!?????!???!?? !!?????!!!!!!? !!?????!?!??!! !!????!???!!?? !!????!??!??!? !!??!!?!!!!!??? !!?????!???!?? !!?????!!??!!? !!?????!???!?? !!????!???!?!! !?!????!!?!?!?! !!????!?????!? !!??!!?!????!!! !??!!!?!?!!!?!? !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!?!???! !!?????!!?!??! !!?????!!?!!?!

!!?????!!!?!!? !!?????!!???!! !!?????!!????! !!????!?????!! !!?????!?!???! !??!!!!?!?!?!?! !!!?!?!?!!?!?!? !!!???!?!??!!!? !!?????!?!!??? !!?????!!????? !!????!???!??? !!?????!!???!! !!?????!!??!!? !!????!!??!!?! !!????!?!!!!!! !!?????!!??!!! !!?????!?!!??! !!?????!??!!?? !!?????!!?!!?!

!!?????!?!??!! !!????!??!??!! !!?????!!?!?!? !!?????!?!??!! !!?????!!?!??? !!????!??!??!? !!?????!?!??!! !!?????!?!??!! !!?????!!?!?!! !!??!!?!!!!!??? !!?????!??!!!! !!?????!!?!!!? !!?????!!?!!!! !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!?!???! !!?????!!?!??! !!???????????! !!?????!?!??!! !!?????!!?!!!? !???!?!????!!??? !??!!!?!???!?!! !!????!??!??!? !???!?!?!?!?!!?! !!????!??????? !!?????!!?!?!! !!?????!!?!!!! !??!!!?!!!??!?! !??!!!?????!?!! !!?????!!?!!!? !!????!?!!?!!! !!????!?!??!!! !!????!!!?!?!! !?????!?!?!!!??? !!?????!??!!?? !!??!!!????!??! !?!??!?!?!!!??! !!?????!!????? !!????!???!???

!!??!?! !!???!! !!?!??? !!?!!!! !????? !???!? !!????!?????!? !!?????!?!!??? !!????!???!!?? !!?????!!??!?? !???!? !!!!!?? !!!??!! !!??!?! !!??!?? !????? !??!!! !!!??!! !?!!!! !!???! !?!!!! !????! !?!!!! !!??!!! !!!?!! !!!??!! !?!!!! !!???? !?!!!! !!!!!! !?!!!! !!??!!! !!!?!! !!!??!! !?!!!! !????? !?!!!! !?!!!?? !!?!!!? !?!!!! !!??!!! !??!!! !!!!!?? !!????! !!!?!!! !!?!?!! !????? !??!!! !!!!?!! !!!???? !!!??!? !!?!??! !!?!!!? !!!?!?? !????? !???!? !!?!??! !!???!? !!????! !!!??!! !!??!?! !!!!?! !!??!? !!!?!! !???!? !??!?? !!???! !!!!!?! !??!!! !!!!!?? !!???!? !!???!! !!!!!?? !!????! !!!?!!! !!?!?!! !????? !??!!! !!!!!?? !!!???? !!!??!? !!?!??! !!?!!!? !!!?!?? !!??!!? !????? !???!? !??!?! !!???!! !???!? !?!!?? !??!?? !!???! !!!?!! !!??!?! !!?!??? !!???!! !!?!!!!

!!?????!????!? !!?????!!?!??? !!?????!!?!!!! !?????! !?!??!! !????!! !??!??! !??!??! !!?????!!?!?!! !?!!??!????!??! !!???!!!!?!!?!! !!?????!?!!??! !!????!???!?!! !!?????!!????? !!?????!?!???! !!?????!!??!!! !!?????!?!!??! !!?????!!?!!?! !!!!!!!!???????!

!??!!!!?!?!?!?! !!?????!!?!!!? !!?????!?!!!!! !!????!??????! !!?????!!?!!!? !???!?!????!!??? !??!!!?!???!?!! !!?????!!?!?!? !!?????!!?!!!? !!!!!!!!???!!!!!

!!????!?????!? !!?????!??!!!! !!?????!!??!!? !!?????!??!!?! !!?????!!?!!!! !!?????!!?!??? !!?????!??!!!! !!?????!!?!?!! !!?????!!?!?!? !!?????!???!?? !!?????!!??!!! !!?????!?!!??! !!????!???!??? !!????!!!!!?!! !!????!!!!!?!! !!????!!!!!?!! !!!!!!!!???????! !!?????!?!?!!! !!?????!???!?? !!?????!!??!!? !???!?!????????? !!?????!???!!? !!?????!!?!?!? !!????!???!??! !!?????!!!???! !!?????!!???!! !!?????!!?!??? !???!??!!???!?!! !!?????!!??!!! !?!!!!!???!?!?! !!?????!???!?? !!?????!!??!!? !!?????!!!!?!! !!?????!?!?!!! !!?????!??!?!! !!?????!!???!! !!?????!?!!!!! !!?????!!????? !!?????!?!???! !!?????!!??!!! !!?????!?!!??! !!?????!!?!!?! !!!!!!!!???????!

!!?????!?!!!?! !!????!???!!?! !!?????!?!!!?! !!????!???!!?! !??????!!!!?!?!? !?!??!??????!!? !!?????!!??!!! !!????!?????!? !??!!!!?!?!?!?! !!?????!??!?!! !!?????!???!?? !!?????!!??!!? !!????!???!?!! !!?????!??!?!! !!????!???!!!! !!?????!??!?!! !!????!???!??! !!?????!!?!?!? !!?????!??!!!! !!?????!!?!?!? !!?????!!???!! !!?????!!??!!? !!?????!??!!?! !!?????!?!!!!! !!?????!!?!!!? !!?????!!??!!! !!?????!?!??!! !!?????!!?!!!? !???!!!!!?!!!?!? !!?????!!??!!! !!!!!?!?!????!? !!????!???!!!! !!????!???!?!? !!?????!!!!!!? !!?????!?!!??!

!!?????!?!!!?! !!????!???!!?? !!?????!!??!!! !!?????!!?!!!! !?????!??!!?!!!! !!?????!???!?? !??!!!?!???!!?? !??!??????!!??!? !!??!?!?!!!???? !!????!!!?!??! !!????!?!??!?? !!????!!?!?!?! !!????!??!??!? !!!!!!!!???????!

!?!??!!!!????!? !?????????????!!

!?????! !?!??!! !????!! !??!??! !??!??! !!????!?!!??!! !!????!!!!!!?? !!????!!??!??! !?!!??!????!??! !!???!!!!?!!?!! !!?!?!??!?!!!!!