% GENERAL GRAPH THEORY CLASS
%
% CAVE: Any special application oriented stuff 
% should be outside here

classdef Graph < DiGraph
 
methods

    % ---
% Constructor
% ---
function G = Graph(V, E)
     
    if nargin == 1
        % ---
        % this uses an imput grpaph
        % to create a new graph
        % ---
        G = Graph.copy(V);
        
    elseif nargin == 2
        
        if numel(V) == size(V,2)
            G.V = V;
        else
            G.V = V';
        end

        G.E = E;
    end
end
function w = edge(G, V1, V2)
    
    sm = min([V1, V2]);
    bg = max([V1, V2]);
    
    w = G.E(sm, bg);
end

% ---      
% add an edge
% the undirected case: G.E is triangular
% ---
function add_edge(G, V1, V2, weight)
    
    G.add_node(V1);
	G.add_node(V2);
    
    sm = min([V1, V2]);
    bg = max([V1, V2]);
    
    if  G.E(sm, bg) == 0

        % save weight
        G.E(sm, bg) = weight;
    else
        
        join_edge(G, V1, V2, weight);
    end
end

% remove an edge
function remove_edge(G, V1, V2)
    
    sm = min([V1, V2]);
    bg = max([V1, V2]);
    
    G.E(sm,bg) = 0;
end

% ---
% sets the edge without considering earlier weights
% ---
function set_edge(G, V1, V2, weight)
    
    sm = min([V1, V2]);
    bg = max([V1, V2]);
    
    G.E(sm, bg) = weight;
    
end

% ---
% Graph-specific functions
% in the undirected case, these are the same
% ---
function c = children(G, Vid)

    c = [find(G.E(Vid,:) > 0) find(G.E(:,Vid) > 0)'];
end

function p = parents(G, Vid)

    p = [find(G.E(Vid,:) > 0) find(G.E(:,Vid) > 0)'];
end

% ---
% Vertex Degree
% ---
function out = degree(G, V)
    
    out = sum(G.E(V,:) > 0) + sum(G.E(:,V) > 0);
end

% ---
% Vertex Degree
% ---
function out = degree_in(G, V)
    
    out = sum(G.E(V,:) > 0) + sum(G.E(:,V) > 0);
end

% ---
% Vertex Degree
% ---
function out = degree_out(G, V)
    
    out = sum(G.E(:,V) > 0) + sum(G.E(:,V) > 0);
end

end


methods (Static)
   function G = copy(Gin)
       
       if strcmp(class(Gin), 'Graph')
           
           G = Graph(Gin.V, Gin.E);
           G.Vlbl = Gin.Vlbl;
           
       else
           warning ('cannot copy graph, casting instead')
           G = Graph.cast(Gin);
       end
        
   end
   
   function G = cast(Gin)
       
        % ---
        % this uses an imput grpaph
        % to create a new digraph
        % ---
        G = Graph();
        
        % ---
        % Add the input graph to the empty one
        % ---
        G.add_graph(Gin);
   end
end
end