The Algorithms logo
The Algorithms
Acerca deDonar

Directed Weighted Graph Via Adjacency Matrix

using System;
using System.Collections.Generic;

namespace DataStructures.Graph;

/// <summary>
///     Implementation of the directed weighted graph via adjacency matrix.
/// </summary>
/// <typeparam name="T">Generic Type.</typeparam>
public class DirectedWeightedGraph<T> : IDirectedWeightedGraph<T>
{
    /// <summary>
    ///     Capacity of the graph, indicates the maximum amount of vertices.
    /// </summary>
    private readonly int capacity;

    /// <summary>
    ///     Adjacency matrix which reflects the edges between vertices and their weight.
    ///     Zero value indicates no edge between two vertices.
    /// </summary>
    private readonly double[,] adjacencyMatrix;

    /// <summary>
    ///     Initializes a new instance of the <see cref="DirectedWeightedGraph{T}"/> class.
    /// </summary>
    /// <param name="capacity">Capacity of the graph, indicates the maximum amount of vertices.</param>
    public DirectedWeightedGraph(int capacity)
    {
        ThrowIfNegativeCapacity(capacity);

        this.capacity = capacity;
        Vertices = new Vertex<T>[capacity];
        adjacencyMatrix = new double[capacity, capacity];
        Count = 0;
    }

    /// <summary>
    ///     Gets list of vertices of the graph.
    /// </summary>
    public Vertex<T>?[] Vertices { get; private set; }

    /// <summary>
    ///     Gets current amount of vertices in the graph.
    /// </summary>
    public int Count { get; private set; }

    /// <summary>
    ///     Adds new vertex to the graph.
    /// </summary>
    /// <param name="data">Data of the vertex.</param>
    /// <returns>Reference to created vertex.</returns>
    public Vertex<T> AddVertex(T data)
    {
        ThrowIfOverflow();
        var vertex = new Vertex<T>(data, Count, this);
        Vertices[Count] = vertex;
        Count++;
        return vertex;
    }

    /// <summary>
    ///     Creates an edge between two vertices of the graph.
    /// </summary>
    /// <param name="startVertex">Vertex, edge starts at.</param>
    /// <param name="endVertex">Vertex, edge ends at.</param>
    /// <param name="weight">Double weight of an edge.</param>
    public void AddEdge(Vertex<T> startVertex, Vertex<T> endVertex, double weight)
    {
        ThrowIfVertexNotInGraph(startVertex);
        ThrowIfVertexNotInGraph(endVertex);

        ThrowIfWeightZero(weight);

        var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index];

        ThrowIfEdgeExists(currentEdgeWeight);

        adjacencyMatrix[startVertex.Index, endVertex.Index] = weight;
    }

    /// <summary>
    ///     Removes vertex from the graph.
    /// </summary>
    /// <param name="vertex">Vertex to be removed.</param>
    public void RemoveVertex(Vertex<T> vertex)
    {
        ThrowIfVertexNotInGraph(vertex);

        Vertices[vertex.Index] = null;
        vertex.SetGraphNull();

        for (var i = 0; i < Count; i++)
        {
            adjacencyMatrix[i, vertex.Index] = 0;
            adjacencyMatrix[vertex.Index, i] = 0;
        }

        Count--;
    }

    /// <summary>
    ///     Removes edge between two vertices.
    /// </summary>
    /// <param name="startVertex">Vertex, edge starts at.</param>
    /// <param name="endVertex">Vertex, edge ends at.</param>
    public void RemoveEdge(Vertex<T> startVertex, Vertex<T> endVertex)
    {
        ThrowIfVertexNotInGraph(startVertex);
        ThrowIfVertexNotInGraph(endVertex);
        adjacencyMatrix[startVertex.Index, endVertex.Index] = 0;
    }

    /// <summary>
    ///     Gets a neighbors of particular vertex.
    /// </summary>
    /// <param name="vertex">Vertex, method gets list of neighbors for.</param>
    /// <returns>Collection of the neighbors of particular vertex.</returns>
    public IEnumerable<Vertex<T>?> GetNeighbors(Vertex<T> vertex)
    {
        ThrowIfVertexNotInGraph(vertex);

        for (var i = 0; i < Count; i++)
        {
            if (adjacencyMatrix[vertex.Index, i] != 0)
            {
                yield return Vertices[i];
            }
        }
    }

    /// <summary>
    ///     Returns true, if there is an edge between two vertices.
    /// </summary>
    /// <param name="startVertex">Vertex, edge starts at.</param>
    /// <param name="endVertex">Vertex, edge ends at.</param>
    /// <returns>True if edge exists, otherwise false.</returns>
    public bool AreAdjacent(Vertex<T> startVertex, Vertex<T> endVertex)
    {
        ThrowIfVertexNotInGraph(startVertex);
        ThrowIfVertexNotInGraph(endVertex);

        return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0;
    }

    /// <summary>
    /// Return the distance between two vertices in the graph.
    /// </summary>
    /// <param name="startVertex">first vertex in edge.</param>
    /// <param name="endVertex">secnod vertex in edge.</param>
    /// <returns>distance between the two.</returns>
    public double AdjacentDistance(Vertex<T> startVertex, Vertex<T> endVertex)
    {
        if (AreAdjacent(startVertex, endVertex))
        {
            return adjacencyMatrix[startVertex.Index, endVertex.Index];
        }

        return 0;
    }

    private static void ThrowIfNegativeCapacity(int capacity)
    {
        if (capacity < 0)
        {
            throw new InvalidOperationException("Graph capacity should always be a non-negative integer.");
        }
    }

    private static void ThrowIfWeightZero(double weight)
    {
        if (weight.Equals(0.0d))
        {
            throw new InvalidOperationException("Edge weight cannot be zero.");
        }
    }

    private static void ThrowIfEdgeExists(double currentEdgeWeight)
    {
        if (!currentEdgeWeight.Equals(0.0d))
        {
            throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}");
        }
    }

    private void ThrowIfOverflow()
    {
        if (Count == capacity)
        {
            throw new InvalidOperationException("Graph overflow.");
        }
    }

    private void ThrowIfVertexNotInGraph(Vertex<T> vertex)
    {
        if (vertex.Graph != this)
        {
            throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}.");
        }
    }
}