C C T B H P N
click to close

CsGraph.cs, CsGraph.txt Code folder, Generics.htm

Generics support parameterizing classes with types that are unspecified until an application is compiled.

///////////////////////////////////////////////////////////////////////////
// CsGraph.cs - Generic edge, node and directed graph classes            //
// ver 1.1                                                               //
// Jim Fawcett, CSE681 - Software Modeling and Analysis, Spring 2018     //
///////////////////////////////////////////////////////////////////////////
/*
 * Maintenance History:
 * --------------------
 * ver 1.1 : 23 Aug 2018
 * - changed definition of CsNode<V,E>
 * - changed logic and return type of CsGraph<V,E>::walk
 * ver 1,0 : 18 Aug 2018
 * - first release
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CsGraph
{
  /////////////////////////////////////////////////////////////////////////
  // CsNode<V,E> class

  public class Edge<V, E> // holds child node and instance of edge type E
  {
    public CsNode<V, E> targetNode { get; set; } = null;
    public E edgeValue { get; set; }

    public Edge(CsNode<V,E> node, E value)
    {
      targetNode = node;
      edgeValue = value;
    }
  };

  public class CsNode<V,E>
  {
    public V nodeValue { get; set; }
    public string name { get; set; }
    public List<Edge<V, E>> children { get; set; }
    public bool visited { get; set; }

    //----< construct a named node >---------------------------------------

    public CsNode(string nodeName)
    {
      name = nodeName;
      children = new List<Edge<V,E>>();
      visited = false;
    }
    //----< add child vertex and its associated edge value to vertex >-----

    public void addChild(CsNode<V,E> childNode, E edgeVal)
    {
      children.Add(new Edge<V, E>(childNode, edgeVal));
    }
    //----< find the next unvisited child >--------------------------------

    public Edge<V, E> getNextUnmarkedChild()
    {
      foreach (Edge<V, E> child in children)
      {
        if (!child.targetNode.visited)
        {
          child.targetNode.visited = true;
          return child;
        }
      }
      return null;
    }
    //----< has unvisited child? >-----------------------------------

    public bool hasUnmarkedChild()
    {
      foreach (Edge<V, E> child in children)
      {
        if (!child.targetNode.visited)
        {
          return true;
        }
      }
      return false;
    }
    public void unmark()
    {
      visited = false;
    }
    public override string ToString()
    {
      return name;
    }
  }
  /////////////////////////////////////////////////////////////////////////
  // Operation<V,E> class

  class Operation<V, E>
  {
    //----< graph.walk() calls this on every node >------------------------

    virtual public bool doNodeOp(CsNode<V,E> node)
    {
      Console.Write("\n  {0}", node.ToString());
      return true;
    }
    //----< graph calls this on every child visitation >-------------------

    virtual public bool doEdgeOp(E edgeVal)
    {
      Console.Write(" {0}", edgeVal.ToString());
      return true;
    }
  }
  /////////////////////////////////////////////////////////////////////////
  // CsGraph<V,E> class

  class CsGraph<V,E>
  {
    public CsNode<V, E> startNode { get; set; }
    public string name { get; set; }
    public bool showBackTrack { get; set; } = false;

    private List<CsNode<V, E>> adjList { get; set; }  // node adjacency list
    private Operation<V, E> gop = null;

    //----< construct a named graph >--------------------------------------

    public CsGraph(string graphName)
    {
      name = graphName;
      adjList = new List<CsNode<V, E>>();
      gop = new Operation<V, E>();
      startNode = null;
    }
    //----< register an Operation with the graph >-------------------------

    public Operation<V, E> setOperation(Operation<V, E> newOp)
    {
      Operation<V, E> temp = gop;
      gop = newOp;
      return temp;
    }
    //----< add vertex to graph adjacency list >---------------------------

    public void addNode(CsNode<V,E> node)
    {
      adjList.Add(node);
    }
    //----< clear visitation marks to prepare for next walk >--------------

    public void clearMarks()
    {
      foreach (CsNode<V, E> node in adjList)
        node.unmark();
    }
    //----< depth first search from startNode >----------------------------

    public void walk()
    {
      if(adjList.Count == 0)
      {
        Console.Write("\n  no nodes in graph");
        return;
      }
      if(startNode == null)
      {
        Console.Write("\n  no starting node defined");
        return;
      }
      if(gop == null)
      {
        Console.Write("\n  no node or edge operation defined");
        return;
      }
      this.walk(startNode);
      foreach (CsNode<V, E> node in adjList)
        if (!node.visited)
          walk(node);
      foreach (CsNode<V, E> node in adjList)
        node.unmark();
      return;
    }
    //----< depth first search from specific node >------------------------

    public void walk(CsNode<V,E> node)
    {
      // process this node

      gop.doNodeOp(node);
      node.visited = true;

      // visit children
      do
      {
        Edge<V,E> childEdge = node.getNextUnmarkedChild();
        if (childEdge == null)
        {
          return;
        }
        else
        {
          gop.doEdgeOp(childEdge.edgeValue);
          walk(childEdge.targetNode);
          if (node.hasUnmarkedChild() || showBackTrack)
          {                         // popped back to predecessor node
            gop.doNodeOp(node);     // more edges to visit so announce
          }                         // location and next edge
        }
      } while (true);
    }
  }
  /////////////////////////////////////////////////////////////////////////
  // Test class

  class demoOperation : Operation<string, string>
  {
    override public bool doNodeOp(CsNode<string, string> node)
    {
      Console.Write("\n -- {0}", node.name);
      return true;
    }
  }
  class Test
  {
    static void Main(string[] args)
    {
      Console.Write("\n  Testing CsGraph class");
      Console.Write("\n =======================");

      CsNode<string, string> node1 = new CsNode<string, string>("node1");
      CsNode<string, string> node2 = new CsNode<string, string>("node2");
      CsNode<string, string> node3 = new CsNode<string, string>("node3");
      CsNode<string, string> node4 = new CsNode<string, string>("node4");
      CsNode<string, string> node5 = new CsNode<string, string>("node5");

      node1.addChild(node2, "edge12");
      node1.addChild(node3, "edge13");
      node2.addChild(node3, "edge23");
      node2.addChild(node4, "edge24");
      node3.addChild(node1, "edge31");
      node5.addChild(node1, "edge51");
      node5.addChild(node4, "edge54");

      CsGraph<string,string> graph = new CsGraph<string,string>("Fred");
      graph.addNode(node1);
      graph.addNode(node2);
      graph.addNode(node3);
      graph.addNode(node4);
      graph.addNode(node5);

      graph.startNode = node1;
      Console.Write("\n\n  starting walk at {0}", graph.startNode.name);
      Console.Write("\n  not showing backtracks");
      graph.walk();

      graph.startNode = node2;
      Console.Write("\n\n  starting walk at {0}", graph.startNode.name);
      graph.showBackTrack = true;
      Console.Write("\n  show backtracks");
      graph.setOperation(new demoOperation());
      graph.walk();

      Console.Write("\n\n");
    }
  }
}