2012年2月7日火曜日

Erlang AVL Tree まだ途中


コンパイルは通した。
deleteのユニットテストがまだ。
それにしても、ブログに書くと、見難い。

◆ソース

-module( avl_tree ).


-ifdef( debug ).
-compile( export_all ).
-else.
-export( [  lookup/2,
            insert/3,
            update/3,
            delete/2 ] ).
-endif.


-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.




% tree()
-define( undef, undefined ).
-define( bal, balanced ).
%% tree: { tree, key, value, 左部分木, 右部分木, バランス状態 }
%%
%% バランス状態    |        意味
%% ================|==============================================
%% left            | 左部分木が1つだけ高い
%% right           | 右部分木が1つだけ高い
%% balanced        | 左と右の部分木のバランス状態が均衡している


-record( tree, { key=?undef, value=?undef, smaller=?undef, bigger=?undef, dir=?bal } ).
%% @spec delete( Key, tree() ) -> tree() | undefined.




delete( Key, Tree ) when is_record( Tree, tree) ->
    delete_1( Key, Tree );
delete( _, _ ) ->
        undefined.


delete_1( Key, Tree ) when Key < Tree#tree.key ->
        Tree1 = delete_1( Key, Tree#tree.smaller ),
        Tree#tree{ smaller=Tree1 };


delete_1( Key, Tree ) when Key > Tree#tree.key ->
        Tree1 = delete_1( Key, Tree#tree.bigger ),
        Tree#tree{ bigger=Tree1 };


delete_1( _, Tree ) ->
        take_max( Tree ).


%% balance_del( Dir, tree() ) -> { shrinked, tree() } | { unchanged, tree() } | undefined


%% 左部分木にあるノードが削除されると仮定する( Dir = left )
%% A:tree() の頂点ノード
%% B:Aの右側(bigger)のノード
%%              Aの状態                 |
%%      ================================|       木の高さ    |
%%          前      |    後             |       前→後      |   回転
%%      ============|===================|===================|==========
%%      balanced    | right             |  不変             | しない                    --- ①
%%      left        | balanced          |  減る             | しない                    --- ②
%%      right       | BやCの状態による  |  Bの状態による    | する(Bの状態による)       --- ③


%%      Bの状態  |   回転
%%     =====================
%%      right    |   1重            --- ④
%%      left     |   2重            --- ⑤
%%      balanced |   1重            --- ⑥


%% 1重回転                  
%% Bの状態(前)  | Aの状態(後)   Bの状態(後)     高さ
%% =============|====================================
%% right        | right         left            不変            --- ⑦
%% left         | right         left            不変            --- ⑧
%% balanced     | balanced      balanced        減る            --- ⑨


%% 2重回転
%% Cの状態(前)  | Aの状態(後)     Bの状態(後)     Cの状態(後)     高さ
%% =============|========================================================
%% right        | left            balanced        balanced        減る
%% left         | balanced        right           balanced        減る
%% balanced     | balanced        balanced        balanced        減る


%% tree() が定義されていない場合
balance_del( _, undefined ) ->
        undefined;


%% ツリーの頂点がバランス(balanced)状態         --- ①
%% 高さ減らない、木の高さは右が+1
balance_del( left, A ) when A#tree.dir =:= balanced ->
    { unchanged, A#tree{ dir=right } };


%% 高さ減らない、木の高さは左が+1
balance_del( right, A ) when A#tree.dir =:= balanced ->
    { unchanged, A#tree{ dir=left } };


%% ツリーの頂点のバランス状態 = 頂点から見て、ノード削除対象の方向      --- ②
%% 高さが減る(shrinked)、木の高さは左右同じ
balance_del( Dir, A ) when A#tree.dir =:= Dir ->
    { shrinked, A#tree{ dir=balanced } };


%% ツリーの頂点のバランス状態 ≠ 頂点から見て、ノード削除対象の方向     --- ③
balance_del( Dir, A ) ->
    balance_del_B( Dir, A ).


%% balance_del_B( Dir, A ) -> { shrinked, tree() } | { unchanged, tree() }
%% Dir:ノード削除対象の方向
%% A:tree()
balance_del_B( Dir, A ) ->
    %% 
    case Dir =:= left of
        true    -> B = A#tree.smaller;
        false   -> B = A#tree.bigger
    end,
    %% Bのバランス状態とノード削除対象の方向が違う
    case B#tree.dir =/= Dir of
        true    ->  rolling_1( Dir, A );       %% --- ④、⑥
        false   ->  rolling_2( Dir, A )       %% --- ⑤
    end.


%% rolling_1( Dir, tree() ) -> { shrinked, tree() } | { unchanged, tree() }
%% 1重回転
-define( ROLLING_1( X, Y, L, R),
                     ( begin
                        B       = A#tree.X,
                        Alpha   = B#tree.Y,
                        case B#tree.dir of
                            R           -> { unchanged, B#tree{ Y=A#tree{ X=Alpha, dir=R },         dir=L } };
                            L           -> { unchanged, B#tree{ Y=A#tree{ X=Alpha, dir=R },         dir=L } };
                            balanced    -> { shrinked,  B#tree{ Y=A#tree{ X=Alpha, dir=balanced },  dir=balanced } }
                        end
                     end ) ).


rolling_1( left, A ) ->
    ?ROLLING_1( smaller, bigger, left, right );
rolling_1( right, A ) ->
    ?ROLLING_1( bigger, smaller, right, left ).


%% rolling_2
%% 2重回転
-define( rolling_2( X, Y, L, R ),
                 ( begin
                        B = A#tree.X,
                        C = B#tree.Y,
                        Beta1 = C#tree.X,
                        Beta2 = C#tree.Y,
                        case C#tree.dir of
                            R  ->       { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=balanced },
                                                            Y=A#tree{ X=Beta2, dir=L },
                                                            dir=balanced } };
                            L ->        { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=R },
                                                            Y=A#tree{ X=Beta2, dir=balanced },
                                                            dir=balanced } };
                            balanced -> { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=balanced },
                                                            Y=A#tree{ X=Beta2, dir=balanced },
                                                            dir=balanced } }
                        end
                    end 
                )
         ).


rolling_2( left, A ) ->
    ?rolling_2( smaller, bigger, left, right );
rolling_2( right, A ) ->
    ?rolling_2( bigger, smaller, right, left ).


%% @spec take_max( tree() ) -> { Key, Value, tree() } | undefined.
%% Key,Value = tree()内の最大ノード
%% tree() = 取り出した後のツリー
take_max( Tree ) when is_record( Tree, tree ) ->
        take_max_1( Tree );
take_max( _ ) ->
        undefined.


take_max_1( #tree{ key=Key, value=Value, smaller=Smaller, bigger=undefined } )  ->
        { Key, Value, Smaller };
take_max_1( Tree ) ->
        { Key1, Value1, Smaller1 } = take_max_1( Tree#tree.bigger ),
        { Key1, Value1, Tree#tree{ smaller=Smaller1 } }.


%% procCase( X = smaller or bigger, Y = direction = left or right or balanced )
-define( procCase( X, Y ),
        ( begin
               case is_record( SubTree1, tree ) of
                       false->
                                ?print( "tree(false):~p",[ Tree ] ),
                                Tree;
                       true ->
                                Tree1 = Tree#tree{ X=SubTree1 },
                                ?print( "tree(true):~p",[ Tree1 ] ),
                                balance( Y, Tree1 )
               end
          end )
        ).


%% @spec update( Key, Value, Tree ) -> tree()
%% Tree内にKeyが存在する場合、Valueを更新する.
%% Tree内にKeyが存在しない場合、Key/Valueのノードを挿入する.
update( Key, Value, Tree ) ->
        update_1( #tree{ key=Key, value=Value }, Tree ).


update_1( Node, undefined ) ->
        Node;


update_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, SubTree1 );


update_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, SubTree1 );


update_1( Node, Tree ) ->
        Tree#tree{ value=Node#tree.value }.


%% @spec insert( Key, Value , Tree ) -> tree() | { key_exists, Value }
%% { key_exists, Value } = already exist.


insert( Key, Value, Tree ) ->
        ?print(" Start.", [] ),
        insert_1( #tree{ key=Key, value=Value }, Tree ).


insert_1( Node, undefined ) ->
        ?print(" Start.", [] ),
        Node;


insert_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, left );


insert_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, right );


insert_1( _, Tree ) ->
        ?print(" Start.", [] ),
        ?print( "tree:~p",[ { key_exists, Tree#tree.value } ] ),
        { key_exists, Tree#tree.value }.


%% This process use balance/1.
-define( __proc_bal() ,
                        ( begin
                            case Dir =:= SubDir of
                                true ->
                                    ?print( "Dir(true):~p",[ Dir ] ),
                                    balance_1( Tree, Dir );
                                false ->
                                    ?print( "Dir(false):~p",[ Dir ] ),
                                    balance_2( Tree, Dir )
                            end
                        end )
        ).


%% @spec balance( Dir, tree() ) -> tree()
%% Dir : ノードの挿入方向


%% 左部分木に挿入した(挿入方向:left)と仮定(右部分木の場合はleft⇔right)


%% Aの状態(前)  | Aの状態(後)         高さ
%% =============|=================================
%% right        | balanced            不変
%% left         | Bの状態による       Bの状態による
%% balanced     | left                増える


%% Bの状態(前)  | 回転    | Aの状態(後)     | Bの状態(後)
%% =============|=========|=================|=============
%% right        | 2重     | Cの状態による   | Cの状態による
%% left         | 1重     | balanced        | balanced
%% balanced     | 1重     | -               | -


%% Cの状態(前)  | Aの状態(後)     Bの状態(後)     Cの状態(後)
%% =============|===============================================
%% right        | balanced        left            balanced
%% left         | right           balanced        balanced
%% balanced     | balanced        balanced        balanced


%% ノードの挿入方向と木のバランス状態が同じ
balance( Dir, Tree ) when Dir =:= Tree#tree.dir ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    %% 子ノードのバランス状態取得
    case Dir of
        left ->
            SubDir = ( Tree#tree.smaller )#tree.dir,
            ?__proc_bal();
        right ->
            SubDir = ( Tree#tree.bigger )#tree.dir,
            ?__proc_bal();
        _ ->
            unexpect
    end;


%% 木のバランス状態が均衡している
balance( Dir, Tree ) when Tree#tree.dir =:= balanced ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    ?print("Output Tree:(~p).", [ Tree#tree{ dir=Dir } ] ),
    Tree#tree{ dir=Dir };


%% 木のバランス状態と挿入方向が違う
balance( Dir, Tree ) ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    Tree#tree{ dir=balanced }.


%% 1回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
-define( __proc_bal_1( X, Y ),
                        ( begin
                            ?print("Input ( X, Y ) = ( ~p, ~p ).", [ X, Y ] ),
                            Sub = Tree#tree.X,
                            SubBig = Sub#tree.Y,
                            Bigger = Tree#tree{ X=SubBig, dir=balanced },
                            ?print("Output:( ~p ) .", [ Sub#tree{ Y=Bigger, dir=balanced } ] ),
                            Sub#tree{ Y=Bigger, dir=balanced }
                        end )
        ).
%% 1回転(left)
%% Tree:回転させる木
%% left:Treeからのノードの挿入方向
balance_1( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( smaller, bigger );


%% 1回転(right)
%% Tree:回転させる木
%% right:Treeからのノードの挿入方向
balance_1( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( bigger, smaller ).


%% 2回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
%% Z = Treeからのノード挿入方向( left or right )
-define( __proc_bal_2( X, Y, Z ),
                                ( begin
                                    ?print("Input ( X, Y, Z ) = ( ~p, ~p, ~p ).", [ X, Y, Z ] ),
                                    Sub = Tree#tree.X,
                                    SubBig = Sub#tree.Y,
                                    case Tree#tree.dir of
                                        balanced ->
                                            ?print( "Output(balanced): (~p) ", [SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=balanced }, 
                                                    dir=Z };
                                        _ ->
                                            ?print( "Output(~p): (~p) ", [ Tree#tree.dir, SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced }
                                    end
                                end )
        ).
%% 2回転
%% @spec balance_2( tree(), Direction ) -> tree()
%% Direction : tree()からのノード挿入方向
balance_2( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( smaller, bigger, right );


balance_2( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( bigger, smaller, left ).






%% @spec lookup( Target, Tree ) -> undefined | { value, Value }
lookup ( Key, T ) ->
        lookup_1 ( Key, T ).


lookup_1 ( _, undefined ) ->
        ?print( "", [] ),
        undefined;


lookup_1 ( Key, Tree ) when Key < Tree#tree.key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.smaller );


lookup_1 ( Key, Tree ) when Tree#tree.key < Key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.bigger );


lookup_1 ( _, Tree ) ->
        ?print( "", [] ),
        { value, Tree#tree.value }.