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

:- module( undgraphs,
	   [
	    undgraph_new/1,
	    undgraph_add_edge/4,
	    undgraph_add_edges/3,
	    undgraph_add_vertex/3,
	    undgraph_add_vertices/3,
	    undgraph_del_edge/4,
	    undgraph_del_edges/3,
	    undgraph_del_vertex/3,
	    undgraph_del_vertices/3,
	    undgraph_edge/3,
	    undgraph_edges/2,
	    undgraph_vertices/2,
	    undgraph_neighbors/3,
	    undgraph_neighbours/3,
	    undgraph_complement/2,
	    dgraph_to_undgraph/2,
	    undgraph_min_tree/2]).

:- use_module( library(dgraphs),
	   [
	    dgraph_new/1,
	    dgraph_add_edge/4,
	    dgraph_add_edges/3,
	    dgraph_add_vertex/3,
	    dgraph_add_vertices/3,
	    dgraph_del_edge/4,
	    dgraph_del_edges/3,
	    dgraph_del_vertex/3,
	    dgraph_del_vertices/3,
	    dgraph_edge/3,
	    dgraph_edges/2,
	    dgraph_vertices/2,
	    dgraph_neighbors/3,
	    dgraph_neighbours/3,
	    dgraph_complement/2,
	    dgraph_symmetric_closure/2]).

:- use_module(library(wundgraphs), [
            undgraph_to_wundgraph/2,
	    wundgraph_min_tree/3,
	    wundgraph_max_tree/3,
	    wundgraph_to_undgraph/2]).

:- use_module(library(ordsets),
	[ ord_del_element/3,
	  ord_union/3,
	  ord_subtract/3]).

:- use_module(library(rbtrees),
	[  rb_delete/4,
	   rb_partial_map/4
	]).

undgraph_new(Vertices) :-
	dgraph_new(Vertices).

undgraph_add_edge(V1,V2,Vs0,Vs2) :-
	dgraphs:dgraph_new_edge(V1,V2,Vs0,Vs1),
	dgraphs:dgraph_new_edge(V2,V1,Vs1,Vs2).
	
undgraph_add_edges(Edges) -->
	{ dup_edges(Edges, DupEdges) },
	dgraph_add_edges(DupEdges).

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

undgraph_add_vertices([]) --> [].
undgraph_add_vertices([V|Vs]) -->
	dgraph_add_vertex(V),
	undgraph_add_vertices(Vs).

undgraph_add_vertex(V) -->
	dgraph_add_vertex(V).

undgraph_edges(Vs,Edges) :-
	dgraph_edges(Vs,DupEdges),
	remove_dups(DupEdges,Edges).

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

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

undgraph_neighbours(V,Vertices,Children) :-
	dgraph_neighbours(V,Vertices,Children0),
	(
	    ord_del_element(Children0,V,Children)
	->
	    true
	;
	    Children = Children0
	).
undgraph_neighbors(V,Vertices,Children) :-
	dgraph_neighbors(V,Vertices,Children0),
	(
	    ord_del_element(Children0,V,Children)
	->
	    true
	;
	    Children = Children0
	).

undgraph_complement(Vs0,VsF) :-
	dgraph_complement(Vs0,VsF).

undgraph_del_edge(V1,V2,Vs0,VsF) :-
	dgraph_del_edge(V1,V2,Vs0,Vs1),
	dgraph_del_edge(V2,V1,Vs1,VsF).

undgraph_del_edges(Edges) -->
	{
	  dup_edges(Edges,DupEdges)
	},
	dgraph_del_edges(DupEdges).

undgraph_del_vertex(V, Vs0, Vsf) :-
	rb_delete(Vs0, V, BackEdges, Vsi),
	(
	    ord_del_element(BackEdges,V,RealBackEdges)
	->
	    true
	;
	    BackEdges = RealBackEdges
	),
	rb_partial_map(Vsi, RealBackEdges, del_edge(V), Vsf).

undgraph_del_vertices(Vs) -->
	{ sort(Vs,SortedVs) },
	delete_all(SortedVs, [], BackEdges),
	{ ord_subtract(BackEdges, SortedVs, TrueBackEdges) },
	delete_remaining_edges(SortedVs, TrueBackEdges).

% it would be nice to be able to delete a set of elements from an RB tree
% but I don't how to do it yet.
delete_all([], BackEdges, BackEdges) --> [].
delete_all([V|Vs], BackEdges0, BackEdgesF, Vs0,Vsf) :-
	rb_delete(Vs0, V, NewEdges, Vsi),
	ord_union(NewEdges,BackEdges0,BackEdgesI),
	delete_all(Vs, BackEdgesI ,BackEdgesF, Vsi,Vsf).

delete_remaining_edges(SortedVs, TrueBackEdges, Vs0,Vsf) :-
	rb_partial_map(Vs0, TrueBackEdges, del_edges(SortedVs), Vsf).

del_edges(ToRemove,E0,E) :-
	ord_subtract(E0,ToRemove,E).

del_edge(ToRemove,E0,E) :-
	ord_del_element(E0,ToRemove,E).

dgraph_to_undgraph(G, U) :-
	dgraph_symmetric_closure(G, U).

undgraph_edge(N1, N2, G) :-
	dgraph_edge(N1, N2, G).


undgraph_min_tree(G, T) :-
	undgraph_to_wundgraph(G, WG),
	wundgraph_min_tree(WG, WT, _),
	wundgraph_to_undgraph(WT, T).

undgraph_max_tree(G, T) :-
	undgraph_to_wundgraph(G, WG),
	wundgraph_max_tree(WG, WT, _),
	wundgraph_to_undgraph(WT, T).