2020東京オリンピックエンブレム(新しい方)の面積

つい昨日、2020年東京オリンピックのエンブレムが決まりました。
ぱっと見たところ、適当に四角形を並べただけじゃないかと思いましたが、よく見ると結構数学的に面白いなあと感じて、いろいろ計算し始めていました。
ふと検索してみると前のエンブレムの面積を計算したとかいうのが話題になってて、じゃあやってみようかな、とやってみました。
結論から言うと、オリンピックエンブレムの色のついた部分の面積は、正方形の1辺を1としたとき
18+9√3≒33.59
でした。

詳しくはPDFをどうぞ
http://www1.axfc.net/u/3657007

PDFアップロードできないなんて面倒だな。






NIC1つのWindowsマシンでルータ+OpenVPNサーバ

前口上+PC構成

OpenVPNでリモートから自宅にアクセスできるようにしていたのだけど、
いちいちメインPCをwolで起こすのが無駄に感じたので、2ヶ月ぐらい前にサーバ用PCを自作してみました。
ついでに古いルータも置き換えちゃおうって軽い気持ちで思ってました。
構成はこんな感じ

  • CPU : Athlon5350
  • マザボ : MSI AM1I
  • メモリ : 4GBx1
  • HDD : 3TB
  • 電源 : 100W ACアダプタ
  • ケース : ホームセンターで買った収納ボックス

24時間動かすもんだから省電力がいいと言うことで調べたところ、
5350+AM1Iが良いと言う評判だったので買ってみました。
AMDの自作はやったことがなくて興味があったってのもあるけど。
ACアダプタも効率が良いらしいと言うことで。
でかい立派なケースも要らないので、1000円ぐらいのケースを
糸鋸+千枚通しで加工してケースにしました。

あと、Gigabitハブも買ってきました。

やりたいこと

設定するに当たっての条件は次の通り。

  • Windows上に作りたい : TS鯖にしたり最近容量無制限になったOneDriveにいろいろ上げたりしたかったから
  • NICは1つで : 普通ルータを作るって言うとNIC2つでやるけど原理上は1つでもいけるはず。NIC代をけちりたい。
  • OpenVPN鯖を動かす : できればtapで
  • インターネットはFTTH(PPPOE接続)

ルータとか鯖だったらLinuxが基本とか言われていますが、
まあなんとかなるでしょう。Windows好きだし。

いろいろやってみた

で、ルータ兼サーバなんて簡単に設定できるだろうと思ってたんですが、
いろいろ不都合が出てきてずいぶん大変でした。
2ヶ月ぐらいやってました。
結果だけ知りたい人はすっ飛ばしてください。

とりあえずOpenVPNとPPPOE

とりあえずルーティングは後回しにして、
windows8.1にOpenVPN(tap)を入れてPPPOEでインターネットにつないで見ました。
そしたら、
見事にBSOD(IRQL_NOT_LESS_OR_EQUAL)…

tapのOpenVPNサーバを構成するにはブリッジ接続が必要で、
ブリッジ接続とPPPOE接続を同時にするとBSODが出るようでした。
…じゃあルータとOpenVPNサーバを同時に動かすのは無理なのか?

しょうがないから仮想マシン上でいろいろ動かそう。

VMWare上にルータを構成してみる

仮想マシンでルータとOpenVPNサーバを動かせばいけるか?ということでやってみました。
OSは以前のルータで使ってたdd-wrtと本格的なルータOSのvyOS。
どっちも一応ルーティングができるところまで設定してみたけど、
パフォーマンスがいまいち(50Mbpsぐらいしか出なそう)だったので却下。

Win8.1単独で頑張ってみる

ルーティングに関してはNAT32とかいうルータ化ソフトがあるらしい。
OpenVPNはtunならブリッジしないし何とかなるんじゃないか?
と言うことで仮想マシンを入れないで頑張ってみました。

結果

  • NAT32

遅すぎて話にならない。
あと使い方もよく分からないなあ。
ずいぶん昔に作られたソフトっぽいし。

  • tun

サーバマシンとリモートの通信はできました。
しかし、ルーティングして自宅の別マシンとリモートで通信しようとすると上手くいきませんでした。
どうやら、tunで別マシンとリモート間の通信をするにはNICをプロミスキャスモードと言うモードにしなければいけないけれど、Windowsだとそのプロミスキャスモードにできないらしいです。

Windowsだめじゃん…

Windows server2012を入れてみる

WindowsにはサーバOSがあります。
サーバOSならルーティングとかいろいろできるのでは?という
淡い希望を持って入れてみました。
無料で使えるご身分だったので。

結果
ルーティング設定とかいろいろいじってみましたが、
NIC1つでルーティングを可能にする方法は見つかりませんでした。

しかも、Athlon5350+AM1IのマシンではWindows serverにおいてグラボがちゃんと動かず画面が真っ暗(リモートデスクトップでしか操作できない)
という問題もあり、サーバOSは諦めました。

インターネット接続の共有

いろいろ調べているうちに、Windows(クライアントOS含む)にはインターネット接続の共有(ICS)という機能があることを見つけました。
一見ライトユーザ向けの機能に見えるこの機能は意外と使えるもので、
ちゃんとルータとして動いてポートフォワーディングも使えます。
設定すると勝手にIPアドレスとサブネットを決められてしまいますが、後から変えられます。
他のマシンもサブネット内の固定アドレスを設定すればちゃんと動きます。

VMwareOpenVPNを動かす

後はOpenVPN(tap)ですが、今までやったように直接Windows8.1上では動かなそうなので、
VMware上のubuntuに入れてみました。
http://www.slsmk.com/getting-started-with-openvpn/installing-openvpn-on-ubuntu-server-12-04-or-14-04-using-tap/
の通りやると、すんなり設定できました。
いやーLinuxってWindowsと違って素晴らしいですね。

しかし、仮想マシンpingを送ると4倍ぐらいに増殖した応答が返ってくるという謎の現象が起こってしまいました。
これのせいでOpenVPNも異常なパケットが来たと認識してちゃんとつながりません。
どうやら、ルーティング機能を有効にしているとパケットが増殖してしまうようです。

じゃあ仮想化ソフト変えようか。

Hyper-Vにしてみる

VMwareではなくMicrosoft謹製の仮想化ソフトHyper-VOpenVPNを動かしてみました。
GUIはめちゃくちゃ重い(MS糞すぎる)けれど別にsshでつないで設定すれば良いので影響はありません。

やっとOpenVPNも正常に動きました。
なぜかVPNプロトコルUDPだと10Mbpsぐらいしか出なくてTCPだと40Mbps出るという
不思議な現象が起こっていますが。

その後

いろいろいじってるとたまにBSOD(UNEXPECTED_KERNEL_MODE_TRAP)が出ました。
ドライバが原因のことが多いらしいので、Catalystを新しいのにしたりしたら出なくなりました。
その代わりに再起動時にBSOD(DRIVER_POWER_STATE_FAILURE)が出るようになったけれど、再起動はあんまりしないし実害もないのでいいや。

結論

結論として、次のような構成にすれば、
NIC1つのWindows8.1上でルータとtapのOpenVPNサーバが作れました。

ポイントとしては

  • ルータのマシンでインターネット接続の共有を使う
  • 仮想マシンNICはブリッジ接続にする
  • OpenVPNプロトコルは何故かTCPの方が速い
  • PPPOE接続はrasdialコマンドでスタートアップ時につなげる

こんなところでしょうか。

ネットワーク関係をやるんだったらWindowsじゃなくてLinuxを使えという忠告が嫌と言うほど実感できた2ヶ月でした…

epsをきれいなemfに変換する

TeXとかで普通使われるベクタイメージはepsですが、Wordとかではemfが使われます。
この2つを変換する方法はいろいろなところで紹介されていますが、ひどい出来になったりフォントが変わったりフォントがラスタライズされたりと、いまいちな物が多い印象です。

今回、epsからemfに綺麗なベクタイメージのまま変換する方法が分かったので書きます。
必要なものはGhostScriptとInkscapeです。
Inkscapeは普通の最新版はバグがあるのでPortable版がいいです。
(参考:http://inkscape.paix.jp/manual/overview/known-problems/)
GhostScriptのgswin32cが実行できるようにpathを通しておいてください。
(C:\Program Files\gs\gs9.10\bin とか)

  1. 以下の内容をoutline.batとかいう名前で保存する

gswin32c -dNOPAUSE -dBATCH -dNOCACHE -sDEVICE=epswrite -sOutputFile=%~n1_outline.eps %1
gswin32c -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dEPSCrop -sOutputFile=%~n1_outline.pdf %~n1_outline.eps

  1. 「outline.bat epsファイル名」でepsをアウトライン化してpdfにする
  2. 生成されたpdfをInkscapeで開く
  3. Inkscapeでemf保存

これでフォントを含めアウトライン化された、元のepsと遜色ないemfが出来上がります。
batファイル内の-dNOCACHEが大事で、これがないとeps中の文字がラスタライズされてしまうみたいです。

指定した時間にPCを起動する

朝起きたときにPCが立ち上がっていて欲しいとか、指定した時間にPCを起動したいときがあります。
休止状態からなら、タスクスケジューラを使って起動することができます。

  1. ファイル名を指定して実行から"taskschd.msc"でタスクスケジューラを起動する
  2. 右列の「タスクの作成」
  3. 全般タブ:名前は適当
  4. トリガー:起動したい時間を指定
  5. 操作:適当なコマンドを入れる。今回は起こすだけだから何でもいい。"timeout /t 10"とか。
  6. 条件:「タスクを実行するためにスリープを解除する」にチェック。これが一番大事
  7. OK

これで休止状態にしておくと、指定した時間のちょっと前にPCが起動します。

コマンドプロンプトでsuを使いたい

コマンドプロンプトで管理者権限が必要な操作をするとき、

  • 検索窓からcmdとか打つ
  • 右クリック->管理者として実行
  • UACが出てくるからボタンを押す
  • カレントディレクトリがC:\Windows\system32になるから、移動する

とかしなければいけなくて、非常に面倒です。
そこで、手軽に管理者権限のコマンドプロンプトを立ち上げられる方法を考えました。
欲しい機能は以下の通り。

  • パスワード入力が要らない … runasを使うとパスワード入力が必要
  • UACを出さない
  • カレントディレクトリが引き継がれる

つまり、unixでいうsuコマンドのパスワードが要らないバージョンが欲しい訳です。

UACを出さないで管理者権限で実行する方法には、タスクスケジューラを用いる方法があります。
タスクスケジューラから実行されるタスクに直接カレントディレクトリを与えることはできないため、ファイルで受け渡ししました。


処理の流れは以下の通り

  1. su.batを起動する
  2. su.batが、カレントディレクトリを記入したファイルpwd.txtを作る
  3. su.batが、あらかじめ登録しておいたタスク"su"を実行
  4. タスク"su"が、su_child.batを管理者権限で実行
  5. su_child.batがpwd.txtを読み、カレントディレクトリを取得
  6. su_child.batが、元のカレントディレクトリでコマンドプロンプトを起動

su.bat

cd>%USERPROFILE%\pwd.txt
schtasks /run /tn su

su_child.bat

for /f %%i in ('type %USERPROFILE%\pwd.txt') do cmd /k "cd %%i & del %USERPROFILE%\pwd.txt"

タスク"su"

全般タブ:"最上位の特権で実行する" をチェック
トリガー:空欄のまま
操作:プログラムの開始 su_child.bat (パスが通っていない場合は絶対パスで)
設定:"タスクが既に実行中の場合に適用される規則"を"新しいインスタンスを並列で実行"にする (複数のコマンドプロンプトを起動できるように)

これでsu.batを起動すると、カレントディレクトリを保ったまま、UAEのダイアログなしで管理者権限のコマンドプロンプトが立ち上がります。

pwd.txtはホームディレクトリ(C:\Users\ユーザ名\)に作られますが、su.batとsu_child.batの%USERPROFILE%\pwd.txtのところを変えれば自由に変えられます。
(%USERPROFILE%はホームディレクトリを示す環境変数)

コマンドプロンプトで無限ループ

コマンドプロンプトやバッチファイルで無限ループしたいことがよくあります。
そういうときには、次のように書くと上手くいくみたいです。

for /l %i in () do コマンド

本来、forコマンドの/lオプションは、変数%iを(開始,ステップ,終了)まで動かしてコマンドを実行するものですが、()内を空にすると無限ループするようです。
もうちょっといろいろ実験してみると、

  • ()内に数字以外の文字が入っていても無限ループする
  • ()内に数字が1文字でも入っていて、文法が正しくない(=(1,1,10)とかになっていない)ときには、1度もコマンドは実行されず終わる

という挙動をするようです。

ちなみに、無限ループ中でstartコマンド(=ファイル名を指定して実行みたいなもの)を使うとウインドウが無限に開いて大変なことになるので注意してください。
やってみたい場合はシャットダウンしていい状態にしてどうぞ。

pythonの日本語

pythonで日本語を扱うのはいろいろと面倒です。
とりあえず、いろいろ試行錯誤してるうちに大体のエラーは防げるようになりました。

以下、シフトJIS(sjis)の例を挙げてみます。
sjisのところを他の文字コードに変えれば他のでも大丈夫なはず。

ソースコード内に日本語を記述するときには
#-*- Coding: sjis -*-
(sjisのところはファイルの文字コード)を入れる。

ソースコード内の日本語は
u"日本語"の形で書く。
これにより、python内部でのコード体系のunicodeに自動変換して処理してくれる。

・ファイルから日本語を読み出すときには、read().decode("sjis", "ignore")
とする。
これによりファイルをunicodeに変換できる。
"ignore"はおかしなデータ(そもそも元のデータにsjisじゃない文字が入ってたとか)を無視する。これを入れないとsjisとして認識できない文字が来るとエラーになる。
あと、"ignore"の代わりに"replace"にすると認識できない文字を?にしてくれる。

・ファイルに日本語を書き出すときには、
write((日本語が格納された変数).encode("sjis", "ignore"))
とする。
これにより、python内部でunicodeとして扱われていた日本語がsjisの形で書き出せる。


しかし、今日久しぶりに日本語に悩まされました。
エラーが出たのは以下のコード

print "%s%s" %(str1, str2)
str1, str2には日本語が入っています。
コマンドプロンプト文字コードはcp932(ほとんどsjisと同じもの)なので、普通はsjisに変換してprintしてくれます。

これで出たエラーが
UnicodeEncodeError: 'cp932' codec can't encode character u'\u25de' in position 20: illegal multibyte sequence

調べてみたところ、str2にsjisで表記できない文字が入ってたみたい。
じゃあencodeで強制的に変換すればいいのでは?と思ってやってみたら、

print "%s%s" %(str1, str2.encode("sjis", "ignore"))

UnicodeDecodeError: 'ascii' codec can't decode byte 0x81 in position 9: ordinal not in range(128)

今度はdecode(sjis->unicode)できないって言われました。なんで?
ここで不思議だったのが、

print str2.encode("sjis", "ignore")
print "%s" %str2.encode("sjis", "ignore")

だとエラーが出ないことです。

で、いろいろ調べてみて分かったのは、

format が Unicode オブジェクトであるか、または %s 変換を使って Unicode オブジェクトが変換される場合、その結果も Unicode オブジェクトになります。
http://docs.python.jp/2.6/library/stdtypes.html#index-24

今まで書きませんでしたが、pythonの文字列にはstr型とunicode型があります。
str型は英数字、sjisなどにencodeされたもので、unicode型はdecodeされたものでu""で表されるものです。
それで、
print "%s%s" %(str1, str2.encode("sjis", "ignore"))
と書くと、

  1. str2がsjisにencodeされてstr型になる
  2. "%s%s"で文字列が連結されるが、str1の方はunicode型なので連結された結果unicode型になる。つまり、str2が再びdecodeされてunicode型に戻され、str1と連結される。
  3. printによって、sjisにencodeされてコマンドプロンプトに出力される。

という過程を経て出力されます。
2.のdecodeでは、文字列はascii文字列と解釈されるため、sjisとかだとエラーが発生します。今回の場合もここでエラーが発生していたようです。

とまあいろいろ書いたけれど、解決策としては

・そもそも文字列フォーマット操作をやめる。
・str1もencodeして、
print "%s%s" %(str1.encode("sjis", "ignore"), str2.encode("sjis", "ignore"))
とする。

あたりでしょうか。

10/27追記
後になって思ったけど
print "%s%s".encode("sjis", "ignore") %(str1, str2)
が一番自然ですね。

11/7追記
10/27の方法では、sjisエンコードされたものにstr1、str2を埋め込んでいるためエラーになってしまうみたいでした。
正しくは
print ("%s%s" %(str1, str2)).encode("sjis", "ignore")
でした。