PreviousUpNext

15.4.879  src/lib/src/digraph.pkg

## digraph.pkg
#
# Simple general-purposee fully-persistent directed graph.
# This is a specialization of src/lib/src/tuplebase.pkg
#
# Space usage will be dominated by Edges rather than Nodes:
#
#     Each Edge will consume:
#        3 words directly
#        2 words each in 3 single-field indices.
#        4 words each in 3 double-field indices.
#        1 word  each in 1 Edge-set   index.
#      --------------
#       22 words total.  Internal heap overhead will add another 9 words or so; call it 30 words/edge == 120 bytes/edge on a 32-bit machine.
#
# So on a 32-bit machine a graph containing million edges will
# consume about 128MB -- quite reasonable on today's desktop machines.
#
# On a 64-bit machine that would be 256MB -- except Mythryl doesn't
# support 64-bit architectures yet. :-)         -- 2014-07-16 CrT

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

# Compare to:
#     src/lib/src/digraphxy.pkg
#     src/lib/src/tuplebase.pkg
#     src/lib/graph/digraph-by-adjacency-list.pkg
#     src/lib/compiler/back/low/mcg/machcode-controlflow-graph-g.pkg


stipulate
    package im1  =  int_red_black_map;                                          # int_red_black_map                             is from   src/lib/src/int-red-black-map.pkg
    package is1  =  int_red_black_set;                                          # int_red_black_set                             is from   src/lib/src/int-red-black-set.pkg
herein

    package digraph
    :       Digraph                                                             # Digraph                                       is from   src/lib/src/digraph.api
    {
        Other = Exception;
        #
        Datum = NONE
              | INT    Int
              | ID     Id
              | FLOAT  Float
              | STRING String
              | OTHER  Other
              | TBASE  Exception                                                # Making Datum and Graph mutually recursive would be messy, so we use the exception hack instead.
              ;

        Node = { id:    Int,
                 datum: Datum
               };

        Tag  = { id:    Int,
                 datum: Datum
               };

        Tagless_Edge  = (Node, Node);
        Edge          = (Node, Tag, Node);

        fun compare_i2
              ( ( i1a: Int,
                  i1b: Int
                ),
                ( i2a: Int,
                  i2b: Int
                )
              )
            =
            case (int::compare (i1a, i2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  int::compare (i1b, i2b);
            esac;

        fun compare_12of2
              ( ( { id => id1a, ... },
                  { id => id1b, ... }
                ):                              Tagless_Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... }
                ):                              Tagless_Edge
              )
            =
            case (int::compare (id1a, id2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  (int::compare (id1b, id2b));
            esac;

        fun compare_12of3
              ( ( { id => id1a, ... },
                  { id => id1b, ... },
                  { id => id1c, ... }
                ):                              Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... },
                  { id => id2c, ... }
                ):                              Edge
              )
            =
            case (int::compare (id1a, id2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  (int::compare (id1b, id2b));
            esac;

        fun compare_13of3
              ( ( { id => id1a, ... },
                  { id => id1b, ... },
                  { id => id1c, ... }
                ):                              Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... },
                  { id => id2c, ... }
                ):                              Edge
              )
            =
            case (int::compare (id1a, id2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  (int::compare (id1c, id2c));
            esac;

        fun compare_23of3
              ( ( { id => id1a, ... },
                  { id => id1b, ... },
                  { id => id1c, ... }
                ):                              Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... },
                  { id => id2c, ... }
                ):                              Edge
              )
            =
            case (int::compare (id1b, id2b))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  (int::compare (id1c, id2c));
            esac;

        fun compare_123of3
              ( ( { id => id1a, ... },
                  { id => id1b, ... },
                  { id => id1c, ... }
                ):                              Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... },
                  { id => id2c, ... }
                ):                              Edge
              )
            =
            case (int::compare (id1a, id2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  case (int::compare (id1b, id2b))
                                GREATER =>  GREATER;
                                LESS    =>  LESS;
                                EQUAL   =>  int::compare (id1c, id2c);
                            esac;
            esac;

        fun compare_123of3
              ( ( { id => id1a, ... },
                  { id => id1b, ... },
                  { id => id1c, ... }
                ):                              Edge,
                ( { id => id2a, ... },
                  { id => id2b, ... },
                  { id => id2c, ... }
                ):                              Edge
              )
            =
            case (int::compare (id1a, id2a))
                #
                GREATER =>  GREATER;
                LESS    =>  LESS;
                EQUAL   =>  case (int::compare (id1b, id2b))
                                GREATER =>  GREATER;
                                LESS    =>  LESS;
                                EQUAL   =>  int::compare (id1c, id2c);
                            esac;
            esac;

        package im2
            =
            red_black_map_g (
                #
                package {
                    Key = (Int, Int);
                    #
                    compare = compare_i2;
                }
            );

        package ts                                                              # Sets of Tagless_Edges
            =
            red_black_set_g (                                                   # red_black_set_g                               is from   src/lib/src/red-black-set-g.pkg
                #
                package {
                    Key = Tagless_Edge;
                    #
                    compare = compare_12of2;
                }
            );

        package es                                                              # Sets of Edges
            =
            red_black_set_g (                                                   # red_black_set_g                               is from   src/lib/src/red-black-set-g.pkg
                #
                package {
                    Key = Edge;
                    #
                    compare = compare_123of3;
                }
            );


        Graph
          =
          { index_1of2:         im1::Map( ts::Set ),
            index_2of2:         im1::Map( ts::Set ),
            #
            index_12of2:                  ts::Set,
            #
            #
            index_1of3:         im1::Map( es::Set ),
            index_2of3:         im1::Map( es::Set ),
            index_3of3:         im1::Map( es::Set ),
            #
            index_12of3:        im2::Map( es::Set ),
            index_13of3:        im2::Map( es::Set ),
            index_23of3:        im2::Map( es::Set ),
            #
            index_123of3:                 es::Set
          };


        empty_graph
          =
          { index_1of2   =>     im1::empty:     im1::Map( ts::Set ),
            index_2of2   =>     im1::empty:     im1::Map( ts::Set ),
            #
            index_12of2  =>     ts::empty:                ts::Set,
            #
            #
            index_1of3   =>     im1::empty:     im1::Map( es::Set ),
            index_2of3   =>     im1::empty:     im1::Map( es::Set ),
            index_3of3   =>     im1::empty:     im1::Map( es::Set ),
            #
            index_12of3  =>     im2::empty:     im2::Map( es::Set ),
            index_13of3  =>     im2::empty:     im2::Map( es::Set ),
            index_23of3  =>     im2::empty:     im2::Map( es::Set ),
            #
            index_123of3 =>     es::empty:                es::Set
          };

        fun  put_tagless_edge
              (
                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph,
                tagless_edge as
                ( { id => id1, ... },
                  { id => id2, ... }
                ):                                                      Tagless_Edge
              )
            =
            {   index_1of2
                    =
                    case (im1::get (index_1of2, id1))
                        #
                        THE set =>  im1::set (index_1of2, id1, ts::add (set, tagless_edge));
                        NULL    =>  im1::set (index_1of2, id1, ts::singleton(tagless_edge));
                    esac;

                index_2of2
                    =
                    case (im1::get (index_2of2, id2))
                        #
                        THE set =>  im1::set (index_2of2, id2, ts::add (set, tagless_edge));
                        NULL    =>  im1::set (index_2of2, id2, ts::singleton(tagless_edge));
                    esac;

                index_12of2
                    =
                    ts::add (index_12of2, tagless_edge);

                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph;
            };

        fun  put_edge
              (
                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph,
                edge as
                ( { id => id1, ... },
                  { id => id2, ... },
                  { id => id3, ... }
                ):                                                      Edge
              )
            =
            {   index_1of3
                    =
                    case (im1::get (index_1of3, id1))
                        #
                        THE set =>  im1::set (index_1of3, id1, es::add (set, edge));
                        NULL    =>  im1::set (index_1of3, id1, es::singleton(edge));
                    esac;

                index_2of3
                    =
                    case (im1::get (index_2of3, id2))
                        #
                        THE set =>  im1::set (index_2of3, id2, es::add (set, edge));
                        NULL    =>  im1::set (index_2of3, id2, es::singleton(edge));
                    esac;

                index_3of3
                    =
                    case (im1::get (index_3of3, id3))
                        #
                        THE set =>  im1::set (index_3of3, id3, es::add (set, edge));
                        NULL    =>  im1::set (index_3of3, id3, es::singleton(edge));
                    esac;


                index_12of3
                    =
                    case (im2::get (index_12of3, (id1, id2)))
                        #
                        THE set =>  im2::set (index_12of3, (id1, id2), es::add (set, edge));
                        NULL    =>  im2::set (index_12of3, (id1, id2), es::singleton(edge));
                    esac;

                index_13of3
                    =
                    case (im2::get (index_13of3, (id1, id3)))
                        #
                        THE set =>  im2::set (index_13of3, (id1, id3), es::add (set, edge));
                        NULL    =>  im2::set (index_13of3, (id1, id3), es::singleton(edge));
                    esac;

                index_23of3
                    =
                    case (im2::get (index_23of3, (id2, id3)))
                        #
                        THE set =>  im2::set (index_23of3, (id2, id3), es::add (set, edge));
                        NULL    =>  im2::set (index_23of3, (id2, id3), es::singleton(edge));
                    esac;


                index_123of3
                    =
                    es::add (index_123of3, edge);


                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph;
            };


        fun  drop_tagless_edge
              (
                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph,
                tagless_edge as
                ( { id => id1, ... },
                  { id => id2, ... }
                ):                                                      Tagless_Edge
              )
            =
            {   index_1of2
                    =
                    case (im1::get (index_1of2, id1))
                        #
                        THE set =>  if (ts::vals_count(set) > 1)  im1::set  (index_1of2, id1, ts::drop (set, tagless_edge));
                                    else                          im1::drop (index_1of2, id1);
                                    fi;
                        NULL    =>  index_1of2;                 # Tagless_Edge isn't in graph. Possibly we should raise an exception here.
                    esac;

                index_2of2
                    =
                    case (im1::get (index_2of2, id2))
                        #
                        THE set =>  if (ts::vals_count(set) > 1)  im1::set  (index_2of2, id2, ts::drop (set, tagless_edge));
                                    else                          im1::drop (index_1of2, id2);
                                    fi;
                        NULL    =>  index_2of2;                 # Tagless_Edge isn't in graph. Possibly we should raise an exception here.
                    esac;


                index_12of2
                    =
                    ts::drop (index_12of2, tagless_edge);


                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph;
            };

        fun  drop_edge
              (
                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph,
                edge as
                ( { id => id1, ... },
                  { id => id2, ... },
                  { id => id3, ... }
                ):                                                      Edge
              )
            =
            {   index_1of3
                    =
                    case (im1::get (index_1of3, id1))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im1::set  (index_1of3, id1, es::drop (set, edge));
                                    else                          im1::drop (index_1of3, id1);
                                    fi;
                        NULL    =>  index_1of3;                 # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;

                index_2of3
                    =
                    case (im1::get (index_2of3, id2))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im1::set  (index_2of3, id2, es::drop (set, edge));
                                    else                          im1::drop (index_2of3, id2);
                                    fi;
                        NULL    =>  index_2of3;                 # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;

                index_3of3
                    =
                    case (im1::get (index_3of3, id3))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im1::set  (index_3of3, id3, es::drop (set, edge));
                                    else                          im1::drop (index_3of3, id3);
                                    fi;
                        NULL    =>  index_3of3;                 # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;


                index_12of3
                    =
                    case (im2::get (index_12of3, (id1, id2)))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im2::set  (index_12of3, (id1, id2), es::drop (set, edge));
                                    else                          im2::drop (index_12of3, (id1, id2));
                                    fi;
                        NULL    =>  index_12of3;                # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;

                index_13of3
                    =
                    case (im2::get (index_13of3, (id1, id3)))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im2::set  (index_13of3, (id1, id3), es::drop (set, edge));
                                    else                          im2::drop (index_13of3, (id1, id3));
                                    fi;
                        NULL    =>  index_13of3;                # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;

                index_23of3
                    =
                    case (im2::get (index_23of3, (id2, id3)))
                        #
                        THE set =>  if (es::vals_count(set) > 1)  im2::set  (index_23of3, (id2, id3), es::drop (set, edge));
                                    else                          im2::drop (index_23of3, (id2, id3));
                                    fi;
                        NULL    =>  index_23of3;                # Edge isn't in graph. Possibly we should raise an exception here.
                    esac;



                index_123of3
                    =
                    es::drop (index_123of3, edge);


                { index_1of2,
                  index_2of2,
                  #
                  index_12of2,
                  #
                  #
                  index_1of3,
                  index_2of3,
                  index_3of3,
                  #
                  index_12of3,
                  index_13of3,
                  index_23of3,
                  #
                  index_123of3
                }:                                                      Graph;
            };


        fun get_tagless_edges    (t: Graph)                   =              t.index_12of2;
        #
        fun get_tagless_edges1   (t: Graph, a: Node)          =  im1::get   (t.index_1of2, a.id);
        fun get_tagless_edges2   (t: Graph, a: Node)          =  im1::get   (t.index_2of2, a.id);
        #
        fun has_tagless_edge     (t: Graph, d: Tagless_Edge)         =  ts::member (t.index_12of2, d);

        fun get_edges   (t: Graph)                   =              t.index_123of3;
        #
        fun get_edges1  (t: Graph, a: Node)          =  im1::get   (t.index_1of3, a.id);
        fun get_edges2  (t: Graph, a: Node)          =  im1::get   (t.index_2of3, a.id);
        fun get_edges3  (t: Graph, a: Node)          =  im1::get   (t.index_3of3, a.id);
        #
        fun get_edges12 (t: Graph, a: Node, b: Node) =  im2::get   (t.index_12of3, (a.id, b.id));
        fun get_edges13 (t: Graph, a: Node, c: Node) =  im2::get   (t.index_13of3, (a.id, c.id));
        fun get_edges23 (t: Graph, b: Node, c: Node) =  im2::get   (t.index_23of3, (b.id, c.id));
        #
        fun has_edge    (t: Graph, d: Edge)        =  es::member (t.index_123of3, d);


        fun make_node ()
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  NONE
            };

        fun make_int_node (i: Int)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  INT i
            };

        fun make_id_node (i: Id)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  ID i
            };

        fun make_string_node (s: String)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  STRING s
            };

        fun make_float_node (f: Float)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  FLOAT f
            };

        fun make_other_node (x: Other)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  OTHER x
            };

        exception GRAPH Graph;                                          # Making Datum and Graph mutually recursive would be messy, so we use the exception hack instead.

        fun make_graph_node (graph: Graph)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  TBASE (GRAPH graph)
            };


        fun node_datum  ({ datum, ...            }: Node) =   datum;

        fun node_int    ({ id, datum => INT i    }: Node) =>  THE i;
            node_int    _                                 =>  NULL;
        end;

        fun node_id     ({ id, datum => ID  i    }: Node) =>  THE i;
            node_id     _                                 =>  NULL;
        end;

        fun node_string ({ id, datum => STRING s }: Node) =>  THE s;
            node_string _                                 =>  NULL;
        end;

        fun node_float  ({ id, datum => FLOAT  f }: Node) =>  THE f;
            node_float  _                                 =>  NULL;
        end;

        fun node_other  ({ id, datum => OTHER  x }: Node) =>  THE x;
            node_other  _                                 =>  NULL;
        end;

        fun node_graph  ({ id, datum => TBASE (GRAPH graph) }: Node) =>  THE graph;
            node_graph  _                                            =>  NULL;
        end;


        fun make_tag ()
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  NONE
            };

        fun make_int_tag (i: Int)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  INT i
            };

        fun make_id_tag (i: Id)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  ID i
            };

        fun make_string_tag (s: String)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  STRING s
            };

        fun make_float_tag (f: Float)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  FLOAT f
            };

        fun make_other_tag (x: Other)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  OTHER x
            };

        fun make_graph_tag (graph: Graph)
            =
            { id    =>  id_to_int (issue_unique_id ()),
              datum =>  TBASE (GRAPH graph)
            };


        fun tag_datum  ({ datum, ...            }: Tag) =   datum;

        fun tag_int    ({ id, datum => INT i    }: Tag) =>  THE i;
            tag_int    _                                =>  NULL;
        end;

        fun tag_id     ({ id, datum => ID  i    }: Tag) =>  THE i;
            tag_id     _                                =>  NULL;
        end;

        fun tag_string ({ id, datum => STRING s }: Tag) =>  THE s;
            tag_string _                                =>  NULL;
        end;

        fun tag_float  ({ id, datum => FLOAT  f }: Tag) =>  THE f;
            tag_float  _                                =>  NULL;
        end;

        fun tag_other  ({ id, datum => OTHER  x }: Tag) =>  THE x;
            tag_other  _                                =>  NULL;
        end;

        fun tag_graph  ({ id, datum => TBASE (GRAPH graph) }: Tag) =>  THE graph;
            tag_graph  _                                           =>  NULL;
        end;


        fun nodes_apply                                                         # Apply do_node to all Nodes in Graph. 
              ( { index_12of2,
                  index_123of3,
                  ...
                }:      Graph
              )
              (do_node: Node -> Void)
            =
            {   ts::apply  do_tagless_edge   index_12of2;
                es::apply  do_edge           index_123of3;
            }
            where
                already_seen =  REF is1::empty;
                #
                fun do_tagless_edge ((a1, a2): Tagless_Edge)
                    =
                    {
                        if (not (is1::member (*already_seen, a1.id)))
                            #
                            already_seen :=  is1::add (*already_seen, a1.id);

                            do_node  a1;
                        fi;

                        if (not (is1::member (*already_seen, a2.id)))
                            #
                            already_seen :=  is1::add (*already_seen, a2.id);

                            do_node  a2;
                        fi;
                    };


                fun do_edge ((a1, t2, a3): Edge)
                    =
                    {
                        if (not (is1::member (*already_seen, a1.id)))
                            #
                            already_seen :=  is1::add (*already_seen, a1.id);

                            do_node  a1;
                        fi;

                        if (not (is1::member (*already_seen, a3.id)))
                            #
                            already_seen :=  is1::add (*already_seen, a3.id);

                            do_node  a3;
                        fi;
                    };
            end;

        fun tags_apply                                                          # Apply do_tag to all Tags in Graph. 
              ( { index_123of3,
                  ...
                }:      Graph
              )
              (do_tag: Tag -> Void)
            =
            {   es::apply  do_edge           index_123of3;
            }
            where
                already_seen =  REF is1::empty;
                #
                fun do_edge ((a1, t2, a3): Edge)
                    =
                    {
                        if (not (is1::member (*already_seen, t2.id)))
                            #
                            already_seen :=  is1::add (*already_seen, t2.id);

                            do_tag  t2;
                        fi;
                    };
            end;

    };
end;





Comments and suggestions to: bugs@mythryl.org

PreviousUpNext