PreviousUpNext

15.4.870  src/lib/src/binary-map-g.pkg

## binary-map-g.pkg
#
# Normally
#     src/lib/src/red-black-map-g.pkg
# is preferred.

# Compiled by:
#     src/lib/std/standard.lib

# This code was adapted from Stephen Adams' binary tree implementation
# of applicative integer sets.
#
#   Copyright 1992 Stephen Adams.
#
#    This software may be used freely provided that:
#      1. This copyright notice is attached to any copy, derived work,
#         or work including all or part of this software.
#      2. Any derived work must contain a prominent notice stating that
#         it has been altered from the original.
#
#
#   Name (s): Stephen Adams.
#   Department, Institution: Electronics & Computer Science,
#      University of Southampton
#   Address:  Electronics & Computer Science
#             University of Southampton
#            Southampton  SO9 5NH
#            Great Britian
#   E-mail:   sra@ecs.soton.ac.uk
#
#   Comments:
#
#     1.  The implementation is based on Binary search trees of Bounded
#         Balance, similar to Nievergelt & Reingold, SIAM J. Computing
#         2 (1), March 1973.  The main advantage of these trees is that
#         they keep the size of the tree in the node, giving a constant
#         time size operation.
#
#     2.  The bounded balance criterion is simpler than N&R's alpha.
#         Simply, one subtree must not have more than `weight' times as
#         many elements as the opposite subtree.  Rebalancing is
#         guaranteed to reinstate the criterion for weight>2.23, but
#         the occasional incorrect behaviour for weight=2 is not
#         detrimental to performance.
#

##      THIS DERIVED WORK HAS BEEN ALTERED FROM THE ORIGINAL

generic package binary_map_g (k:  Key )                 # Key   is from   src/lib/src/key.api
: (weak)
Map                                                     # Map   is from   src/lib/src/map.api
{
    package key = k;

    #
    #  weight = 3
    #  fun wt i = weight * i

    fun wt (i:  Int)
        =
        i + i + i;

    Map X
        = EMPTY 
        | TREE_NODE  {
            key:  k::Key, 
            value:  X, 
            count:  Int, 
            left:   Map(X), 
            right:  Map(X)
          };

    empty = EMPTY;

    fun is_empty EMPTY =>  TRUE;
        is_empty _     =>  FALSE;
    end;

    fun debug_print   (map, print_key, print_val) = 0;                  # Placeholder
    fun all_invariants_hold map = TRUE;                                 # Placeholder

    fun vals_count EMPTY => 0;
        vals_count (TREE_NODE { count, ... } ) => count;
    end;


    # Return the first item in the map
    # or NULL if it is empty:
    #
    fun first_val_else_null EMPTY                                    =>  NULL;
        first_val_else_null (TREE_NODE { value, left=>EMPTY, ... } ) =>  THE value;
        first_val_else_null (TREE_NODE { left, ... }               ) =>  first_val_else_null left;
    end;

    # Return the first item in the map
    # and its key, or NULL if it is empty:
    #
    fun first_keyval_else_null EMPTY                                         =>  NULL;
        first_keyval_else_null (TREE_NODE { key, value, left=>EMPTY, ... } ) =>  THE (key, value);
        first_keyval_else_null (TREE_NODE { left, ... }                    ) =>  first_keyval_else_null left;
    end;


    # Return the last item in the map
    # or NULL if it is empty:
    #
    fun last_val_else_null EMPTY                                     =>  NULL;
        last_val_else_null (TREE_NODE { value, right=>EMPTY, ... } ) =>  THE value;
        last_val_else_null (TREE_NODE { right, ... }               ) =>  last_val_else_null right;
    end;

    # Return the last item in the map
    # and its key, or NULL if it is empty:
    #
    fun last_keyval_else_null EMPTY                                          =>  NULL;
        last_keyval_else_null (TREE_NODE { key, value, right=>EMPTY, ... } ) =>  THE (key, value);
        last_keyval_else_null (TREE_NODE { right, ... }                    ) =>  last_keyval_else_null right;
    end;



    stipulate

        fun node_count (k, v, EMPTY, EMPTY)            => TREE_NODE { key=>k, value=>v, count=>1, left=>EMPTY, right=>EMPTY };
            node_count (k, v, EMPTY, r as TREE_NODE n) => TREE_NODE { key=>k, value=>v, count=>1+n.count, left=>EMPTY, right=>r };
            node_count (k, v, l as TREE_NODE n, EMPTY) => TREE_NODE { key=>k, value=>v, count=>1+n.count, left=>l, right=>EMPTY };

            node_count (k, v, l as TREE_NODE n, r as TREE_NODE n')
                => 
                TREE_NODE { key=>k, value=>v, count=>1+n.count+n'.count, left=>l, right=>r };
        end;


        fun single_l (a, av, x, TREE_NODE { key=>b, value=>bv, left=>y, right=>z, ... } )
                => 
                node_count (b, bv, node_count (a, av, x, y), z);

            single_l _ =>   raise exception MATCH;
        end;


        fun single_r (b, bv, TREE_NODE { key=>a, value=>av, left=>x, right=>y, ... }, z)
                => 
                node_count (a, av, x, node_count (b, bv, y, z));

            single_r _ =>   raise exception MATCH;
        end;


        fun double_l (a, av, w, TREE_NODE { key=>c, value=>cv, left=>TREE_NODE { key=>b, value=>bv, left=>x, right=>y, ... }, right=>z, ... } )
                =>
                node_count (b, bv, node_count (a, av, w, x), node_count (c, cv, y, z));

            double_l _ =>   raise exception MATCH;
        end;


        fun double_r (c, cv, TREE_NODE { key=>a, value=>av, left=>w, right=>TREE_NODE { key=>b, value=>bv, left=>x, right=>y, ... }, ... }, z)
                => 
                node_count (b, bv, node_count (a, av, w, x), node_count (c, cv, y, z));

            double_r _ =>   raise exception MATCH;
        end;


        fun rebalance (k, v, EMPTY, EMPTY)
                =>
                TREE_NODE { key=>k, value=>v, count=>1, left=>EMPTY, right=>EMPTY };

            rebalance (k, v, EMPTY, r as TREE_NODE { right=>EMPTY, left=>EMPTY, ... } )
                =>
                TREE_NODE { key=>k, value=>v, count=>2, left=>EMPTY, right=>r };

            rebalance (k, v, l as TREE_NODE { right=>EMPTY, left=>EMPTY, ... }, EMPTY)
                =>
                TREE_NODE { key=>k, value=>v, count=>2, left=>l, right=>EMPTY };

            rebalance (p as (_, _, EMPTY, TREE_NODE { left=>TREE_NODE _, right=>EMPTY, ... } ))
                =>
                double_l p;

            rebalance (p as (_, _, TREE_NODE { left=>EMPTY, right=>TREE_NODE _, ... }, EMPTY))
                =>
                double_r p;

             #  these cases almost never happen with small weight
            rebalance (p as (_, _, EMPTY, TREE_NODE { left=>TREE_NODE { count=>ln, ... }, right=>TREE_NODE { count=>rn, ... }, ... } ))
                =>
                if (ln < rn ) single_l p; else double_l p;fi;

            rebalance (p as (_, _, TREE_NODE { left=>TREE_NODE { count=>ln, ... }, right=>TREE_NODE { count=>rn, ... }, ... }, EMPTY))
                =>
                if (ln > rn ) single_r p; else double_r p;fi;

            rebalance (p as (_, _, EMPTY, TREE_NODE { left=>EMPTY, ... } ))
                =>
                single_l p;

            rebalance (p as (_, _, TREE_NODE { right=>EMPTY, ... }, EMPTY))
                =>
                single_r p;

            rebalance (p as (k, v, l as TREE_NODE { count=>ln, left=>ll, right=>lr, ... },
                          r as TREE_NODE { count=>rn, left=>rl, right=>rr, ... } ))
                =>
                if (rn >= wt ln ) # right is too big
                      rln = vals_count rl;
                      rrn = vals_count rr;

                    if (rln < rrn )  single_l p;  else  double_l p;fi;

                elif (ln >= wt rn )  # left is too big
                    lln = vals_count ll;
                    lrn = vals_count lr;

                  if (lrn < lln )  single_r p;  else  double_r p;fi;


              else TREE_NODE { key=>k, value=>v, count=>ln+rn+1, left=>l, right=>r };
              fi;
        end;

        stipulate
            fun min (TREE_NODE { left=>EMPTY, key, value, ... } ) => (key, value);
                min (TREE_NODE { left, ... } ) => min left;
                min _ => raise exception MATCH;
            end;

            fun delmin (TREE_NODE { left=>EMPTY, right, ... } ) => right;
                delmin (TREE_NODE { key, value, left, right, ... } ) => rebalance(key, value, delmin left, right);
                delmin _ => raise exception MATCH;
            end;

        herein

            fun delete' (EMPTY, r) => r;
                delete' (l, EMPTY) => l;
                delete' (l, r) => { my (mink, minv) = min r; 
                  rebalance(mink, minv, l, delmin r);
                };
            end;
        end;

    herein

        fun make_dictionary ()
            =
            EMPTY;


        fun singleton (x, v)
            =
            TREE_NODE { key=>x, value=>v, count=>1, left=>EMPTY, right=>EMPTY };


        fun set (EMPTY, x, v)
                =>
                TREE_NODE { key=>x, value=>v, count=>1, left=>EMPTY, right=>EMPTY };

            set (TREE_NODE (my_set as { key, left, right, value, ... } ), x, v)
                =>
                case (k::compare (key, x))
                    #
                    GREATER =>  rebalance(key, value, set (left, x, v), right);
                    LESS    =>  rebalance(key, value, left, set (right, x, v));
                    _       =>  TREE_NODE { key=>x, value=>v, left, right, count=> my_set.count };
                esac;
        end;


        fun m $ (x, v)
            =
            set (m, x, v);


        fun set' ((k, x), m)
            =
            set (m, k, x);


        fun contains_key (set, x)
            =
            mem set
            where
                fun mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        case (k::compare (x, key))
                            #
                            GREATER =>  mem right;
                            EQUAL   =>  TRUE;
                            LESS    =>  mem left;
                        esac;

                    mem EMPTY => FALSE;
                end;
            end;
        fun preceding_key (set, x)
            =
            mem (set, NULL)
            where
                fun maxkey (EMPTY, result)
                        =>
                        result;

                    maxkey (TREE_NODE { key, left, right, ... }, result)
                        =>
                        maxkey (right, THE key);
                end;

                fun mem (TREE_NODE (n as { key, left, right, ... } ), result)
                        =>
                        case (k::compare (x, key))
                            #
                            GREATER =>  mem   (right, THE key);
                            EQUAL   =>  maxkey(left, result);
                            LESS    =>  mem   (left,  result);
                        esac;

                    mem (EMPTY, result) => result;
                end;
            end;
        fun following_key (set, x)
            =
            mem (set, NULL)
            where
                fun minkey (EMPTY, result)
                        =>
                        result;

                    minkey (TREE_NODE { key, left, right, ... }, result)
                        =>
                        minkey (left, THE key);
                end;

                fun mem (TREE_NODE (n as { key, left, right, ... } ), result)
                        =>
                        case (k::compare (x, key))
                            #
                            GREATER =>  mem (right, result);
                            EQUAL   =>  result;
                            LESS    =>  mem (left, THE key);
                        esac;

                    mem (EMPTY, result) => result;
                end;
            end;

        # Search on a key, return (THE value) if found,
        # else return NULL.
        #
        fun get (set, x)
            =
            mem set
            where
                fun mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        case (k::compare (x, key))
                            #
                            GREATER =>  mem right;
                            EQUAL   =>  THE n.value;
                            LESS    =>  mem left;
                        esac;

                    mem EMPTY => NULL;
                end;
            end;

        # Search on a key, return value if found,
        # else raise lib_base::NOT_FOUND
        #
        fun get_or_raise_exception_not_found (set, x)
            =
            mem set
            where
                fun mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        case (k::compare (x, key))
                            #
                            GREATER =>  mem right;
                            EQUAL   =>  n.value;
                            LESS    =>  mem left;
                        esac;

                    mem EMPTY => raise exception lib_base::NOT_FOUND;
                end;
            end;


        stipulate
            fun drop'' (EMPTY, x)
                    =>
                    raise exception lib_base::NOT_FOUND;

                drop'' (set as TREE_NODE { key, left, right, value, ... }, x)
                    =>
                    case (k::compare (key, x))
                        #
                        GREATER =>  {   (drop'' (left,  x)) ->  (left', v);
                                        #
                                        (rebalance (key, value, left', right), v);
                                    };

                        LESS =>     {   (drop'' (right, x)) ->   (right', v);
                                        #
                                        (rebalance (key, value, left, right'), v);
                                    };

                         _ => (delete'(left, right), value);
                    esac;
            end;
        herein
            fun drop (old_map, key_to_drop)                                     # Return new_map, or old_map if key_to_drop was not found.
                =
                #1 (drop'' (old_map, key_to_drop))
                except
                    lib_base::NOT_FOUND = old_map;

            fun get_and_drop (old_map, key_to_drop)                             # Return (new_map, THE value)  or (old_map, NULL) if key_to_drop was not found.
                =
                {   (drop'' (old_map, key_to_drop))
                        ->
                        (new_map, val);

                    (new_map, THE val);
                }
                except
                    lib_base::NOT_FOUND = (old_map, NULL);
        end;

        fun vals_list d
            =
            d2l (d,[])
            where
                fun d2l (TREE_NODE { key, value, left, right, ... }, l)
                        =>
                        d2l (left, value ! (d2l (right, l)));

                    d2l (EMPTY, l) => l;
                end;
            end;


        fun keyvals_list d
            =
            d2l (d,[])
            where
                fun d2l (TREE_NODE { key, value, left, right, ... }, l)
                        =>
                        d2l (left, (key, value) ! (d2l (right, l)));

                    d2l (EMPTY, l)
                        =>
                        l;
                end;
            end;


        fun keys_list d
            =
            d2l (d,[])
            where
                fun d2l (TREE_NODE { key, left, right, ... }, l)
                        =>
                        d2l (left, key ! (d2l (right, l)));

                    d2l (EMPTY, l)
                        =>
                        l;
                end;
            end;



        stipulate

            fun next ((t as TREE_NODE { right, ... } ) ! rest)
                    =>
                    (t, left (right, rest));

                next _
                    =>
                    (EMPTY, []);
            end 

            also
            fun left (t as TREE_NODE { left=>l, ... }, rest)
                    =>
                    left (l, t ! rest);

                left (EMPTY, rest)
                    =>
                    rest;
            end;

        herein

            fun compare_sequences compare_rng (s1, s2)
                =
                compare (left (s1, []), left (s2, []))
                where 
                    fun compare (t1, t2)
                        =
                        case (next t1, next t2)
                            #
                            ((EMPTY, _), (EMPTY, _)) => EQUAL;

                            ((EMPTY, _), _) => LESS;

                            (_, (EMPTY, _)) => GREATER;

                            ((TREE_NODE { key=>x1, value=>y1, ... }, r1), (TREE_NODE { key=>x2, value=>y2, ... }, r2))
                                =>
                                case (key::compare (x1, x2))
                                    #
                                    EQUAL =>    case (compare_rng (y1, y2))
                                                    #
                                                    EQUAL => compare (r1, r2);
                                                    order => order;
                                                 esac;

                                    order => order;
                                esac;
                        esac;
                end;
        end;                            # stipulate

        fun keyed_apply f d
            =
            apply' d
            where
                fun apply' (TREE_NODE { key, value, left, right, ... } )
                        =>
                        {   apply' left;
                            f (key, value);
                            apply' right;
                        };
                    
                    apply' EMPTY
                        =>
                        ();
                end;
            end;

        fun apply f d
            =
            apply' d
            where
                fun apply' (TREE_NODE { value, left, right, ... } )
                        =>
                        {   apply' left;
                            f value;
                            apply' right;
                        };

                    apply' EMPTY
                        =>
                        ();
                end;
            end;

        fun keyed_map f d
            =
            map' d
            where
                fun map' (TREE_NODE { key, value, left, right, count } ) 
                        =>
                        {   left' = map' left;
                            value' = f (key, value);
                            right' = map' right;

                            TREE_NODE { count, key, value=>value', left => left', right => right'};
                        };

                    map' EMPTY
                        =>
                        EMPTY;
                end;
            end;

        fun map f d
            =
            keyed_map
                (\\ (_, x) =  f x)
                d;

        fun keyed_fold_forward f init d
            =
            fold (d, init)
            where
                fun fold (TREE_NODE { key, value, left, right, ... }, v)
                        =>
                        fold (right, f (key, value, fold (left, v)));

                    fold (EMPTY, v)
                        =>
                        v;
                end;
            end;

        fun fold_forward f init d
            =
            keyed_fold_forward
                (\\ (_, v, accum) =  f (v, accum))
                init
                d;

        fun keyed_fold_backward f init d
            =
            fold (d, init)
            where
                fun fold (TREE_NODE { key, value, left, right, ... }, v)
                        =>
                        fold (left, f (key, value, fold (right, v)));

                    fold (EMPTY, v)
                        =>
                        v;
                end;
            end;

        fun fold_backward f init d
            =
            keyed_fold_backward
                (\\ (_, v, accum) = f (v, accum))
                init
                d;

## To be implemented ## XXX BUGGO FIXME
#       my filter:   (X -> Bool) -> Map(X) -> Map(X)
#       my keyed_filter:  (key::Key * X -> Bool) -> Map(X) -> Map(X)


    end;                        # stipulate

    fun difference_with (m1, m2)
        =
        {   keys_to_remove =  keys_list  m2;
            #
            remove (m1, keys_to_remove)
            where
                fun remove (m1, [])
                        =>
                        m1;

                    remove (m1, key ! rest)
                        =>
                        remove (drop (m1, key), rest);
                end;
            end;
        };

    fun from_list (pairs: List((key::Key, X)))
        =
        {   tree = empty;
            #
            add (tree, pairs)
            where
                fun add (tree, [])
                        =>
                        tree;

                    add (tree, (key,val) ! rest)
                        =>
                        add (set (tree, key, val), rest);
                end;
            end;
        };

    # The following are generic implementations
    # of the union_with, intersect_with, and
    # merge_with operations.  These should be
    # specialized for the internal
    # representations at some point.

    fun union_with f (m1, m2)
        =
        if (vals_count m1 > vals_count m2)   keyed_fold_forward  (ins (\\ (a, b) = f (b, a)))  m1  m2;
        else                                 keyed_fold_forward  (ins f)                       m2  m1;
        fi
        where
            fun ins  f (key, x, m)
                =
                case (get (m, key))
                    #
                    THE x' =>  set (m, key, f (x, x'));
                    NULL   =>  set (m, key, x);
                esac;
        end;


    fun keyed_union_with f (m1, m2)
        =
        if (vals_count m1 > vals_count m2)   keyed_fold_forward  (ins (\\ (k, a, b) = f (k, b, a)))  m1  m2;
        else                                 keyed_fold_forward  (ins f)                             m2  m1;
        fi
        where
            fun ins f (key, x, m)
                =
                case (get (m, key))
                    #
                    THE x' => set (m, key, f (key, x, x'));
                    NULL   => set (m, key, x);
                esac;
        end;


    fun intersect_with f (m1, m2)
        =
        if (vals_count m1 <= vals_count m2)  intersect  (\\ (a, b) = f (b, a))  (m2, m1);
        else                                 intersect  f                       (m1, m2);
        fi
        where
            # Iterate over the elements of m1,
            # checking for membership in m2:
            #
            fun intersect f (m1, m2)
                =
                keyed_fold_forward ins empty m1
                where 
                    fun ins (key, x, m)
                        =
                        case (get (m2, key))
                            #
                            NULL   =>   m;
                            THE x' =>   set (m, key, f (x, x'));
                        esac;
                end;
        end;

    fun keyed_intersect_with f (m1, m2)
        =
        if (vals_count m1 <= vals_count m2)   intersect  (\\ (k, a, b) =  f (k, b, a))  (m2, m1);
        else                                  intersect  f                              (m1, m2);
        fi
        where
            # Iterate over the elements of m1,
            # checking for membership in m2:
            #
            fun intersect f (m1, m2)
                =
                keyed_fold_forward ins empty m1
                where 

                    fun ins (key, x, m)
                        =
                        case (get (m2, key))
                            #
                            NULL   => m;
                            THE x' => set (m, key, f (key, x, x'));
                        esac;
                end;
        end;

    fun merge_with f (m1, m2)
        =
        merge (keyvals_list m1, keyvals_list m2, empty)
        where
            fun merge ([], [], m) => m;
                merge ((k1, x1) ! r1, [], m) => mergef (k1, THE x1, NULL, r1, [], m);
                merge ([], (k2, x2) ! r2, m) => mergef (k2, NULL, THE x2, [], r2, m);

                merge (m1 as ((k1, x1) ! r1), m2 as ((k2, x2) ! r2), m)
                    =>
                    case (key::compare (k1, k2))
                        #
                        LESS    => mergef (k1, THE x1, NULL, r1, m2, m);
                        EQUAL   => mergef (k1, THE x1, THE x2, r1, r2, m);
                        GREATER => mergef (k2, NULL, THE x2, m1, r2, m);
                    esac;
            end

            also
            fun mergef (k, x1, x2, r1, r2, m)
                =
                case (f (x1, x2))
                    #
                    THE y => merge (r1, r2, set (m, k, y));
                    NULL  => merge (r1, r2, m);
                esac;
        end;

    fun keyed_merge_with f (m1, m2)
        =
        merge (keyvals_list m1, keyvals_list m2, empty)
        where
            fun merge ([], [], m) => m;
                merge ((k1, x1) ! r1, [], m) => mergef (k1, THE x1, NULL, r1, [], m);
                merge ([], (k2, x2) ! r2, m) => mergef (k2, NULL, THE x2, [], r2, m);
                merge (m1 as ((k1, x1) ! r1), m2 as ((k2, x2) ! r2), m)
                    =>
                    case (key::compare (k1, k2))
                        #
                        LESS    => mergef (k1, THE x1, NULL, r1, m2, m);
                        EQUAL   => mergef (k1, THE x1, THE x2, r1, r2, m);
                        GREATER => mergef (k2, NULL, THE x2, m1, r2, m);
                    esac;
            end

            also
            fun mergef (k, x1, x2, r1, r2, m)
                =
                case (f (k, x1, x2))
                    #
                    NULL => merge (r1, r2, m);
                    THE y => merge (r1, r2, set (m, k, y));
                esac;
        end;

    # This is a generic implementation of filter.
    # It should be specialized to the data-package at some point.

    fun filter pred_g m
        =
        keyed_fold_forward f empty m
        where 
            fun f (key, item, m)
                =
                if (pred_g item)   set (m, key, item);
                else                    m;
                fi;
        end;

    fun keyed_filter pred_g m
        =
        keyed_fold_forward f empty m
        where
            fun f (key, item, m)
                =
                if (pred_g (key, item))   set (m, key, item);
                else                           m;
                fi;
        end;

    # This is a generic implementation of map'.
    # It should be specialized to the data-package at some point.

    fun map' f m
        =
        keyed_fold_forward g empty m
        where
            fun g (key, item, m)
                =
                case (f item)
                    #
                    THE item' =>  set (m, key, item');
                    NULL      =>  m;
                esac;
        end;

    fun keyed_map' f m
        =
        keyed_fold_forward g empty m
        where
            fun g (key, item, m)
                =
                case (f (key, item))
                    #
                    THE item' =>  set (m, key, item');
                    NULL      =>  m;
                esac;
        end;

};                                      #  generic package binary_map_g 


## COPYRIGHT (c) 1993 by AT&T Bell Laboratories.  See SMLNJ-COPYRIGHT file for details.
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext