ACER AspireOne 753購入

去年はASUSだったので、今年はACERということで、49800円のときに2GBのDDR3メモリとともに AO753をamazonで購入しました。

BUFFALO ノート用増設メモリ PC3-8500(DDR3-1066) 2GB D3N1066-2G/E

BUFFALO ノート用増設メモリ PC3-8500(DDR3-1066) 2GB D3N1066-2G/E

届いて開封してみると、メモリスロットカバーのネジに保障封印がしてありました。ちょっと迷ったけど、保障なんて受けたことないしいいやと思い封印を破りメモリ増設しました。そのカバーには、Windowsのライセンスシールの中央の上あたりに爪もついているので、ネジを緩めるだけでははがれず、つめをはずすのに苦労しました。

メモリ増設後のパフォーマンス エクスペリエンスインデックスは以下のとおりです。

1世代前であるCeleron U2300機で、オーバークロック済みのUL20AのパフォーマンスインデックスはASUS UL20A届いた - ラシウラにあるとおりですが、ベースとしてみればCeleron U3400機は、旧アーキテクチャにくらべ全体的にパフォーマンスがあがっているようです。

ASUSとの比較をすると、ACERよりもASUSのほうが評価は上です。というのもメモリ増設の封印してあるのは、はっきりいってほかの評価をどうでもよくするほどのマイナス評価です。ASUSはほかにもオーバークロックできたりするのもプラスです。逆にACERBIOSはほとんど何もできません。

つくりも若干安っぽく、ACアダプタは立体的だし、キーボードはぺこぺこさがあるし、液晶背面も貧弱で背面でも指でつかむと画面がすぐへこみますし、スピーカーもUL20Aのほうが音がよいです。とはいえ、これはASUSのが値段のわりにすばらしいのだと思ったほうがいいでしょう。あと無駄なプレインストール体験版もASUSにくらべ多いです。

一方、ACER機のいい面は小ささと軽さ、キーボードのPageUp PageDownが独立キーになっている、タッチパネルの左右クリックボタンが分離している点(押すのに力が要らない、同時押しがしやすい)、運搬用ケースがついてる、というデザイン面でしょうでしょうか。


今見るとAmazonでの値段も値上がりしたようなので、ASUSのUL20FTのほうがいいかもしれません。

ASUS UL20FT 12.1型ワイド ノートPC Office無 Windows7搭載 シルバー UL20FT-2X034V

ASUS UL20FT 12.1型ワイド ノートPC Office無 Windows7搭載 シルバー UL20FT-2X034V

Python 2.6以降で作ったコードを 2.5へ対応するときに引っかかる点

python-specfor · PyPI を作るとき、Python2.6と3.1を中心に書いてました。
これを2.5にも対応させたのですが、そのときその差が意外と大きかったのが新鮮だったのでメモ。

with文

2.5では、with文を有効にするには、そのファイルで__future__指定する必要があります。

from __future__ import with_statement

これは今回の中で一番分かりやすかった違いですが、specfor自体がwith文を使わせるものであるため、そこそこ多くのファイルに埋めこむことになりました。

namedtuple

collections.namedtupleも2.6からの機能です。
自らはnamedtuple型を定義しなかったのですが、標準モジュールのほうで結構使っていて、それに依存したコードがありました。

対策A: 代入時tuple展開
# difflib.SequenceMatcher.get_matching_blocks()の戻り値 macthesにて
for match in matches:
    vindex = match.a + match.size

を以下のようにする

# difflib.SequenceMatcher.get_matching_blocks()の戻り値 macthesにて
for a, b, size in matches:
    vindex = a + size
対策B: classを作る

matchはループ中でのみ使うものだったので、展開するのが楽です。
しかし、tupleを持ち運んで使っている場合もあって、その場合には、同じインタフェースを持ったclassを作成しました。

class funcspec(object):
    def __init__(self, argspec):
        self.args, self.varargs, self.keywords, self.defaults = argspec
        pass
    pass

def getargspec(func):
    import inspect
    return funcspec(inspect.getargspec(func))

argspecがnamedtupleですが、それをfuncspecクラスで同じ名前のメンバーとなるよう調整しています。

相対import

python2.5は2.6以降にくらべ、相対importの能力が低いです。

from .framework import * # 2.5ではここで*は使えない。
# 2.5の場合、__main__モジュールでは、-mオプション経由の起動でも、相対importが使えない
from . import version

def main():
    ...

if __name__ == "__main__": main()

これは、以下のように対策しました

from .framework import spec # 個別の名前で指定する
# main内で、絶対importを使う
def main():
    from specfor import version
    ...

if __name__ == "__main__": main()

python3用とpython2用のmultipleなdebパッケージを作るubuntuパッケージの作り方

debパッケージの作るのにはdebianディレクトリ以下の記述ファイルが多く必要で、その管理のためにコマンドやツールを多用したりするため、(rpmのspec作成がコピペから始められるのに比べると、)最初のものを用意するまでに多くの知識を得る必要があります。
そして、pythonパッケージを作る、python2系だけじゃなくpython3系でも使えるようにするパッケージの作るための情報というのは特に少なく、他の構成でのやり方をいろいろ試していきながらうまくいく方法を得ました。
この記事では、python-helloソースから、python2系用のpython-helloパッケージと、python3系用のpython3-helloパッケージを同時に作成することを想定したもので、応用可能な方法を記述していきます。

パッケージ対象

debの元となるパッケージは、python2でもpython3でも、同一のsetup.pyでbuild/installできるpythonパッケージである必要があります。

こうになっていればpure pythonモジュールでも、dllモジュールでも基本は同じやり方でdebパッケージ化できます。

例題: python-helloのdebパッケージ化

以下のような簡単な3ファイルだけで構成されたpythonパッケージを例に、debの作り方を説明します。

def world():
    return "Hello World!"
from distutils.core import setup

setup(
    name="hello",
    version="1.0.0",
    py_modules=["hello"],
    )
module example: hello

dh_makeによるdebianディレクトリ雛形作成

まずディレクトリ名をpackagename-versionとなるように変更し、その中でdh_makeします。

mv python-hello python-hello-1.0.0
cd  python-hello-1.0.0
dh_make --createorig -b -c bsd
rm debian/*.ex debian/*.EX
rm debian/docs
rm debian/README.source

(以下、このカレントディレクトリから、すべてコマンドを実行していきます)

man dh_makeで出てくるオプションをつけることで、例えば"-c bsd"とすることでライセンス情報なども含んだ雛形が作れます。
dh_makeをすると、いっぱいファイルが出来ますが、生成されるファイルのうち*.exや*.EXは追加処理用のテンプレートであり、不要なので全部消します。docsファイルもmultipleパッケージでは、バイナリパッケージごとに用意することになるので消します。
また、debian/README.sourceも特に書く事がないのであれば消します。すると、以下のファイルだけが残ることになります。

以下これらのファイルが適切なものになるよう編集していくことになります。

まず、debian/README.Debiandebian/copyrightを適切に編集します。"<"と">"で括られた箇所は何か書けと書いてある部分であり、そこを埋めるか削るかします。

comaptファイルは、このdebianディレクトリ下ファイルの仕様バージョン(debhelperのバージョン)です。ふつうそのままでOKです。

debian/changelog の編集

changelogファイルは生成バイナリパッケージのバージョン情報となるので、適切に編集していく必要があります。
changelogを編集するためのコマンドdchがあるので、それを使うのがいいでしょう。

最初のdebian/changelogを編集します。

dch -v 1.0.0-1nmu1

このコマンドでは、雛形が埋められてエディタが立ち上がります。そのまま編集してから、保存終了させます。changelogエントリは、たとえば以下のように書きます。

python-hello (1.0.0-1nmu1) lucid; urgency=low

  * packaging for ubuntu

 -- bellbind <bellbind@gmail.com>  Wed, 28 Jul 2010 23:58:57 +0900

"nmu"とはNone Maintainers Updateの略で、野良パッケージではこれをパッケージバージョンに入れておけば、チェックでいろいろ警告がでるのを抑えることができます。

debian/rules の編集

debian/rulesは、本来ビルドやインストールの方法を書くファイルです。

標準では以下の3行にします。

#!/usr/bin/make -f
%:
	dh $@


pythonパッケージで、setup.pyが使える場合、debian/rulesは以下の五行にしておけば良いです。

#!/usr/bin/make -f
DEB_PYTHON_SYSTEM=pysupport
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk
DEB_COMPRESS_EXCLUDE := .py

debian/control の編集

このファイルには、ソースパッケージとバイナリパッケージの説明や依存関係等の情報を記述します。
このソースパッケージの情報をもとにビルドし、インストール時は、その下のバイナリパッケージの情報に基づいて管理されます。

python-helloのcontrolファイルの全体を書いておきます。
このファイルだけは、パッケージ化するpythonモジュールがpure pythonだけか、native moduleを含むかで若干記述が変わるので、両方載せておきます。

pure python module用 debian/control
Source: python-hello
Section: python
Priority: extra
Maintainer: bellbind <bellbind@gmail.com>
Build-Depends: debhelper (>= 7),  python-support,
 python-setuptools, python3-setuptools
Standards-Version: 3.8.4
XS-Python-Version: 2.6, 3.1
Homepage: http://example.com/python-hello

Package: python-hello
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, python
Description: Example python module pakage
 Usage:
 .
      import hello
      print(hello.world())
 .

Package: python3-hello
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, python3
Description: Example python module pakage
 Usage:
 .
      import hello
      print(hello.world())
 .

重要なポイントは、XS-Python-Versionにpython2とpython3の両方のバージョンを書くことです。Build-DependsとDependsも適切なものを指定する必要があります。

native module用debian/control
Source: python-hello
Section: python
Priority: extra
Maintainer: bellbind <bellbind@gmail.com>
Build-Depends: debhelper (>= 7),  python-support,
 python2.6-dev, python3.1-dev, python-setuptools, python3-setuptools
Standards-Version: 3.8.4
XS-Python-Version: 2.6, 3.1
Homepage: http://example.com/python-hello

Package: python2.6-hello
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python2.6
Description: Example python module pakage
 Usage:
 .
      import hello
      print(hello.world())
 .

Package: python3.1-hello
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python3.1
Description: Example python module pakage
 Usage:
 .
      import hello
      print(hello.world())
 .

pure python版との違いは、以下3点だけです(debian/rules, .install等も変更不要です)。

  • バイナリパッケージをPythonのバイナリバージョンごとに用意する(python2系でもpython2.6とpython2.7で別パッケージにする必要がある)
  • Archtecture: allを Archtecture: any にする(もしくは特定のアーキテクチャ名をリストアップ)
  • Build-Depends に python2.6-dev, python3.1-devを入れる


2.6系と3.1系バイナリでメジャーパッケージのProvidesを設定してもよいでしょう。

Package: python2.6-hello
Provides: python-hello
...

将来、2.7系がpythonのメジャーパッケージに変わったらReplacesを設定して置き換えることになります。たとえば、1.0.1からpython2.7系をpython-helloのメインにする場合以下のようになります。

Package: python2.7-hello
Provides: python-hello
Replaces: python-hello (<< 1.0.1)
...

追加ファイル: packagename.install

マルチバイナリパッケージの場合、debian/tmp以下にinstall後のファイル構造が作られます。
その中にあるファイルを各バイナリパッケージに振り分ける必要があります。
それを指定するのが、*.installファイルです。ディレクトリトップからのファイルglobパターンで記述します。

この例では、バイナリパッケージは、python-helloとpython3-helloがあるので、

debian/tmp/usr/lib/python2.6/*
  • debian/python3-hello.install
debian/tmp/usr/lib/python3.1/*

を用意します。

追加ファイル: packagename.docs

同様に、パッケージの追加ドキュメントになるファイルは、*.docsファイルで指定します。

README.txt
README.txt

確認

ここまでできていれば、ファイル構成は以下のようになっているはずです。

パッケージビルド

この記述でバイナリパッケージを生成できるかチェックします。

debuild -uc -us

作成時、多くのlintianのエラーや警告が出ますが、上のディレクトリにはpython-helloとpython3-helloの二つのdebファイルが生成できているはずです。debファイルの生成確認を持って、ここで終わりとします。launchpadのppa等に公開するためには、このlintianエラーや警告が消えるように頑張っていくことになります。

応用

  • binaryモジュールは、必要なライブラリをcontrolに書く必要があります。
  • /etc等どっちのモジュールでも使うものはpackagename-commonパッケージをつくるようにし、それを各バイナリパッケージのDependsに入れておけばいいでしょう。
  • ソースがpython3とpython2でsetupファイルが別の場合、sys.versionでimportするsetupファイルを切り替えるsetup.pyを用意します。
  • debian/rulesのpysupportでだめなら、pycentralを使う

おまけ: debファイル中のファイルリスト一覧を取り出すには

dpkg-deb -c ../python-hello_1.0.0-1nmu1_i386.deb

おまけ: lintianメッセージを詳細情報つきで出すには

lintian -i ../python-hello_1.0.0-1nmu1_i386.changes | lv

トラブルシューティング

  • ログで「XS-Python-Versionはdeprecateで、debian/pyversionを使え」のようなメッセージが出るが、lucidではこのやり方ではmulti packageではエラーになってしまい、debian/pyversionsは使えなかった
  • python3パッケージのモジュールが、debian系でsys.pathが通っているdist-packagesではなく、site-packages下に入る。python2では同じ記述でpyshared下に入るので、まだpython-support/python-centralがpython3向けにうまく機能していないのだろう。これはsys.pathにsite-packagesへのPATHを追加すれば使うことはできるが...
    • debian/rulesを(cdbsではなく)dhで書く

emacsのpython-modeで、TABキーでのインデント位置を固定する方法

自分はキーボタンにモードがあるのが嫌いです。たとえばIMEではトグルは使わず、macのように変換/無変換キーにON/OFFを割り当てて使っています。これによってキーを何度押してもASCIIはASCIIだし、ひらがなはひらがなになります。*1

emacspython-modeでは、同じ位置でTAB keyを複数回押すと、インデントが下がる(outdent)ようになっていて、これが非常にいらつかせます。

そこで、以下のようなフックを.emacsで入れることで、cやrubyなどの言語モードと同じように、python-modeでも何回TABを押してもインデント位置が変わらないようにしています。

(add-hook 'python-mode-hook
          '(lambda()
             (defun my-indent-line (&optional arg)
               "modeless indent for python indentation"
               (interactive "P")
               (let ((old-this-command this-command))
                 (setq this-command t)
                 (py-indent-line arg)
                 (setq this-command old-this-command)
                 ))
             (setq indent-line-function 'my-indent-line)
             ))

ちなみに、インデントを戻すときはブロックの最後に必ず pass や return を入れることになります。*2

emacs23 progmodes/python.elの場合

コマンド名や引数がpython-mode.elから若干変わっていますが、やり方は一緒です。

(add-hook
 'python-mode-hook
 '(lambda ()
    (defun my-indent-line ()
      "modeless indent for python indentation"
      (interactive "P")
      (let ((old-this-command this-command))
        (setq this-command t)
        (python-indent-line)
        (setq this-command old-this-command)
        ))
    (setq indent-line-function 'my-indent-line)
    ))

python-mode.elとprogmodes/python.el両方に対応するフック

(add-hook
 'python-mode-hook
 '(lambda ()
    (defun my-indent-line (&optional arg)
      "modeless indent for python indentation"
      (interactive "P")
      (let ((old-this-command this-command))
        (setq this-command t)
        (cond
         ((fboundp #'python-indent-line) (funcall #'python-indent-line))
         ((fboundp #'py-indent-line) (funcall #'py-indent-line arg))
         )
        (setq this-command old-this-command)
        ))
    (setq indent-line-function 'my-indent-line)
    ))

*1:つまりはドナルド・ノーマンの言うmodeless、またはRESTの冪等性(idenpotent)のメリットをWebだとか以前に実感しているわけですわ

*2:py-indent-lineを別キーにでも割り当てるのでもいいだろうけど。

Cythonを使ってみる

python-simplexqueryでは、C&C++pythonのextensionを書いたけど、これ以上ベタでpython層変換を書くのはきついので、このあたりをサポートするツールを試すことにし、まずcythonを使ってみました。

cythonはpyxというpython言語の拡張言語(pyrex)のソースファイルを、cファイルに変換するpythonモジュールです。distutilsのコマンド拡張も入っているので、setup.pyに.cや.cpp同様直接.pyxを混ぜて入れることができ、cコードに変換後にコンパイルさせることも可能です。

setup.py

from distutils.core import setup, Extension
from Cython.Distutils import build_ext

setup(
  name = 'hello',
  cmdclass = {'build_ext': build_ext},
  ext_modules = [Extension("hello", ["chello.c", "hello.pyx"])]
)

ミソは、build_extをCythonのものに指定すること

chello.c

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <string.h>
extern void * (* get_malloc(void))(size_t);

extern char *
say_hello(const char * name)
{
  char * hello = "Hello";
  int hellolen = strlen(hello);
  int namelen = strlen(name);
  int buflen = hellolen + 1 + namelen + 1;
  char * buf = get_malloc()(buflen);
  sprintf(buf, "%s %s", hello, name);
  return buf;
}

mallocpythonのものを使うことにします。ここでは、それを外で定義させるため、関数get_malloc()経由で取得させました。最初のdefineはcl.exeの警告を消すためのおまじない。

hello.pyx

cdef extern from "Python.h":
    void * PyMem_Malloc(size_t)
    void PyMem_Free(void *)
    pass

#cdef extern char * say_hello(char *)
# or
cdef extern:
    char * say_hello(char *)
    pass

ctypedef void * malloc_t(size_t)
cdef extern malloc_t * get_malloc():
    return PyMem_Malloc

# function returned func cdef requires arg name of its func type
cdef extern void (* get_free())(void * ptr):
    return &PyMem_Free

def say_hello_to(name):
    # convert py to c
    # cannot mix py exp in cdef exp without pyvars
    bname = name.encode("utf-8")
    cdef char * cname = bname

    cdef char * buf = say_hello(cname)
    # convert c to py
    ret = buf

    PyMem_Free(buf)
    return ret.decode("utf-8")

いくつかポイント

  • pyxはcにソースtoソースで変換される。
  • 型定義では、constや引数なし指定voidは書かない
  • typedefしないで関数型を返す関数の宣言部で、関数型に引数名が必要だったりする(型だけだとそれがobject型の変数とみなされる)。ctypedefではいらない
  • cdef文中でpythonの式は書けない。pythonの変数だけは使える
  • var = cvarでは、python objectが生成される。
  • ちなみにprintfのextern記述は、'cdef extern from "stdio.h": int printf(char *, ...)'

実行

普通のと同じで、python setup.py build でビルドできます。できたモジュールはcythonライブラリを必要としません。

python3対応

cythonの生成cファイルは、python2でもpython3でも対応している。

ただし、Cython-0.12.1のCython/Compiler/Main.pyがpython3非互換なのでsetup.py build時に修正する必要がある。

  • build時のエラー部分をprint(msg)、raise Excep(msg)、except Excep as ex: などにする程度。2to3でいける?

twitter記法をつかってみる

久々にはてなダイアリーに書いてみました。


このtwitter記法だけど、最初URLしかでなくて戸惑った。リンクが じゃだめで、 だといいようだ。シビアな。


この記事は、Google Chrome拡張のcrxパッケージ作成をするコマンドをpythonで書くことで学んだ、crxの仕組みやそこで使ってるRSA/OpenSSLの使い方への感想です。

そもそも記事風にtwitterを使うのもいまいちなじめない。。。書いたあとも文面をなんども書き直すタイプだし、コードも書けないしなあ。

暫定、完成版

(未完成)やってみた。麻雀の上がりチェックプログラム - ラシウラから、ちょっと修正して要件を満たしたものです。

# -*- coding: utf-8 -*-

class Hai(object):
    def __init__(self, num, tsumo=False):
        self.num = num
        self.tsumo = tsumo
        pass
    def __eq__(self, other):
        return self.num == other.num and self.tsumo == other.tsumo
    pass

class Tenpai(object):
    def __init__(self, toitsu, sumentsu):
        self.groups = [toitsu] + sorted(sumentsu, self.rimentsu)
        pass
    def __hash__(self):
        return hash(str(self.group))
    def __eq__(self, other):
        return self.groups == other.groups
    def rimentsu(self, mentsua, mentsub):
        if is_juntsu(mentsua): return 1
        if is_juntsu(mentsub): return -1
        return 1
    def format(self):
        return "".join(self.format_group(group) for group in self.groups)
    def format_group(self, group):
        if any(hai.tsumo for hai in group):
            return "[%s]" % "".join(str(hai.num) for hai in group if not hai.tsumo)
        else:
            return "(%s)" % "".join(str(hai.num) for hai in group)
        pass
    pass

def main():
    while True:
        try:
            tehai_str = raw_input("haipai> ").strip()
            if not tehai_str: break
            tehai = parse(tehai_str)
            for tenpai in iter_tenpai(tehai):
                print(tenpai)
                pass
            pass
        except:
            import traceback
            traceback.print_exc()
            pass
        pass
    pass

def parse(tehai):
    if len(tehai) != 13: raise Exception("tehai should be 13 hai")
    return [Hai(int(tehai[i])) for i in range(len(tehai))]

def iter_tenpai(tehai):
    tsumo_list = [Hai(i, True) for i in range(1, 10)]
    # TBD: check: count of same num <= 4
    tenpaiset = set()
    for tsumo in tsumo_list:
        tsumotehai = rihai(tehai + [tsumo])
        for toitsu, rest in iter_toitsu(tsumotehai):
            for sumentsu in iter_allmentsu(rest):
                tenpai = Tenpai(toitsu, sumentsu).format()
                if tenpai in tenpaiset: continue
                tenpaiset.add(tenpai)
                yield tenpai
                pass
            pass
        pass
    pass

def rihai(tsumo_and_tehai):
    return sorted(tsumo_and_tehai, lambda a, b: cmp(a.num, b.num))

def iter_toitsu(ritsumo):
    same = []
    for i in range(len(ritsumo) - 1):
        if ritsumo[i].num == ritsumo[i+1].num:
            toitsu = ritsumo[i:i+2]
            rest = ritsumo[:i] + ritsumo[i+2:]
            if toitsu in same: continue
            same.append(toitsu)
            yield (toitsu, rest)
            pass
        pass
    pass

def is_juntsu(mentsu):
    return (mentsu[0].num + 1 == mentsu[1].num and 
            mentsu[0].num + 2 == mentsu[2].num)
def is_kotsu(mentsu):
    return mentsu[0].num == mentsu[1].num == mentsu[2].num
def is_mentsu(mentsu):
    return is_juntsu(mentsu) or is_kotsu(mentsu)

def pick1(hailist):
    for i in range(len(hailist)):
        yield (hailist[i], hailist[:i] + hailist[i+1:])
        pass
    pass

def iter_allmentsu(rest):
    if len(rest) == 0:
        yield []
        return
    a = rest[0]
    r1 = rest[1:]
    same = []
    for b, r2 in pick1(r1):
        for c, r3 in pick1(r2):
            mentsu = [a, b, c]
            if mentsu in same: continue
            same.append(mentsu)
            if not is_mentsu(mentsu): continue
            for allmentsu in iter_allmentsu(r3):
                yield [mentsu] + allmentsu
                pass
            pass
        pass
    pass

if __name__ == "__main__": main()

追記: ドメイン知識を利用する

上の四面子の探し方(iter_allmentsu)は、先頭の牌に対し全ての三牌の組を列挙してそれが順子か刻子かを判定していく方式をとってます。これは細かい性質を知らなくても問題文からすぐ割り出せるやり方です。

しかし麻雀のルールから考えれば、先頭の牌に対し刻子となる組み合わせ、順子となる組み合わせを探していく方が、効率が良いと思います。なぜなら探索範囲の限定がしやすいから。整牌済なら、刻子なら数値が違う牌が出た時点で失敗となり、順子なら一つ上二つ上の牌それぞれがない時点で失敗とわかるからです。それが下記のコードです:

def iter_allmentsu(hai_list):
    if len(hai_list) == 0:
        yield []
        return
    juntsu, rest = find_juntsu(hai_list)
    if juntsu:
        for allmentsu in iter_allmentsu(rest):
            yield [juntsu] + allmentsu
            pass
        pass
    kotsu, rest = find_kotsu(hai_list)
    if kotsu:
        for allmentsu in iter_allmentsu(rest):
            yield [kotsu] + allmentsu
            pass
        pass
    pass

def find_kotsu(hai_list):
    if (hai_list[0].num == hai_list[1].num and 
        hai_list[0].num == hai_list[2].num):
        return hai_list[:3], hai_list[3:]
    return [], hai_list

def find_juntsu(hai_list):
    juntsu = [hai_list[0], None, None]
    rest = []
    for hai in hai_list[1:]:
        if juntsu[1] is None and hai.num == juntsu[0].num + 1: 
            juntsu[1] = hai
            pass
        elif juntsu[2] is None and hai.num == juntsu[0].num + 2: 
            juntsu[2] = hai
            pass
        else: 
            rest.append(hai)
            pass
        pass
    return (juntsu, rest) if juntsu[1] and juntsu[2] else ([], hai_list)