Graph.cs

212 lines | 5.7 kB Blame History Raw Download
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Graph.Simple.Entities.Base;
using Graph.Simple.Entities.Internal;

namespace Graph.Simple.Entities
{
    public class Graph
    {
        #region

        /// <summary>
        /// Коллекция вершин
        /// key - ID
        /// value - вершина графа
        /// </summary>
        private Dictionary<int, GraphNode> _Nodes;
        private Dictionary<int, Node> __Nodes;

        public IReadOnlyDictionary<int, Node> Nodes => __Nodes;



        /// <summary>
        /// Коллекция ребер
        /// key - ID
        /// value - Ребро
        /// </summary>
        private Dictionary<int, GraphEdge> _Edges;
        private Dictionary<int, Edge> __Edges;// { private set; get; }

        public IReadOnlyDictionary<int, Edge> Edges => __Edges;



        private int _MaxNodeID = -1;
        private int GetNextNodeID => ++_MaxNodeID;


        private int _MaxEdgeID = -1;
        private int GetNextEdgeID => ++_MaxEdgeID;


        #endregion


        private void Init()
        {
            _Nodes = new Dictionary<int, GraphNode>();
            __Nodes = new Dictionary<int, Node>();

            _Edges = new Dictionary<int, GraphEdge>();
            __Edges = new Dictionary<int, Edge>();
        }

        public Graph()
        {
            Init();
        }


        #region

        /// <summary>
        /// Метод проверяет принадлежат ли данные элементы графу
        /// </summary>
        /// <param name="elements"></param>
        /// <returns></returns>
        public bool ContainsElements(params BaseGraphUnit[] elements)
        {
            foreach (var elem in elements)
            {
                if (elem == null)
                    return false;

                if (elem.Graph != this)
                    return false;
            }

            return true;
        }


        public Node AddNode()
        {
            var NewNode = new GraphNode(GetNextNodeID, this);

            _Nodes.Add(NewNode.ID, NewNode);
            __Nodes.Add(NewNode.ID, NewNode);

            return NewNode;
        }


        public void RemoveNode(Node node, bool WithEdge = false)
        {
            if (!ContainsElements(node))
                throw new Exception("_Graph.RemoveNode | _Graph not contains this node");

            GraphNode graphNode = node as GraphNode;

            if (!WithEdge && graphNode.Inputs.Count != 0)
                throw new Exception("_Graph.RemoveNode | Node contact with edge(s), but WithEdge set false");

            while (graphNode.Outputs.Count != 0)
            {
                RemoveEdge(graphNode.Outputs.First());
            }

            while (graphNode.Inputs.Count != 0)
            {
                RemoveEdge(graphNode.Inputs.First());
            }

            _Nodes.Remove(node.ID);
            __Nodes.Remove(node.ID);
        }

        /// <summary>
        /// Добавить ребро
        /// </summary>
        /// <param name="node_start">Начало</param>
        /// <param name="node_end">Окончание</param>
        /// <param name="Bidir">Создать двунаправленное</param>
        /// <param name="weight">Вес</param>
        /// <returns></returns>
        public Edge AddEdge(Node node_start, Node node_end, float widht = 1)
        {
            if (!ContainsElements(node_start, node_end))
                throw new Exception("_Graph.AddEdge | _Graph not contains node_start or/and node_end");

            if (/*!UseCicleEdge &&*/ node_start == node_end)
                throw new Exception("_Graph.AddEdge | node_start == node_end and UseCicleEdge == false");

            var graphnode_start = node_start as GraphNode;
            var graphnode_end = node_end as GraphNode;

            if (/*UseMultiEdge &&*/ graphnode_start.Outputs.FirstOrDefault(e => e.End == node_end) != null
                || (graphnode_start.Inputs.FirstOrDefault(e => e.Start == node_end) != null))
                throw new Exception("_Graph.AddEdge | Edge already exists (maybe bidir <->), if change remove and create");

            GraphEdge NewEdge = null;


            if (widht < 0)
                throw new Exception();
            NewEdge = new GraphEdge(GetNextEdgeID, this, node_start, node_end)
            {
                Weight = widht
            };


            graphnode_start.Outputs.Add(NewEdge);
            graphnode_end.Inputs.Add(NewEdge);

            _Edges.Add(NewEdge.ID, NewEdge);
            __Edges.Add(NewEdge.ID, NewEdge);

            return NewEdge;
        }

        /// <summary>
        /// Удаляет ребро и обратное ему
        /// </summary>
        /// <param name="edge"></param>
        public void RemoveEdge(Edge edge)
        {
            if (!ContainsElements(edge))
                throw new Exception("");

            ((GraphNode)edge.Start).Outputs.Remove(edge);
            ((GraphNode)edge.End).Inputs.Remove(edge);

            _Edges.Remove(edge.ID);
            __Edges.Remove(edge.ID);
        }


        public Edge GetEdge(Node node1, Node node2)
        {
            if (!ContainsElements(node1, node2))
                throw new Exception("Graph.GetEdge | graph not contains nodes");

            return Edges.Values.FirstOrDefault(e => e.Start.ID == node1.ID && e.End.ID == node2.ID);
        }

        #endregion


        #region     

        public void AllClear()
        {
            _Edges.Clear();
            __Edges.Clear();
            _MaxEdgeID = -1;


            _Nodes.Clear();
            __Nodes.Clear();
            _MaxNodeID = -1;
        }

        #endregion

    }
}