// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Daniel Liles (Los Alamos National Laboratory), Jonathan Berry
// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.
// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.
// Last File Update: 21-Feb-1998
// 
#include <stdio.h>
#include <strstream.h>
#include <LINK/graph/Graph.h>
#include <LINK/graph/HyperGraph.h>
#include <LINK/graph/Vertex.h>
#include <LINK/graph/Edge.h>
#include <LINK/graph/Attribute.h>
#include <LINK/basic/Set.h>
#include <LINK/basic/DJSet.h>
#include <LINK/basic/BinaryHeap.h>
#include <LINK/algorithm/Algorithms.h>
//
// minimum spanning tree, Kruskal's algorithm
//
Set<Edge*> 
Kruskal(Graph* graph, char* graph_view) 
{
    MHyperGraph animation_tree;  // used *only* for animations
    Vertex *v1;
    Vertex *v2;
    Vertex *p1;
    Vertex *p2;
    Vertex *vertex1, *vertex2;
    Edge *edge;
    //****************************************************************
    //** not used right now, but a good example of pts to member fcns*
    //****************************************************************
    DJSet* (DJSet::*find_set)();
    void   (DJSet::*union_with)(DJSet*);
    if (graph_view) {
	find_set   = &DJSet::findSetLinked;
	union_with = &DJSet::unionWithLinked;
    } else {
	find_set   = &DJSet::findSet;
	union_with = &DJSet::unionWith;
    }
    //****************************************************************
    ResetAttributes(graph);
 
    // temporary attributes used by the algorithm
    Attribute<DJSet*> dj_set(graph, "DJSet", (DJSet*) 0);
    
    int edgeCount = graph->size();
    // make disjoint sets
    Vertex** vertices = graph->vertexStart();
    int order = graph->order();
    int i;
    for (i=0; i < order; i++) {
        DJSet* djset = new DJSet(vertices[i]->name());
        setAttribute((GraphObject*) vertices[i], "DJSet", djset);
	if (graph_view) {
		animation_tree.addVertex(vertices[i]->name());
		char *color = RandomTkColor();
    		animateVertex(vertices[i], graph_view, "color", 
					      "color", INIT_STEP, color,
				FLASH, 0, 0);
	}
    }
    // sort edges by weight by using a binary heap
    BinaryHeap<double,Edge*> heap(edgeCount);
    const MSet<Edge*>& edges = graph->edges();
    Iterator<Edge*> get_edge(&edges);
    while (get_edge(edge)) {
        double weight;
        getAttribute((GraphObject*) edge, "weight", weight);
	setAttribute((GraphObject*) edge, "mark", 0);
        heap.insert(weight, edge);
	if (graph_view) {
		ostrstream oss;
		char *wgt;
		oss << weight << ends;
		wgt = oss.str();
    		animateEdge(edge, graph_view, "label", 
					      "edge-label", INIT_STEP, wgt,
				FLASH, 0, 0);
		// must incorporate LINK_DYNAMIC
	}
    }
    for (i = 0; i < edgeCount; i++) {
        edge = (heap.extractMin())->item;
        Iterator<Vertex*> get_Vertices(edge->vertices());
        get_Vertices(vertex1);
        while (get_Vertices(vertex2)) {
            DJSet* set_a;
            DJSet* set_b;
            getAttribute((GraphObject*) vertex1, "DJSet", set_b);
            getAttribute((GraphObject*) vertex2, "DJSet", set_a);
            DJSet* parent_a = set_a->findSet();
            DJSet* parent_b = set_b->findSet();
	    if (graph_view) {
	    	v1 = graph->findVertexByName(set_a->id());
	    	v2 = graph->findVertexByName(set_b->id());
	    	p1 = graph->findVertexByName(parent_a->id());
	    	p2 = graph->findVertexByName(parent_b->id());
	    }
            if (parent_a != parent_b) {
	        parent_a->unionWith(parent_b);
	        setAttribute((GraphObject*) edge, "mark", 1);
		if (graph_view) {
			char *color;
			int i;
            		DJSet* next, *next_p;
	
	        	getAttribute((GraphObject*) p1, "color", color);
			for (i=0; i<order; i++) {
            			getAttribute((GraphObject*) vertices[i], 
						"DJSet", next);
            			DJSet* next_p = next->findSet();
				if ((next_p == parent_a)||(next_p == parent_b)) 
					animateVertex(vertices[i], graph_view,
						"color", "color", 
					        INIT_STEP, (char *)color, 
						FLASH, 0, 0);
			}
			animateEdge(edge, graph_view,"color", "color", 
					      ALG_STEP, (char *)"red");
			Set<Vertex*> new_anim_edge;
			Vertex **anim_v = animation_tree.vertexStart();
	    		Vertex *v1 = graph->findVertexByName(set_a->id());
	    		Vertex *v2 = graph->findVertexByName(set_b->id());
			int rank_v1 = graph->rank(v1);
			int rank_v2 = graph->rank(v2);
			new_anim_edge.insert(anim_v[rank_v1]);
			new_anim_edge.insert(anim_v[rank_v2]);
			animation_tree.addEdge(new_anim_edge);
		}
            } else if (graph_view) {
		animateEdge(edge, graph_view,"color", "color",
					      INIT_STEP, (char *)"black",
					      JIGGLE, 5, 10);
		Edge* e;
		Sequence<Edge*> cycle_edges;
		Vertex *v1, *v2;
		DJSet* p_a, *p_b;
		p_a = set_a->parent();
		p_b = set_b->parent();
	    	Vertex *av1, *av2;
	    	av1 = animation_tree.findVertexByName(set_a->id());
	    	av2 = animation_tree.findVertexByName(set_b->id());
		BreadthFirstSearch(&animation_tree, av1, 0);
		Vertex *oops;
		getAttribute((GraphObject*) av2, "pred", oops);
		cycle_edges = animation_tree.treePath(av1, av2);
		Iterator<Edge*> get_edge(&cycle_edges);
		int width;
		while (get_edge(e)) {
			Collection<Vertex*>* verts = e->vertices();
			Edge *ge = graph->isEdge(*verts);
			getAttribute((GraphObject*) ge, "width", width);
			animateEdge(ge, graph_view,"width", "width",
					      INIT_STEP, width+4,
					      FLASH, 0, 0);
		}
		get_edge.reset();
		while (get_edge(e)) {
			Collection<Vertex*>* verts = e->vertices();
			Edge *ge = graph->isEdge(*verts);
			getAttribute((GraphObject*) ge, "width", width);
			animateEdge(ge, graph_view,"width", "width",
					      INIT_STEP, width,
					      JIGGLE, 5, 10);
		}
		get_edge.reset();
		while (get_edge(e)) {
			Collection<Vertex*>* verts = e->vertices();
			Edge *ge = graph->isEdge(*verts);
			getAttribute((GraphObject*) ge, "width", width);
			animateEdge(ge, graph_view,"width", "width",
					      INIT_STEP, width-4,
					      JIGGLE, 0, 0);
		}
		animateEdge(edge, graph_view,"color", "color",
					      ALG_STEP, (char *)"black");
	    }
            vertex1 = vertex2;
        }
    }
    int ord = graph->order();
    for (int j=0; j < ord; j++) {
        DJSet* djset;
        getAttribute((GraphObject*) vertices[j], "DJSet", djset);
        //delete djset;  LEAK!!
    }
    Set<Edge*> result;
    get_edge.reset();
    while (get_edge(edge)) {
        int tree_edge;
        getAttribute((GraphObject*) edge, "mark", tree_edge);
	if (tree_edge)
		result.insert(edge);
    }
    char *color;
    if (graph_view) {
    	for (i=0; i < order; i++) {
		getAttribute((GraphObject*) vertices[i], "color", color);
		// delete(color);  LEAK!!
		setAttribute((GraphObject*) vertices[i], "color",(char*)"blue");
	}
    }
    return result;
}
