WindowsでSource Code Pro をビルドしようと思うも一筋縄ではいかなかった話

最近、エディタをSpacemacsに乗り換えました。 Spacemacsのデフォルト設定では、フォントがSource Code Proなので、Source Code Proが入っていない場合は起動後にWarningが出ます。
おそらく代替フォントで描画されているかと思いますが、特に日本語の描画が大変厳しいようで、お世辞にも綺麗とは言えない状況でした。

そこで、Source Code Proを導入することとしました。

Source Code Pro(GitHub)にもあるように、 リリースページからダウンロードすればよい のですが、 github.com 見事にそのリンクの情報を見逃した私は、ソースコードからビルドすることにしました。

基本的にはREADMEにしたがって行えばよいのですが、私の環境(Windows10)ではbuild.cmdの実行時にエラーが発生しました。

syntax error at "}" missing ";" 

当該リポジトリのIssueにも同様の事例が報告されていませんでしたので、困惑していたのですが、別リポジトリのIssueを参考に解決することができました。 つまり、familyTables.feaの2行目のセミコロンを削除する ことで対処できました。

github.com

いやわざわざビルドしなくていいのに、と言われればそれまでなのですが。。。

Rustで数値シミュレーションをする際に使用しているライブラリ(クレート)の紹介

この記事はTUT Advent Calendar 2017の18日目の記事です。

概要

普段私はRustを使ってシミュレータを作成しているのですが、そこでお世話になっているライブラリ(クレート)を簡単に紹介します。
今回紹介するライブラリ(用途)は以下の2つです。

  • rayon (データ並列化)
  • statrs (様々な確率分布に従う乱数生成)

rayon

すでにいくつかの記事で紹介されていますが、rayonによって主に以下の処理を簡潔に行うことができます。両者とも、Work stealingと呼ばれる方法で実現されているそうです。

  • スレッドによる並列処理
  • 再帰関数の分割処理

スレッドによる並列処理

rayonでは、Parallel Iteratorの導入によって通常のイテレータに対する関数(mapなど)の処理を並列化することができます。D言語でいうstd.parallelismに似ているかもしれません。 ただし、Rustのシステムによって並列時の安全性は保証されています。つまり、標準ライブラリのスレッド同様、Sendトレイト、Syncトレイトの制約によってコンパイル時に検査が行われます。

iter関数やinto_iter関数が実装されている型は基本的にParallel Iteratorに変換することができ、以下のような対応関係を持ちます。

コード例は以下のとおりです。

extern crate rayon;
use rayon::prelude::*;

fn main() {
    (0..100)
    .into_par_iter()
    .map(|i| i * 2)
    .for_each(|x| println!("{}",x));
// 200未満の偶数が出力される
}

大変便利ですが、Parallel Iteratorに対してcollect::<Vec<_>>()のようなことはできず、 あらかじめ格納先のベクタをミュータブルとして宣言したのち、collect_into関数を用いて格納する必要があります。

コード例は以下のとおりです。

extern crate rayon;
use rayon::prelude::*;

fn main() {
    let mut even_vec = vec![0;100];
    (0..100)
    .into_par_iter()
    .map(|i| i * 2)
    .collect_into(&mut even_vec);
// 200未満の偶数がeven_vecに格納される
}

並列処理時のスレッド数は自動的に決定されますが、スレッド数を能動的に設定したい場合は、num_threads関数によって設定可能です。
また、実行時にスレッド数を設定したい場合は、環境変数RAYON_NUM_THREADSの値を設定してから実行することで対応できます。

再帰関数の分割処理

join関数によって、再帰関数を効率的に並列処理します。
rayonのGithubリポジトリの例が簡潔であるため、引用します。 コード例は、並列化したクイックソートのようです。

fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    rayon::join(|| quick_sort(lo), || quick_sort(hi));
}

リポジトリのREADMEによれば、

Note though that calling join is very different from just spawning two threads in terms of performance. 
This is because join does not guarantee that the two closures will run in parallel. 
If all of your CPUs are already busy with other work, Rayon will instead opt to run them sequentially. 
The call to join is designed to have very low overhead in that case, so that you can safely call it even with very small workloads (as in the example above).

とのことですので、join関数の引数のクロージャを、可能なら並列処理し、CPUの負荷が高い場合は逐次的に処理するよう、うまくスケジューリングされる(?)ようです。

statrs

statrsはRustにおける統計的な数値計算のためのクレートで、特に私はdistributionモジュールを頻繁に利用しています。distributionモジュールを利用することで、各種確率分布に従う乱数を容易に生成することができます。
ただし、rust statrsで検索すると検索エンジンが変に気を利かせて別の検索ワードを推奨してくるので注意です。

離散型分布、連続型分布問わず様々な分布に対応しており、以下の分布ごとに構造体が定義されています(非常に多い)。また、分布の期待値、分散を返す関数が各分布に実装されており、離散型確率分布に対しては確率質量関数の値、連続型確率分布に対しては確率密度関数の値を返す関数が実装されています。

さらに、sample関数(rand::Rngトレイトを実装する乱数生成器を引数)の実装によって、各分布に従う乱数を生成可能です。 rand::Rngトレイトを実装している乱数生成器であればよいため、StdRngやThreadRng、XorShiftなど様々なものに対応しています。

  • 離散型確率分布
    • ベルヌーイ分布
    • 二項分布
    • カテゴリカル分布(Categorical Distribution)
    • 離散一様分布
    • 幾何分布
    • 超幾何分布
    • 多項分布
    • ポワソン分布
  • 連続型確率分布
    • ベータ分布
    • コーシー分布
    • カイ分布
    • カイ2乗分布
    • ディリクレ分布
    • アーラン分布
    • 指数分布
    • F(Fisher-Snedecor)分布
    • ガンマ分布
    • 逆ガンマ分布
    • 対数正規分布
    • 正規分布
    • パレート分布
    • t分布
    • 三角分布
    • 連続一様分布
    • ワイブル分布

本記事ではdistributionモジュールにのみ着目しましたが、他のモジュールにてベータ関数やエラー関数などの複雑な関数の値を計算する関数も用意されているようです。

まとめ

rayonやstatrsは、数値シミュレーションをするうえで、大変有用です。並列処理により計算時間を削減することができ、randクレートでは対応していない様々な確率分布に従う乱数生成を行うことで様々なシミュレーションが可能です。
また、Rustはコンパイラが大変優秀なので、コンパイル時の検査のおかげでシミュレーション実行時のエラーがなく、重宝しています。その一方で、コンパイルを通すのは大変ですが。

私の身の回りでもRustが流行ればいいな、と思うこの頃です。

apt-get dist-upgradeで無線LANに接続できなくなった

概要

Raspbian jessie on Raspberry Pi (1st)の環境で、apt-get dist-upgradeを実行したら無線LANに接続できなくなったので、その解決(?)までの過程を記事に残します。

問題

dist-upgrade実行後に、

  • Raspberry Piが設定した固定IPではなくなる
  • そもそもwlan0にIPが割り振られなくなる

といった問題が発生しました。

解決まで

IPが降られなくなる

$ dmesg

でログを確認してみると、wpa_supplicantがCTRL-EVENT-DISCONNECTEDというエラーで異常終了している様子。

$ cat /etc/wpa_supplicant/wpa_supplicant.conf  

で設定を見てみると、先頭に下記の謎の設定が自動的に追加されていました。

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

この設定を削除して元のwpa_supplicant.confに戻すと、無線LANに接続できました。

固定IPでなくなる

dhcpcd.confを覗いてみると、設定が初期値に戻されていたので、以前と同様に設定を行うことで固定IPの設定も難なくできました。
dist-upgradeの途中で設定ファイル群を更新したことが原因のようです。

まとめ

dist-upgradeの途中で設定を更新するときは要注意。

TDD challenge #1 に参加してきました

概要

2017年8月18日に株式会社ミクシィ 渋谷本社にて開催されたTDD challengeに参加してきました。

このワークショップは、テスト駆動開発(TDD: Test Driven Development)の講義と、ペアプログラミングの形式をとった実習から構成されていて、講義と演習を交互に行うものでした。

当日の流れ

開始前

11:00より受付とのことだったので、あらかじめ場所を確認しておいて、定刻どおりに受付まで行いました。
JR渋谷駅のすぐ近くだったので、迷わずに済みました。

各種設定(演習用)

予め決められたペアで演習を行いました。パートナーの方と共通して使用できる言語がJavaしかなかったので、使用言語はJavaとしました。

全体としては、Javaを使用するペアが多数でしたが、他にもRubyPythonなどを使用するペアもあって、多種多様でした。
とはいえ、私たちのペアは両者ともにしばらくJavaを書いていなかったので、些か不安でした。

講義

なぜTDDが必要なのか、どのような状況でTDDがその威力を発揮するのか、TDDを始めるにあたって意識すべきことは何か、といった点に重点を置いた講義だったように感じました。

TDDでは、その名にあるとおり、

  1. テストを書く
  2. テストを失敗させる
  3. (テストを通すように)実装する

の順で開発を行います。リファクタリングは最後、つまりテストが通っている前提で行うべき、とのことでした。 特に、TDDにおいて意識すべきことやメリットが次節の演習で大きく効いていた印象を受けました。

演習

講義の合間合間に複数の課題が発表されて、要求された仕様を満たすようにペアでTDDによる実装を行います。

一見実装が楽に思える仕様でも、TDDを意識して、テストを先に完成させることに重点を置きました。 私たちのペアはJavaを使用したので、テストフレームワークとしてJUnitを採用しました。
両者ともにJUnitは未経験だったため、講師の方々に助けて頂きながら試行錯誤しました。

演習の前半ではつい実装の方に手を伸ばしてしまいそうになっていましたが、後半ではテストを書くことに多くの時間を割くようになっていました。
各課題ともに1~2時間程度(正確に測ったわけではないのでそれ以上の可能性もある)の時間が設けられていましたが、テストの作製に時間を要したこともあって、すべての課題を完成させることはできませんでした。

しかし、テストを作成するにあたって、テストがコード自体(クラスなど)の設計に直結しているように感じて、テストを完成させたあとの実装は想像以上に速く行うことができたように思いました。

その他

エンジニアの方々とともに昼食をいただきました。8月18日が「お米の日」(「米」を分解すると八 十八 になることから。米寿の由来と同様?)ということで、釜めしをいただきました。お茶漬け用の出汁もあり、二重の楽しみがあって大変おいしくいただきました。

食事中にはエンジニアの方と研究の話題や普段のコーディング、プログラミング言語全般など、様々なお話をさせていただいて、大変有意義な時間となりました。

まとめ

普段私はプロジェクト(もしくはオブジェクト指向としてのクラス)を設計するとき、必要なメソッド群を脳内で雑に実装を始める、といったことをしていたのですが、実際はそれはTDDに直結していて、そのまま置き換えられるように感じました。
TDDではテストがそのまま実装される関数群を網羅していて、かつテストケースとして期待する振る舞いを記述することから、むしろ仕様がテストに表れている印象を受けました。
期待される動作がテストに書かれているため、実装の際に混乱することを抑制することができ、実装をスムーズに行うことができたように思います。

言語によってテストフレームワークは異なりますが、幸い普段私が使用する言語は優秀なフレームワークが整っているようなので、今後TDDを活用していきたい、と強く感じたワークショップとなりました。

最後になりますが、ワークショップを通して丁寧に指導してくださった講師の皆様、本当にありがとうございました。 また、参加者の皆さん、お疲れ様でした。

Phoenix on Raspberry Pi の導入メモ

プログラミング言語ElixirのWebフレームワークである「Phoenix」をRaspberry Pi 1 に導入してみましたが、いたるところで詰まったのでメモしておきます。

# 目標 Raspberry Pi Model BにPhoenixを導入する。

今時何で初代を使っているのだろう。。

問題1

「Elixir Phoenix」などのワードで検索すると、多くの記事で以下のような導入方法が紹介されています。

qiita.com

この記事におけるPhoenixの導入方法を試みると、まずmix local.hexの実行の時点で失敗しました。エラー文の画像はありませんが、以下のような内容です。
Phoenixの導入のためにはhexは必須ではありませんが、mix archive ~の部分も同様のエラーで失敗します。

stackoverflow.com

エラー文から察するに、SSLが有効になっていないことが原因のようです。 opensslが入っていることを確認したうえでErlang VMの再導入を試みましたが、無駄のようです。

そこで、Erlang VMソースコードからビルドすることを試みます。Erlang VMをビルドしてSSLを有効にする方法は以下の記事を始めとして多くの記事で紹介されているように、kerlというバージョンマネージャを使用すれば容易に行うことができますが、コンパイルオプションが曲者でした。

qiita.com

紆余曲折あり、私の環境ではオプションを以下のようにすることでようやくSSLが有効になりました。

KERL_CONFIGURE_OPTIONS="--enable-dynamic-ssl-lib --with-ssl=/usr/bin/ --disable-hipe --enable-smp-support --enable-threads --enable-kernel-poll --disable-native-libs --without-javac"

このオプションでビルドした場合、初代Raspberry Piのスペックでは数時間から半日ほど時間を要しました。

問題2

Erlang VMソースコードからビルドし、Elixirの環境を導入します。 Elixirの環境は、

$ apt-get install elixir

で問題ありませんでした。
導入後、

$ mix phoenix.new phoenix_sample
$ cd phoenix_sample
$ mix phoenix.server

とすることで導入完了の予定でしたが、ここで別の問題が発生しました。mix phoenix.serverの実行中に行われるnpm installでエラーが発生し、失敗する、というものでした。

ここでnpmのバージョンを調べると、

$ npm --version

v0.14.21という、古すぎるバージョンであることが判明したため、以下の記事に則ってnpm(及びnode)のアップデートを行い、

qiita.com

再び

$ mix phoenix.server

とすることで解決できました。

導入成功

導入後、初回起動時はコンパイルが多く、ある程度時間を要しましたが、実際にブラウザからアクセスでき、同時にログ出力も確認されました。
ログの応答速度から察するに、初代Raspberry Piにとってはある程度の速度が出ている・・・のか?というところです。

f:id:je6bmq:20170131213245p:plain f:id:je6bmq:20170131213322p:plain

CTF for ビギナーズ Finalに参加しました(感想+Write up)

Outline

初CTF(at セキュリティ・キャンプ2016)以来、CTFに興味があったのでCTF for ビギナーズ FINALに参加してきました。

2016.seccon.jp

午前中から昼過ぎまでWeb、Forensics、Binaryについての講義が行われ、最後に参加者全員でCTFを行いました。 今回のCTFは初の400人規模のCTFとのことで、独特の雰囲気だったことを覚えています。。 このCTFは講義の復習+αのような内容で、難しくも楽しかったです。

私が解けた問題は以下の通りです。 f:id:je6bmq:20170130210532p:plain

Webの「もぐもぐ」問題、Forensicsの「あけてみよう」問題、Binaryの「HiddenFlag」問題は今思うとあと1歩で解くことができたような(気がする)問題だったので、悔しい限りです。
後でちゃんと解こうと思います。

以降、Write upです。

Write up

スクリーンショットの撮り忘れにより、(特にWebの)情報量が少ないことをお許しください。 また、フラグの形式はctf4b{文字列}です。

Web

Classical Injection

フォームに' OR 1=1を入力すると、フラグが表示されました。

(講義中で「もっとも単純なインジェクションの1つです」のような話があったような・・・?)

May the extensions be with you.

提示されたURLを開くと、「あなたは管理者(admin)ではない」(曖昧)といったメッセージが表示されており、問題から、ブラウザの拡張機能を使用すればよいのでは?と想像しました。

演習環境のブラウザ(Chrome)には、EditThisCookieという拡張機能があり、Cookieの値を参照、変更することができるため、使用してみると、admin(といった名前)のCookiefalseの値を持っていることが確認できました。

そこで、この値をtrueに変更し、リロードするとフラグが表示されました。

Forensics

みつけてみよう

与えられたpcapファイルをWiresharkで開いて、パケットの統計(Packet Hierarchy)を見ると、以下のようにHTTPでいくつかパケットが飛んでいることが分かりました。

f:id:je6bmq:20170130213953p:plain

これらのパケットを眺めていると、無数のctf4b{}の文字が。
そこで、Wiresharkのフィルタリング機能でframe matches "ctf4b"とすると、フラグが出てきました。
正規表現で検索するなど、より理論的な解法は多々あるかと思われる)

Binary

復習

2つのセクションのアセンブリの処理を追った結果得られる最終的なeaxの値を"_(アンダーバー)"で結合したものをフラグとする、といった問題でした。

1つ目のセクションではeaxの初期値を3として、8倍したあと、3を引き、さらに2を足す、といった処理をしていたので、最終的なeaxの値は23となったと思います。

2つ目のセクションでは、ループのインデックスが1から5まで増加する計5回のループ中に、毎回eaxに対してループインデックスが乗算されるような挙動に思えたため、5の階乗、つまり120が得られ、

ctf4b{23_120}

をsubmitした結果通ったと思います。
アセンブリのファイルが手元になく、曖昧さが大きいです)

bashとgnuplot (時々awk)でデータ整理!

この記事はTUT Advent Calendar 2016の22日目の記事です。

本年度の私は、いろいろなデータをCSVファイルで吐かせて、そのデータ整理を行っていました。
データ整理の際、表計算ソフトが重い、そこまで面倒なことはさせない、といった場合のデータ整理でしたら、シェルスクリプトgnuplot(時々awk)でこなすことができます。
特に最近ではbash on Windowsの登場でWindowsでもbashスクリプトを書くことが可能になりましたので、お勧めしたいと思います。(表計算ソフトが重かったりしますし。。)

以降、今年1年で私が得られた知見について書き連ねていきます。 大まかな流れは以下の通りです。

  • 統計処理
  • ファイル整理
  • グラフ化

統計処理

統計処理として、今回取り上げるのは平均分散最大値(最小値)です。
今回は、CSVファイル(hoge.csv)が以下のような構成をしていることを想定します。

キー(サンプル番号やx軸に対応するもの), データ

つまり、ファイルの2列目を使用したい場合です。

平均

bashで平均値を求めたい場合は、awkを使用して1行で書くことができます。平均値meanは、以下のコードで求めることができます。
awkのオプション、-Fでファイルのデリミタ(区切り文字)である","を指定しています。

mean=$(cat hoge.csv | awk -F,  '{m+=$2} END{print m/NR;}')

awkのオプション、-Fでファイルのデリミタ(区切り文字)である","を指定し、ファイル中の2列目($2)の総和をとり、行数(NR)で割るのみです。

分散

先ほど求めた平均値meanを用いて、分散varianceは容易に求めることができます。

mean=$(cat hoge.csv | awk -F,  '{m+=$2} END{print m/NR;}')
variance=$(cat hoge.csv | awk -F,  "{m+=(\$2-${mean})^2} END{print m/NR;}")

meanの例とは異なり、awkの引数をダブルクォートで囲んでいます。これにより、列数を指定する$はエスケープが必要ですが、代わりにbashの変数展開を用いることができますので、分散を楽に求めることができます。(もっと楽な方法があるかもしれません)

最大値(最小値)

最大値は、awkを用いて次のコードで求めることができます。

max=$(cat hoge.csv | awk '{if(m<$2) m=$2} END{print m}')

最小値はこのコードの不等号を入れ替えるだけで求めることができます。

ただし、hoge.csvの2列目がすべて0の場合、結果として空の文字が返ってくるようです。(比較に失敗し続ける?) そのため、awkのBEGINセクションで初期値を与えてあげるのが無難化と思われます。

ファイル整理

ファイル整理として、例えばファイルの分離にはheadやtailコマンドで行うことができ、ファイルの結合にはcatコマンドでファイルを全て指定することで行うことができます。

本記事で着目するのは、ファイルの列結合です。

catコマンドで行うことができるのはファイルを縦に結合する場合です。ファイルを列で結合するには、joinコマンドを用います。
例えばhoge.csvの同様のファイル形式を持つfuga.csvがある場合、その2つのファイルを同一キーで列結合するには、以下のコマンドで行うことができます。

join -t, hoge.csv fuga.csv >> hogefuga.csv

デリミタを-tオプションで指定し、標準出力に結果が出力されるため、結合後ファイル(hogefuga.csv)に記録します。
joinコマンドはデフォルトでは1列目をキーに、2列目をデータとして処理します。 キーとなる列とデータ列を明示的に指定する場合は、それぞれ-1、-2オプションによって列番号を指定することで処理が可能です。

ただし、このjoinコマンドは同時に2ファイルのみを結合可能という制約がありますので、3ファイル以上を結合したい場合はmktempコマンドなどで一時ファイルを生成してから順番に結合する必要があります。

グラフ化

gnuplotを使う際に多用した機能について述べます。
gnuplotCSVファイルを用いる際は、あらかじめ、

set datafile separator ","

とすることで、読み込むファイルのデリミタを","に指定します。

プロット時の簡単な数値処理

グラフを描画する際、ちょっとした規格化や簡単な単位調整等が必要な場合があります。つまり、ある列のすべての値に対して同じ演算を施したい場合、gnuplotで描画する場合に処理を行うことができます。

通常、hoge.csvの1列目を第1軸に、2列目を第2軸として描画を行う場合、以下のようなコマンドをgnuplotでは実行します。

plot "hoge.csv" using 1:2 

例えばデータを1000倍して表示したい場合、以下のようなコマンドを実行します。

plot "hoge.csv" using 1:($2*1000) 

関数近似

gnuplotでは、関数近似も容易に行うことができます。
例えばhoge.csvの1列目をx軸、2列目をy軸の値として

y=ax2+bx+c

の式で近似したい場合は、以下のコマンドを実行します。

f(x)=a*x**2+b*x+c
fit f(x) "hoge.csv" using 1:2 via a,b,c

このコマンドでは、f(x)としてフィッティング先の関数を、via以降で最適化するパラメータを列挙する必要があります。
フィッティング結果(係数の値、誤差)は、標準出力に出力されるとともに、logファイルが生成され、そこに書き込まれます。

文字列

複数のcsvファイルを同時に描画したい場合があるかと思います。 gnuplotではsprintf関数をサポートしているため、以下のような構文を用いることができます。

i=3
s(n)=sprintf("hoge_n%d.csv",n)
s(i)
# s(i) equals to hoge_n3.csv

また、gnuplot version 5.0以降では、描画時ループ構文をサポートしましたので、以下のような構文を用いることができます。

plot for[i=1:10] s(i) using 1:2

gnuplotではスクリプトファイルを作成し、gnuplot内でloadコマンドを実行することでコマンドをまとめて実行することができますが、 文字列処理や使用ファイル数が煩雑になる場合、bashスクリプトgnuplotスクリプトファイルを自動生成することも手です。

bashでは文字列内の変数展開が可能ですので、ファイル名の指定が容易に可能です。

まとめ

bashgnuplotを用いることで、簡単なデータ整理を行うことができます。

個人的にはjoinコマンドにより、表計算ソフトでの負担が大きく減るように思います。

明日のAdvent Calendarは・・・?

明日23日目のAdvent Calendarは@emaxserさんの担当です。「何か頑張る」とのことなので何やら楽しみです。
無理しない程度に頑張っていただきたいです。