PreviousUpNext

15.4.898  src/lib/src/int-binary-map.pkg

## int-binary-map.pkg
#
# Normally
#     src/lib/src/int-red-black-map.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.
#
#  Altered to work as a general intmap - Emden Gansner


package int_binary_map : Map                                    # Map   is from   src/lib/src/map.api
where
    key::Key == int::Int
=
package {
    package key {
        Key = int::Int;
        compare = int::compare;
    };


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

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


    Map(X)    = EMPTY 

              | TREE_NODE  {

                  key:    Int, 
                  value:  X, 

                  count:  Int, 

                  left:   Map(X), 
                  right:  Map(X)
                };

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


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


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

        vals_count EMPTY
            =>
            0;
    end;

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

        first_val_else_null (TREE_NODE { left, ... } )
            =>
            first_val_else_null left;

        first_val_else_null EMPTY
            =>
            NULL;
    end;

    # Return the first item in the map
    # and its key.  Return NULL if it is empty:
    #
    fun 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;

        first_keyval_else_null EMPTY
            =>
            NULL;
    end;


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

        last_val_else_null (TREE_NODE { right, ... } )
            =>
            last_val_else_null right;

        last_val_else_null EMPTY
            =>
            NULL;
    end;

    # Return the last item in the map
    # and its key.  Return NULL if it is empty:
    #
    fun 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;

        last_keyval_else_null EMPTY
            =>
            NULL;
    end;


    stipulate

        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 n ) => TREE_NODE { key=>k, value=>v, count=>1+n.count,          left=>EMPTY, right=>r     };
            rebalance (k, v, l as TREE_NODE n, EMPTY            ) => TREE_NODE { key=>k, value=>v, count=>1+n.count,          left=>l,     right=>EMPTY };
            rebalance (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, ... } )
                => 
                rebalance (b, bv, rebalance (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)
                => 
                rebalance (a, av, x, rebalance (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, ... } )
                =>
                rebalance (b, bv, rebalance (a, av, w, x), rebalance (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)
                => 
                rebalance (b, bv, rebalance (a, av, w, x), rebalance (c, cv, y, z));

            double_r _
                =>
                raise exception MATCH;
        end;


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

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

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

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

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

            # These cases almost never
            # happen with small weight:

            tree_node' (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;

            tree_node' (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;

            tree_node' (p as (_, _, EMPTY, TREE_NODE { left=>EMPTY, ... } )) => single_l p;
            tree_node' (p as (_, _, TREE_NODE { right=>EMPTY, ... }, EMPTY)) => single_r p;

            tree_node' (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, ... } )
                    =>
                    tree_node'(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; 
                                          tree_node'(mink, minv, l, delmin r);
                                      };
            end;

        end;

    herein

        empty = 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)
                =>
                if   (key > x) tree_node'(key, value, set (left, x, v), right);
                elif (key < x) tree_node'(key, value, left, set (right, x, v));
                else           TREE_NODE { key=>x, value=>v, left, right, count=> my_set.count };
                fi;
        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 EMPTY
                        =>
                        FALSE;

                    mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        if   (x > key)   mem right;
                        elif (x < key)   mem left;
                        else             TRUE;
                        fi;
                  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 (EMPTY, result)
                        =>
                        result;

                    mem (TREE_NODE (n as { key, left, right, ... } ), result)
                        =>
                        if   (x > key)   mem   (right, THE key);
                        elif (x < key)   mem   (left,  result );
                        else             maxkey(left,  result );
                        fi;
                  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 (EMPTY, result)
                        =>
                        result;

                    mem (TREE_NODE (n as { key, left, right, ... } ), result)
                        =>
                        if   (x > key)   mem   (right, result );
                        elif (x < key)   mem   (left,  THE key);
                        else             minkey(right, result );
                        fi;
                  end;
            end;

        # Search on a key, return value if found,
        # else raise lib_base::NOT_FOUND
        #
        fun get (set, x)
            =
            {   fun mem EMPTY => NULL;
                        #
                    mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        if   (x > key)  mem right;
                        elif (x < key)  mem left;
                        else            THE n.value;
                        fi;
                end;

                mem set;
            };

        # Search on a key, return value if found,
        # else raise lib_base::NOT_FOUND
        #
        fun get_or_raise_exception_not_found (map, x)
            =
            mem map
            where
                fun mem EMPTY =>  raise exception lib_base::NOT_FOUND;
                        #
                    mem (TREE_NODE (n as { key, left, right, ... } ))
                        =>
                        if   (x > key)  mem right;
                        elif (x < key)  mem left;
                        else            n.value;
                        fi;
                end;
            end;

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

                drop'' (set as TREE_NODE { key, left, right, value, ... }, x)
                    =>
                    if (key > x) 
                        #
                       (drop'' (left, x)) ->  (left',  v);

                       (tree_node'(key, value, left', right), v); 

                    elif (key < x)

                       (drop'' (right, x)) ->  (right', v);

                       (tree_node'(key, value, left, right'), v); 

                   else
                       (delete' (left, right), value);
                   fi;
            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 (EMPTY, l)
                        =>
                        l;

                    d2l (TREE_NODE { key, value, left, right, ... }, l)
                        =>
                        d2l (left, value ! (d2l (right, l)));
                end;
            end;

        fun keyvals_list d
            =
            d2l (d,[])
            where
                fun d2l (EMPTY, l)
                        =>
                        l;

                    d2l (TREE_NODE { key, value, left, right, ... }, l)
                        =>
                        d2l (left, (key, value) ! (d2l (right, 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 (EMPTY, rest)
                    =>
                    rest;

                left (t as TREE_NODE { left=>l, ... }, rest)
                    =>
                    left (l, t ! 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
            =
            appf d
            where
                fun appf (TREE_NODE { key, value, left, right, ... } )
                        =>
                        {   appf left;
                            f (key, value);
                            appf right;
                        };

                   appf EMPTY
                       =>
                       ();
                end;
            end;

        fun apply f d
            =
            keyed_apply
                (\\ (_, v) = f v)
                d;

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

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

                    mapf 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;

    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 operetions.  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))
                  
                     NULL   =>  set (m, key, x);
                     THE x' =>  set (m, key, f (x, 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))
                  
                     NULL   =>  set (m, key, x);
                     THE x' =>  set (m, key, f (key, x, x'));
                esac;
        end;


    fun intersect_with f (m1, m2)
        =
        if   (vals_count m1 > vals_count m2)   intersect f (m1, m2);
        else                                   intersect (\\ (a, b) =  f (b, a))  (m2, m1);
        fi
        where
            # Iterate over the elements of m1,
            # checking for membership in m2 

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

                
                    keyed_fold_forward ins empty m1;
                };
        end;


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

                
                    keyed_fold_forward ins empty m1;
                };
        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)
                    =>
                    if   (k1 < k2)
                         mergef (k1, THE x1, NULL, r1, m2, m);
                    else
                         if   (k1 == k2)   mergef (k1, THE x1, THE x2, r1, r2, m);
                         else              mergef (k2, NULL,   THE x2, m1, r2, m);   fi;
                    fi;
            end

            also
            fun mergef (k, x1, x2, r1, r2, m)
                =
                case (f (x1, x2))
                  
                     NULL  =>  merge (r1, r2, m);
                     THE y =>  merge (r1, r2, set (m, k, y));
                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)
                    =>
                    if       (k1 <  k2)   mergef (k1, THE x1, NULL,   r1, m2, m);   else
                         if  (k1 == k2)   mergef (k1, THE x1, THE x2, r1, r2, m);   else
                                          mergef (k2, NULL,   THE x2, m1, r2, m);   fi;
                    fi;
            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.   XXX BUGGO FIXME

    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)
                  
                     NULL
                         =>
                         m;

                     THE item'
                         =>
                         set (m, key, item');
                esac;
        end;

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


## 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