%   File   : dgraphs.yap
%   Author : Vitor Santos Costa
%   Updated: 2006
%   Purpose: Directed Graph Processing Utilities.

:- module( wundgraphs,
	   [
	    wundgraph_new/1,
	    wundgraph_add_edge/5,
	    wundgraph_add_edges/3,
	    wundgraph_add_vertex/3,
	    wundgraph_add_vertices/3,
	    wundgraph_del_edge/5,
	    wundgraph_del_edges/3,
	    wundgraph_del_vertex/3,
	    wundgraph_del_vertices/3,
	    wundgraph_edge/4,
	    wundgraph_edges/2,
	    wundgraph_vertices/2,
	    wundgraph_neighbors/3,
	    wundgraph_neighbours/3,
	    wdgraph_to_wundgraph/2,
	    wundgraph_to_wdgraph/2,
	    undgraph_to_wundgraph/2,
	    wundgraph_to_undgraph/2,
	    wundgraph_min_tree/3,
	    wundgraph_max_tree/3,
	    wundgraph_min_path/5,
	    wundgraph_min_paths/3,
	    wundgraph_max_path/5,
	    wundgraph_path/3]).

:- use_module( library(wdgraphs),
	   [
	    wdgraph_new/1,
	    wdgraph_add_edge/5,
	    wdgraph_add_edges/3,
	    wdgraph_add_vertex/3,
	    wdgraph_add_vertices/3,
	    wdgraph_del_edge/5,
	    wdgraph_del_edges/3,
	    wdgraph_del_vertex/3,
	    wdgraph_del_vertices/3,
	    wdgraph_edge/4,
	    wdgraph_edges/2,
	    wdgraph_to_dgraph/2,
	    dgraph_to_wdgraph/2,
	    wdgraph_symmetric_closure/2,
	    wdgraph_min_path/5,
	    wdgraph_min_paths/3,
	    wdgraph_max_path/5,
	    wdgraph_path/3]).

:- use_module( library(dgraphs),
	   [
            dgraph_vertices/2,
	    dgraph_neighbors/3
	]).

:- use_module(library(rbtrees),
	[  
         rb_new/1,
         rb_delete/4,
	 rb_partial_map/4,
	 rb_visit/2,
	 rb_insert/4,
	 rb_lookup/3
	]).

:- use_module(library(lists),
	[  
         reverse/2
	]).

wundgraph_new(Vertices) :-
	wdgraph_new(Vertices).

wundgraph_add_edge(V1,V2,K,Vs0,Vs2) :-
	wdgraphs:wdgraph_new_edge(V1,V2,K,Vs0,Vs1),
	wdgraphs:wdgraph_new_edge(V2,V1,K,Vs1,Vs2).
	
wundgraph_add_edges(Edges) -->
	{ dup_edges(Edges, DupEdges) },
	wdgraph_add_edges(DupEdges).

dup_edges([],[]).
dup_edges([E1-(E2-K)|Edges], [E1-(E2-K),E2-(E1-K)|DupEdges]) :-
	dup_edges(Edges, DupEdges).

wundgraph_add_vertices(Vs) -->
	wdgraph_add_vertices(Vs).

wundgraph_add_vertex(V) -->
	wdgraph_add_vertex(V).

wundgraph_edges(Vs,Edges) :-
	wdgraph_edges(Vs,DupEdges),
	remove_dups(DupEdges,Edges).

remove_dups([],[]).
remove_dups([V1-(V2-K)|DupEdges],NEdges) :- V1 @< V2, !,
	NEdges = [V1-(V2-K)|Edges],
	remove_dups(DupEdges,Edges).
remove_dups([_|DupEdges],Edges) :-
	remove_dups(DupEdges,Edges).

wundgraph_vertices(Vs,Vertices) :-
	dgraph_vertices(Vs,Vertices).

wundgraph_neighbours(V,Vertices,Children) :-
	dgraph_neighbours(V,Vertices,Children0),
	(
	    del_me(Children0,V,Children)
	->
	    true
	;
	    Children = Children0
	).
wundgraph_neighbors(V,Vertices,Children) :-
	dgraph_neighbors(V,Vertices,Children0),
	(
	    del_me(Children0,V,Children)
	->
	    true
	;
	    Children = Children0
	).

del_me([], _, []).
del_me([K-_|Children], K1, NewChildren) :-
	( K == K1 ->
	    Children = NewChildren
	;
	    K @< K1 ->
	    NewChildren = [K|ChildrenLeft],
	    del_me(Children, K1, ChildrenLeft)
	;
	    NewChildren = [K|MoreChildren],
	    compact(Children, MoreChildren)
	).

wundgraph_del_edge(V1,V2,K,Vs0,VsF) :-
	wdgraph_del_edge(V1,V2,K,Vs0,Vs1),
	wdgraph_del_edge(V2,V1,K,Vs1,VsF).

wundgraph_del_edges(Edges) -->
	{
	  dup_edges(Edges,DupEdges)
	},
	wdgraph_del_edges(DupEdges).

wundgraph_del_vertex(V, Vs0, Vsf) :-
	rb_delete(Vs0, V, BackEdges, Vsi),
	del_and_compact(BackEdges,V,BackVertices),
	rb_partial_map(Vsi, BackVertices, del_edge(V), Vsf).

del_and_compact([], _, []).
del_and_compact([K-_|Children], K1, NewChildren) :-
	( K == K1 ->
	    compact(Children, NewChildren)
	;
	    K @< K1 ->
	    NewChildren = [K|ChildrenLeft],
	    del_and_compact(Children, K1, ChildrenLeft)
	;
	    NewChildren = [K|CompactChildren],
	    compact(Children, CompactChildren)
	).

compact([], []).
compact([K-_|Children], [K|CompactChildren]) :-
	compact(Children, CompactChildren).


wundgraph_del_vertices(Vs) -->
	wdgraph_del_vertices(Vs).

del_edge(_, [], []).
del_edge(K1, [K-W|Children], NewChildren) :-
	( K == K1 ->
	    Children = NewChildren
	;
	    K @< K1 ->
	    NewChildren = [K-W|ChildrenLeft],
	    del_edge(K1, Children, ChildrenLeft)
	;
	    NewChildren = [K-W|Children]
	).

wundgraph_edge(N1, N2, K, G) :-
	wdgraph_edge(N1, N2, K, G).

wdgraph_to_wundgraph(G, U) :-
	wdgraph_symmetric_closure(G, U).

wundgraph_to_wdgraph(G, G).

wundgraph_min_path(V1, V2, WGraph, Path, Cost) :-
	wdgraph_min_path(V1, V2, WGraph, Path, Cost).

wundgraph_max_path(V1, V2, WGraph, Path, Cost) :-
	wdgraph_max_path(V1, V2, WGraph, Path, Cost).

wundgraph_min_paths(V1, WGraph, T) :-
	wdgraph_min_paths(V1, WGraph, T).

wundgraph_path(V, WG, P) :-
	wdgraph_path(V, WG, P).

undgraph_to_wundgraph(G1, G2) :-
	dgraph_to_wdgraph(G1, G2).

wundgraph_to_undgraph(G1, G2) :-
	    wdgraph_to_dgraph(G1, G2).


% simplistic algorithm to build a minimal spanning tree.
% Just sort edges and then walk over each one.

wundgraph_min_tree(G, T, C) :-
	rb_visit(G, Els0),
	mk_list_of_edges(Els0, Edges),
	keysort(Edges, SortedEdges),
	rb_new(V0),
	rb_new(T0),
	add_sorted_edges(SortedEdges, V0, TreeEdges, 0, C),
	wundgraph_add_edges(TreeEdges, T0, T).

wundgraph_max_tree(G, T, C) :-
	rb_visit(G, Els0),
	mk_list_of_edges(Els0, Edges),
	keysort(Edges, SortedEdges),
	reverse(SortedEdges, ReversedEdges),
	rb_new(V0),
	rb_new(T0),
	add_sorted_edges(ReversedEdges, V0, TreeEdges, 0, C),
	wundgraph_add_edges(TreeEdges, T0, T).

mk_list_of_edges([], []).
mk_list_of_edges([V-Els|Els0], Edges) :-
	add_neighbs(Els, V, Edges, Edges0),
	mk_list_of_edges(Els0, Edges0).

add_neighbs([], _, Edges, Edges).
add_neighbs([V-W|Els], V0, [W-(V0-V)|Edges], Edges0) :-
	V0 @< V, !,
	add_neighbs(Els, V0, Edges, Edges0).
add_neighbs([_|Els], V0, Edges, Edges0) :-
	add_neighbs(Els, V0, Edges, Edges0).

	
add_sorted_edges([], _, [], C, C).
add_sorted_edges([W-(V0-V)|SortedEdges], T0, NewTreeEdges, C0, C) :-
	( rb_lookup(V0, Component, T0) ->
	    ( rb_lookup(V, Component1, T0) ->
		( Component \== Component1 ->
		    /* edge that links two separate sub-trees (components) */
	          Component = Component1,
		  Ti = T0
	        ;
	        /* same component, can't add edge */
	          fail
	        )
	      ;
              /* V is new */
	      rb_insert(T0, V, Component, Ti)
	    )
	;
	    ( rb_lookup(V, Component1, T0) ->
              /* V0 is new */
	      rb_insert(T0, V0, Component1, Ti)
	    ;
              /* new edges, new tree */
	      rb_insert(T0, V0, NewComponent, T1),
	      rb_insert(T1, V, NewComponent, Ti)
	    )
        ),
	!,
	NewTreeEdges = [(V0-(V-W)),(V-(V0-W))|TreeEdges],
	Ci is C0+W,
	add_sorted_edges(SortedEdges, Ti, TreeEdges, Ci, C).
add_sorted_edges([_|SortedEdges],  T0, NewTreeEdges, C0, C) :-
	add_sorted_edges(SortedEdges, T0, NewTreeEdges, C0, C).