Skip to content
Snippets Groups Projects
Select Git revision
  • 8c49379410263c71359c90c7ac4e2a254de279e6
  • master default
  • feat/331-bitnami-kafka-moved-to-bitnamilegacy-kafka
  • feat/330-tid-pcep-component
  • feat/tid-newer-pcep-component
  • feat/policy-refactor
  • feat/314-tid-new-service-for-ipowdm-configuration-fron-orchestrator-to-ipowdm-controller
  • feat/329-ecoc-2025-hack-your-research
  • develop protected
  • feat/116-ubi-updates-in-telemetry-backend-to-support-p4-in-band-network-telemetry
  • feat/320-cttc-ietf-simap-basic-support-with-kafka-yang-push
  • feat/310-cttc-implement-nbi-connector-to-interface-with-osm-client
  • feat/307-update-python-version-service
  • feat/292-cttc-implement-integration-test-for-ryu-openflow
  • cnit_tapi
  • feat/327-tid-new-service-to-ipowdm-controller-to-manage-transceivers-configuration-on-external-agent
  • cnit-p2mp-premerge
  • feat/325-tid-nbi-e2e-to-manage-e2e-path-computation
  • feat/326-tid-external-management-of-devices-telemetry-nbi
  • openroadm-flex-grid
  • CTTC-IMPLEMENT-NBI-CONNECTOR-NOS-ZTP
  • feat/324-tid-nbi-ietf_l3vpn-deploy-fail
  • v5.0.0 protected
  • v4.0.0 protected
  • demo-dpiab-eucnc2024
  • v3.0.0 protected
  • v2.1.0 protected
  • v2.0.0 protected
  • v1.0.0 protected
29 results

pathComp_tools.c

Blame
  • rmartinez.cttc's avatar
    Ricardo Martínez authored
    - Added support of the GET method to check status of PathComp
    -Added support for showing logs via either Screen (stdout) or defined Log file
    c41c7324
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    pathComp_tools.c 92.76 KiB
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	# Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
    
     * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es)
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    
    #include <stdio.h>
    #include <stdlib.h> 
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <glib.h>
    #include <sys/time.h>
    #include <ctype.h>
    #include <strings.h>
    #include <time.h>
    #include <math.h>
    #include <fcntl.h>
    #include <uuid/uuid.h>
    #include <errno.h>
    
    #include "pathComp_log.h"
    #include "pathComp.h"
    #include "pathComp_tools.h"
    
    gint numPathCompIntents = 0;  // number of events triggering the path computation
    //gint numSuccesPathComp = 0; // number of events resulting in succesfully path computations fulfilling the constraints
    struct timeval total_path_comp_time;
    gdouble totalReqBw = 0.0;
    gdouble totalServedBw = 0.0;
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function for time processing
     *
     * 	@param a
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     ////////////////////////////////////////////////////////////////////////////////////////
    struct timeval tv_adjust (struct timeval a) {
    	while (a.tv_usec >= 1000000) {
    		a.tv_usec -= 1000000;
    		a.tv_sec++;
    	}
    
    	while (a.tv_usec < 0) {
    		a.tv_usec += 1000000;
    		a.tv_sec--;
    	}
    	return a;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief friendly function to copy safely strings
     *
     * 	@param dst
     *  @param src
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     ////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_string(gchar* dst, gchar* src) {
    	g_assert(dst);
    	g_assert(src);
    	strcpy(dst, src);
    	dst[strlen(dst)] = '\0';
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to print the computed the path
     *
     *	@param path
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void print_path (struct compRouteOutputItem_t *p) {
    	g_assert(p);
    	
    	DEBUG_PC ("=========== COMPUTED PATH =======================");
    	DEBUG_PC ("Path Avail. Bw: %f, E2E Path Latency: %f, Path Cost: %f", p->availCap, p->delay, p->cost);
    	for (gint k = 0; k < p->numRouteElements; k++) {
    		DEBUG_PC ("aNodeId: %s (%s) --> zNodeId: %s (%s)", p->routeElement[k].aNodeId.nodeId, p->routeElement[k].aEndPointId,
    																p->routeElement[k].zNodeId.nodeId, p->routeElement[k].zEndPointId);
    		DEBUG_PC("linkId: %s", p->routeElement[k].linkId);
    		DEBUG_PC("aTopologyId: %s", p->routeElement[k].aTopologyId);
    		DEBUG_PC("zTopologyId: %s", p->routeElement[k].zTopologyId);
    	}
    	DEBUG_PC ("==================================================================");		
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to print the output path formed by link Ids
     *
     *	@param p
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_path_t(struct path_t* p) {
    	g_assert(p);
    	DEBUG_PC(" ============ COMPUTED OUTPUT PATH =================");
    	DEBUG_PC("Path Avail Capacity: %f, Cost: %f, Latency: %f", p->path_capacity.value,
    			p->path_cost.cost_value, p->path_latency.fixed_latency);
    	DEBUG_PC("number of links of path %d", p->numPathLinks);
    	for (gint k = 0; k < p->numPathLinks; k++) {
    		DEBUG_PC("Link: %s", p->pathLinks[k].linkId);
    		for (gint l = 0; l < p->pathLinks[k].numLinkTopologies; l++) {
    			DEBUG_PC("end Link [%d] TopologyId: %s", l, p->pathLinks[k].linkTopologies[l].topologyId);
    		}
    		DEBUG_PC(" ContextId: %s", p->pathLinks[k].topologyId.contextId);
    		DEBUG_PC(" TopologyUUid: %s", p->pathLinks[k].topologyId.topology_uuid);
    		DEBUG_PC(" aDeviceId: %s", p->pathLinks[k].aDeviceId);
    		DEBUG_PC(" aEndpointId: %s", p->pathLinks[k].aEndPointId);
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Returns the char (36 bytes) format of a uuid
     *
     *	@param uuid
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    gchar* get_uuid_char(uuid_t uuid) {
    	gchar* uuidChar = g_malloc0(16); // uuid has 36 chars
    	if (uuidChar == NULL) {
    		DEBUG_PC("Memory Allocation failure");
    		exit(-1);
    	}
    	uuid_unparse(uuid, (char *)uuidChar);
    	return uuidChar;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Makes a copy of the service identifier (including the context)
     *
     *	@param o
     *  @param i
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void copy_service_id(struct serviceId_t* o, struct serviceId_t* i) {
    	g_assert(o);
    	g_assert(i);
    
    	memcpy(o->contextId, i->contextId, sizeof(i->contextId));
    	memcpy(o->service_uuid, i->service_uuid, sizeof(i->service_uuid));
    
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Makes a copy of the service endpoint identifier (including the topology (contect and topology id), device and endpoint (port))
     *
     *	@param oEp
     *  @param iEp
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void copy_service_endpoint_id(struct service_endpoints_id_t* oEp, struct service_endpoints_id_t* iEp) {
    	g_assert(oEp);
    	g_assert(iEp);
    
    	// copy topology information
    	memcpy(oEp->topology_id.contextId, iEp->topology_id.contextId, sizeof(iEp->topology_id.contextId));
    	memcpy(oEp->topology_id.topology_uuid, iEp->topology_id.topology_uuid, sizeof(iEp->topology_id.topology_uuid));
    
    	// copy the endpoint
    	memcpy(oEp->device_uuid, iEp->device_uuid, sizeof(iEp->device_uuid));
    	memcpy(oEp->endpoint_uuid, iEp->endpoint_uuid, sizeof(iEp->endpoint_uuid));
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief From the set of contexts, it is returned the graph associated to that contexct matching
     * with the passed contextId
     *
     *	@param Set
     *  @param contextId
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct graph_t* get_graph_by_contextId(struct contextSet_t* Set, gchar* contextId) {
    	g_assert(Set);
    	g_assert(contextId);
    
    	// iterate over the set of context. Pick the one matching with contextId, and return the graph.
    	// If not found, return NULL
    	struct graph_t* g = NULL;
    	for (gint i = 0; i < Set->num_context_set; i++) {
    		struct context_t* context = &(Set->contextList[i]);
    		if (strcmp(context->contextId, contextId) == 0) {
    			g = &(context->g);
    			return g;
    		}
    	}
    	return NULL;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Process the service constraint and maps them into the path constraints
     * to be fulfilled
     *
     *  @param path_constraints
     *  @param s
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct path_constraints_t * get_path_constraints(struct service_t* s) {
    	g_assert(s);
    	
    	struct path_constraints_t* path_constraints = g_malloc0(sizeof(struct path_constraints_t));
    	if (path_constraints == NULL) {
    		DEBUG_PC("Memory Allocation Failure");
    		exit(-1);
    	}
    
    	char* eptr;
    	for (gint i = 0; i < s->num_service_constraints; i++) {
    		struct constraint_t* constraint = &(s->constraints[i]);;
    		if (strncmp((const char*)constraint->constraint_type, "bandwidth", 9) == 0) {
    			path_constraints->bwConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr));
    			path_constraints->bw = TRUE;
    			//DEBUG_PC("Path Constraint Bw: %f", path_constraints->bwConstraint);
    		}
    		if (strncmp((const char*)constraint->constraint_type, "cost", 4) == 0) {
    			path_constraints->costConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr));
    			path_constraints->cost = TRUE;
    			//DEBUG_PC("Path Constraint Cost: %f", path_constraints->costConstraint);
    		}
    		if (strncmp((const char*)constraint->constraint_type, "latency", 7) == 0) {
    			path_constraints->latencyConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr));
    			path_constraints->latency = TRUE;
    			//DEBUG_PC("Path Constraint Latency: %f", path_constraints->latencyConstraint);
    		}
    		if (strncmp((const char*)constraint->constraint_type, "energy", 6) == 0) {
    			path_constraints->energyConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr));
    			path_constraints->energy = TRUE;
    			//DEBUG_PC("Path Constraint Energy: %f", path_constraints->energyConstraint);
    		}
    	}
    	return path_constraints;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Creates the predecessors to keep the computed path
     *
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct pred_t * create_predecessors ()
    {
    	struct pred_t *predecessors = g_malloc0 (sizeof (struct pred_t));
    	if (predecessors == NULL)
    	{
    		DEBUG_PC ("memory allocation failed\n");
    		exit (-1);
    	}   
    	return predecessors;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief create edge
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct edges_t* create_edge()
    {
    	struct edges_t* e = g_malloc0(sizeof(struct edges_t));
    	if (e == NULL)
    	{
    		DEBUG_PC("Memory allocation failed\n");
    		exit(-1);
    	}
    	return e;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Prints the list of the predecessors for a given computed Shortest Path
     *
     *	@param p 
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void print_predecessors (struct pred_t *p)
    {
    	g_assert (p);
    	DEBUG_PC ("Number of Predecessors: %d", p->numPredComp);
    	for (gint i = 0; i < p->numPredComp; i++) {
    		struct pred_comp_t *pComp = &(p->predComp[i]);
    		DEBUG_PC ("deviceId: %s", pComp->v.nodeId);		
    		struct edges_t *e = &(pComp->e);
    		DEBUG_PC("Edge[#%d] (linkId): %s", i, e->linkId);
    		DEBUG_PC ("\t %s[%s] ===>", e->aNodeId.nodeId, e->aEndPointId);
    		DEBUG_PC("\t %s[%s]", e->zNodeId.nodeId, e->zEndPointId);
    		DEBUG_PC("\t aTopologyId: %s", e->aTopologyId);
    		DEBUG_PC("\t zTopologyId: %s", e->zTopologyId);
    	}	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Builds the list of predecessors for the request destination using the computed Shortest Path
     *	being stored in map
     *
     *	@param p 
     *	@param s
     *	@param map
     *	
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void build_predecessors (struct pred_t *p, struct service_t *s, struct map_nodes_t *map)
    {
    	g_assert (p);
    	g_assert (s);
    	g_assert (map);
    	
    	struct nodes_t *v = create_node();
    	duplicate_string(v->nodeId, s->service_endpoints_id[1].device_uuid);
    	
    	struct edges_t *e = create_edge ();	
    	get_edge_from_map_by_node (e, v, map);
    			
    	// Get u (being source of edge e)
    	struct nodes_t u;	
    	duplicate_node_id (&e->aNodeId, &u);
    		
    	// Add to the predecessors list
    	struct pred_comp_t *pred = &(p->predComp[p->numPredComp]);
    	duplicate_node_id (&u, &pred->v);	
    	struct edges_t *e1 = &(pred->e);	
    	duplicate_edge (e1, e);
    	p->numPredComp++;	
    	// Back-trace edges till reaching the srcPEId
    	struct nodes_t* srcNode = create_node();
    	duplicate_string(srcNode->nodeId, s->service_endpoints_id[0].device_uuid);
    
    	while (compare_node_id (&u, srcNode) != 0) 	{		
    		duplicate_node_id (&u, v);
    		get_edge_from_map_by_node (e, v, map);		
    		// Get the u (being source of edge e)		
    		duplicate_node_id (&e->aNodeId, &u);		
    		// Get the new predecessor
    		struct pred_comp_t *pred = &p->predComp[p->numPredComp];			
    		// Add to the predecessors list					
    		duplicate_node_id (&u, &pred->v);		
    		struct edges_t *e1 = &(pred->e);
    		duplicate_edge (e1, e);
    		p->numPredComp++;		
    	}
    	print_predecessors (p);
        g_free (e);
    	g_free(v);
    	g_free(srcNode);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief It creates a struct nodes_t
     *
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct nodes_t * create_node ()
    {
    	struct nodes_t *n = g_malloc0 (sizeof (struct nodes_t));
    	if (n == NULL) {
    		DEBUG_PC ("memory allocation problem");
    		exit (-1);
    	}
    	return n;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief It creates a routeElement_t
     *
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct routeElement_t * create_routeElement ()
    {
    	struct routeElement_t *rE = g_malloc0 (sizeof (struct routeElement_t));
    	if (rE == NULL)
    	{
    		DEBUG_PC ("memory allocation problem");
    		exit (-1);		
    	}
    	return rE;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief copy node ids
     *
     *	@param src
     *  @param dst
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_node_id (struct nodes_t *src, struct nodes_t *dst)
    {	
    	g_assert (src);
    	g_assert (dst);
    	
    	//DEBUG_PC ("Duplicate nodeId for %s", src->nodeId);	
    	strcpy (dst->nodeId, src->nodeId);	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief compares a pair of node Ids
     *
     *	@param a
     *  @param b
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint compare_node_id (struct nodes_t *a, struct nodes_t *b)
    {
    	g_assert (a);
    	g_assert (b);	
    	return (memcmp (&a->nodeId, b->nodeId, strlen (b->nodeId)));	
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief duplicate two routeElement_t
     *
     *	@param src
     *  @param dst
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_routeElement (struct routeElement_t *src, struct routeElement_t *dst)
    {
    	g_assert (src);
    	g_assert (dst);
    	
    	duplicate_node_id (&(src->aNodeId), &(dst->aNodeId));
    	duplicate_node_id (&(src->zNodeId), &(dst->zNodeId));
    	duplicate_string(dst->aEndPointId, src->aEndPointId);
    	duplicate_string(dst->zEndPointId, src->zEndPointId);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief duplicate two edges
     *
     *	@param e1 (destination)
     *  @param e2 (source)
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_edge (struct edges_t *e1, struct edges_t *e2) {
    	g_assert (e1);
    	g_assert (e2);
    		
    	duplicate_node_id (&e2->aNodeId, &e1->aNodeId);
    	duplicate_node_id (&e2->zNodeId, &e1->zNodeId);
    	//DEBUG_PC ("e->aNodeId: %s --->  e->zNodeId: %s", e1->aNodeId.nodeId, e1->zNodeId.nodeId);
    	duplicate_string(e1->aEndPointId, e2->aEndPointId);
    	duplicate_string(e1->zEndPointId, e2->zEndPointId);
    	duplicate_string(e1->linkId, e2->linkId);
    	duplicate_string(e1->interDomain_localId, e2->interDomain_localId);
    	duplicate_string(e1->interDomain_remoteId, e2->interDomain_remoteId);
    	duplicate_string(e1->aTopologyId, e2->aTopologyId);
    	duplicate_string(e1->zTopologyId, e2->zTopologyId);
    	
    	e1->unit = e2->unit;
    	memcpy(&e1->totalCap, &e2->totalCap, sizeof(gdouble));
    	memcpy(&e1->availCap, &e2->availCap, sizeof(gdouble));
    
    	memcpy (&e1->cost, &e2->cost, sizeof (gdouble));
        memcpy (&e1->delay, &e2->delay, sizeof (gdouble));	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Duplicate path 
     *
     *	@param a - original
     *  @param b - copy
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_path (struct compRouteOutputItem_t *a, struct compRouteOutputItem_t *b) {		
    	g_assert (a);
    	g_assert (b);
    	memcpy (&b->availCap, &a->availCap, sizeof (gdouble));
    	b->numRouteElements = a->numRouteElements;	
    	memcpy(&b->cost, &a->cost, sizeof(gdouble));	
    	memcpy (&b->delay, &a->delay, sizeof (gdouble));
    	for (gint k = 0; k < a->numRouteElements; k++) {			
    		//DEBUG_PC ("aNodeId: %s // zNodeId: %s", a->routeElement[k].aNodeId.nodeId, a->routeElement[k].zNodeId.nodeId);
    		// aNodeId duplication
    		struct nodes_t *n1 = &(a->routeElement[k].aNodeId);
    		struct nodes_t *n2 = &(b->routeElement[k].aNodeId);			
    		duplicate_node_id (n1, n2);			
    					
    		//zNodeId duplication
    		n1 = &(a->routeElement[k].zNodeId);
    		n2 = &(b->routeElement[k].zNodeId);			
    		duplicate_node_id (n1, n2);
    		duplicate_string(b->routeElement[k].aEndPointId, a->routeElement[k].aEndPointId);
    		duplicate_string(b->routeElement[k].zEndPointId, a->routeElement[k].zEndPointId);
    		duplicate_string(b->routeElement[k].linkId, a->routeElement[k].linkId);
    		duplicate_string(b->routeElement[k].aTopologyId, a->routeElement[k].aTopologyId);
    		duplicate_string(b->routeElement[k].zTopologyId, a->routeElement[k].zTopologyId);
    	}	
    	return;	
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Duplicate path from compRouteOutputItem_t to path_t
     *
     *	@param a - original
     *  @param b - copy
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_path_t(struct compRouteOutputItem_t* a, struct path_t* b)
    {
    	g_assert(a);
    	g_assert(b);
    
    	memcpy(&b->path_capacity.value, &a->availCap, sizeof(gdouble));
    	memcpy(&b->path_cost.cost_value, &a->cost, sizeof(gdouble));
    	memcpy(&b->path_latency.fixed_latency, &a->delay, sizeof(gdouble));
    
    	b->numPathLinks = a->numRouteElements;
    
    	for (gint k = 0; k < a->numRouteElements; k++) {
    		struct routeElement_t* rE = &(a->routeElement[k]);
    		struct pathLink_t* pL = &(b->pathLinks[k]);
    
    		// copy the aDeviceId and aEndpointId, zDeviceId and zEndPointId
    		duplicate_string(pL->aDeviceId, rE->aNodeId.nodeId);
    		duplicate_string(pL->zDeviceId, rE->zNodeId.nodeId);
    		duplicate_string(pL->aEndPointId, rE->aEndPointId);
    		duplicate_string(pL->zEndPointId, rE->zEndPointId);
    
    		duplicate_string(pL->topologyId.topology_uuid, rE->aTopologyId);
    		duplicate_string(pL->topologyId.contextId, rE->contextId);
    
    		//copy the linkId
    		duplicate_string(pL->linkId, rE->linkId);
    		pL->numLinkTopologies++;
    		duplicate_string(pL->linkTopologies[pL->numLinkTopologies - 1].topologyId, rE->aTopologyId);
    		pL->numLinkTopologies++;
    		duplicate_string(pL->linkTopologies[pL->numLinkTopologies - 1].topologyId, rE->zTopologyId);
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Return the index into mapN related nodeId
     * 
     *  @param nodeId
     *  @para mapN
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint get_map_index_by_nodeId (gchar *nodeId, struct map_nodes_t * mapN)
    {
        gint index = -1;
        gint i = 0;
        
        for (i = 0; i < mapN->numMapNodes; i++)
        {
    		//DEBUG_PC ("i: %d; current: %s // targeted: %s", i, mapN->map[i].verticeId.nodeId, nodeId);
            if (memcmp (mapN->map[i].verticeId.nodeId, nodeId, strlen (nodeId)) == 0)
            {
                index = i;
    			//DEBUG_PC ("Index: %d", index);
                return index;            
            }
        }
    	//DEBUG_PC ("Index: %d", index);
        return index;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Get the edge e enabling reaching the computed v in mapNodes
     * 
     *  @param e
     *  @param v
     *  @param mapN
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void get_edge_from_map_by_node (struct edges_t *e, struct nodes_t* v, struct map_nodes_t *mapN) {
    	
    	//DEBUG_PC ("Get the Edge into map from node v: %s", v.nodeId);	
    	// Get the edge reaching the node v from mapNodes
    	gint map_vIndex = get_map_index_by_nodeId (v->nodeId, mapN);
    	
    	//DEBUG_PC ("aNodeId: %s --> zNodeId: %s", mapN->map[map_vIndex].predecessor.aNodeId.nodeId, mapN->map[map_vIndex].predecessor.zNodeId.nodeId);
    	
    	struct edges_t *te = &(mapN->map[map_vIndex].predecessor);	
    	duplicate_edge (e, te);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Get the edge from the predecessors array for a given node n
     * 
     *  @param e
     *  @param n
     *  @param predecessors
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void get_edge_from_predecessors (struct edges_t *e, struct nodes_t* n, struct pred_t *predecessors) {
    	g_assert(predecessors);
    
    	DEBUG_PC ("Get edge outgoing node %s from predecessors list", n->nodeId);
    	//print_predecessors (predecessors);
    	for (gint i = 0; i < predecessors->numPredComp; i++) {
    		struct pred_comp_t *pred = &(predecessors->predComp[i]);
    		if (compare_node_id (n, &pred->v) == 0) {
    			// Add to the predecessors list
    			struct edges_t *te = &(pred->e);
    			DEBUG_PC("add e (linkId): %s", te->linkId);
    			duplicate_edge (e, te);
    			return;
    		}	
    	}	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Construct the path using the predecessors list
     * 
     *  @param path
     *  @param predecessors
     *	@param s
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void build_path (struct compRouteOutputItem_t *p, struct pred_t *predecessors, struct service_t *s) {
    	//DEBUG_PC ("\n");
    	// Get the source device Id	of the network connectivity service
    	struct nodes_t *v = create_node();
    	duplicate_string(v->nodeId, s->service_endpoints_id[0].device_uuid);
    
    	struct edges_t* e = create_edge();
    							  	
    	// Get the edge for v in predecessors
    	get_edge_from_predecessors (e, v, predecessors);	
    	// Get the target for e
    	struct nodes_t u;	
    	duplicate_node_id (&e->zNodeId, &u);
    	//DEBUG_PC ("u: %s", u.nodeId);
    	struct path_constraints_t* pathCons = get_path_constraints(s);		
    
    	// Add route element to the path being constructed
    	gint k = 0;
    	duplicate_node_id (&e->aNodeId, &p->routeElement[k].aNodeId);
    	duplicate_node_id (&e->zNodeId, &p->routeElement[k].zNodeId);
    	duplicate_string(p->routeElement[k].aEndPointId, e->aEndPointId);
    	duplicate_string(p->routeElement[k].zEndPointId, e->zEndPointId);
    	duplicate_string(p->routeElement[k].linkId, e->linkId);
    	duplicate_string(p->routeElement[k].aTopologyId, e->aTopologyId);
    	duplicate_string(p->routeElement[k].zTopologyId, e->zTopologyId);
    	duplicate_string(p->routeElement[k].contextId, s->serviceId.contextId);
    	p->numRouteElements++;
    
    	// Get the destination device Id of the network connectivity service
    	struct nodes_t* dst = create_node();
    	duplicate_string(dst->nodeId, s->service_endpoints_id[1].device_uuid);
    	while (compare_node_id (&u, dst) != 0)	
    	{
    		k++; 
    		p->numRouteElements++;
    		// v = u		
    		duplicate_node_id (&u, v);
    		get_edge_from_predecessors (e, v, predecessors);
    		// Get the target u		
    		duplicate_node_id (&e->zNodeId, &u);
    		// Add route element to the path being constructed		
    		duplicate_node_id (&e->aNodeId, &p->routeElement[k].aNodeId);
    		duplicate_node_id (&e->zNodeId, &p->routeElement[k].zNodeId);
    		duplicate_string(p->routeElement[k].aEndPointId, e->aEndPointId);
    		duplicate_string(p->routeElement[k].zEndPointId, e->zEndPointId);
    		duplicate_string(p->routeElement[k].linkId, e->linkId);
    		duplicate_string(p->routeElement[k].aTopologyId, e->aTopologyId);
    		duplicate_string(p->routeElement[k].zTopologyId, e->zTopologyId);
    		duplicate_string(p->routeElement[k].contextId, s->serviceId.contextId);
    
    		// copy the contextId
    		//duplicate_string(p->routeElement[k].contextId, s->service_endpoints_id[0].topology_id.contextId);
    	}		
    	g_free(e); g_free(v); g_free(pathCons);
    	//DEBUG_PC ("Path is constructed");	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Print the graph for DEBUG_PCging purposes
     * 
     *  @param g
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void print_graph (struct graph_t *g)
    {	     
        DEBUG_PC ("================================================================");
        DEBUG_PC ("===========================   GRAPH   ==========================");
        DEBUG_PC ("================================================================");
    
    	DEBUG_PC("Graph Num Vertices: %d", g->numVertices);
        
        gint i = 0, j = 0, k = 0;
        for (i = 0; i < g->numVertices; i++)
        {
            DEBUG_PC ("Head Vertice [%s]", g->vertices[i].verticeId.nodeId);
            for (j = 0; j < g->vertices[i].numTargetedVertices; j++)
            {
                DEBUG_PC ("  Tail Vertice: %s", g->vertices[i].targetedVertices[j].tVertice.nodeId);
                for (k = 0; k < g->vertices[i].targetedVertices[j].numEdges; k++)
                {
                    struct edges_t *e = &(g->vertices[i].targetedVertices[j].edges[k]);
    				DEBUG_PC ("%s(%s) --> %s(%s) [C: %f, Bw: %f b/s, Delay: %f ms]", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, 
    								e->zEndPointId, e->cost, e->availCap, e->delay);				
               }
            }       
        }     
        return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Look for a given edge into the graph
     *
     *  @param verticeIndex
     *	@param targetedVerticeIndex
     *  @param e
     *  @param g
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint graph_edge_lookup (gint verticeIndex, gint targetedVerticeIndex, struct edges_t *e, struct graph_t *g)	{
    	gint indexEdge = -1;
    	
    	for (gint j = 0; j < g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex].numEdges; j++) {
    		struct edges_t *e2 = &(g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex].edges[j]);
    		if ((compare_node_id (&e->aNodeId, &e2->aNodeId) == 0) &&
    			(compare_node_id (&e->zNodeId, &e2->zNodeId) == 0) &&
    			(strcmp (e->aEndPointId, e2->aEndPointId) == 0) &&
    			(strcmp (e->zEndPointId, e2->zEndPointId) == 0) &&
    			(strcmp(e->linkId, e2->linkId) == 0)) {
    			DEBUG_PC ("%s (%s) --> %s (%s) [linkId: %s] FOUND in the Graph at index: %d", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, 
    							e->zEndPointId, e->linkId, j);
    			indexEdge = j;
    			return indexEdge;
    		}		
    	}	
    	return indexEdge;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Look for a given vertice within the graph using the nodeId
     *
     *  @param nodeId
     *	@param g
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint graph_vertice_lookup (gchar *nodeId, struct graph_t *g)
    {
        gint index = -1; 
    	//DEBUG_PC("Searching Node: %s", nodeId);
        for (gint i = 0; i < g->numVertices; i++) {
    		//DEBUG_PC("Checked Graph Node: %s", g->vertices[i].verticeId.nodeId);
    		if (memcmp (g->vertices[i].verticeId.nodeId, nodeId, strlen (nodeId)) == 0)
            {
                index = i;
                //DEBUG_PC ("%s is found in the graph vertice [%d]", nodeId, index);
                break;
            }     
        }  
        return (index);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Check if a nodeId is already considered into the set of targeted vertices from a given vertice
     *
     *  @param nodeId
     *  @param vIndex
     *  @param g
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint graph_targeted_vertice_lookup (gint vIndex, gchar *nodeId, struct graph_t *g)
    {
        gint addedTargetedVerticeIndex = -1;
        gint i = 0;
        
        if (g->vertices[vIndex].numTargetedVertices == 0)
        {
            return (addedTargetedVerticeIndex);
        }
        
        for (i = 0; i < g->vertices[vIndex].numTargetedVertices; i++)
        {
            if (memcmp (g->vertices[vIndex].targetedVertices[i].tVertice.nodeId, nodeId, strlen (nodeId)) == 0)
            {
                DEBUG_PC ("Targeted %s reachable from %s", nodeId, g->vertices[vIndex].verticeId.nodeId);
                addedTargetedVerticeIndex = i;
                return (addedTargetedVerticeIndex);
            }        
        }    
        // not found ...    
        return (addedTargetedVerticeIndex);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Check if a nodeId is already considered into the set of targeted vertices from a given vertice, if not to be added
     *
     *  @param nodeId
     *  @param vIndex
     *  @param g
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint graph_targeted_vertice_add (gint vIndex, gchar *nodeId, struct graph_t *g)
    {
        gint addedTargetedVerticeIndex = -1;
        gint i = 0;
        
        if (g->vertices[vIndex].numTargetedVertices == 0)
        {
            //DEBUG_PC ("targeted vertice %s being reachable from vertice %s", nodeId, g->vertices[vIndex].verticeId.nodeId);        
            addedTargetedVerticeIndex = 0;
            return (addedTargetedVerticeIndex);
        }
        
        for (i = 0; i < g->vertices[vIndex].numTargetedVertices; i++)
        {        
    		if (memcmp (g->vertices[vIndex].targetedVertices[i].tVertice.nodeId, nodeId, strlen (nodeId)) == 0)
            {
                //DEBUG_PC ("Targeted vertice %s is already considered in the reachable from vertice %s", nodeId, g->vertices[vIndex].verticeId.nodeId);
                addedTargetedVerticeIndex = -1;
                return (addedTargetedVerticeIndex);
            }        
        }    
        // It is not found, next to be added at i position
        addedTargetedVerticeIndex = i;
        return (addedTargetedVerticeIndex);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Remove edge from the graph
     *
     *  @param g
     *  @param e
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    void remove_edge_from_graph (struct graph_t *g, struct edges_t *e)
    {
    	// Find the ingress vertice into the graph
    	DEBUG_PC ("Removing from Graph %s[%s]) ---> %s[%s] (linkId: %s)", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, e->aEndPointId, e->linkId);
    	gint verticeIndex = -1;		
    	verticeIndex = graph_vertice_lookup (e->aNodeId.nodeId, g);
    	if (verticeIndex == -1)	{
    		DEBUG_PC ("Edge w/ %s is NOT in the Graph!!", e->aNodeId.nodeId);
    		return;
    	}
    	
    	// Find the targeted vertice from vertice Id
    	gint targetedVerticeIndex = -1;
    	targetedVerticeIndex = graph_targeted_vertice_lookup (verticeIndex, e->zNodeId.nodeId, g);
    	if (targetedVerticeIndex == -1)	{
    		DEBUG_PC ("%s --> %s NOT in the Graph!!", e->aNodeId.nodeId, e->zNodeId.nodeId);
    		return;
    	}
    	
    	//DEBUG_PC ("%s --> %s found in the Graph", e->aNodeId.nodeId, e->zNodeId.nodeId);
    	
    	// Get the edge position
    	gint edgeIndex = -1;
    	edgeIndex = graph_edge_lookup (verticeIndex, targetedVerticeIndex, e, g);
    	if (edgeIndex == -1) 	{
    		DEBUG_PC ("%s --> %s NOT in the Graph!!", e->aNodeId.nodeId, e->zNodeId.nodeId);
    		return;
    	}
    	
    	//DEBUG_PC ("%s --> %s FOUND in Graph w/ edgeIndex: %d", e->aNodeId.nodeId, e->zNodeId.nodeId, edgeIndex);
    	
    	// Remove the edge
    	//DEBUG_PC ("Start Removing %s --> %s from Graph", e->aNodeId.nodeId, e->zNodeId.nodeId);	
    	struct targetNodes_t *v = &(g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex]);	
    	for (gint j = edgeIndex; j < v->numEdges; j++) {	
    		struct edges_t *e1 = &(v->edges[j]);
    		struct edges_t *e2 = &(v->edges[j+1]);		
    		duplicate_edge (e1, e2);
    	}
    	v->numEdges --;
    	DEBUG_PC ("Number of Edges between %s and %s is %d", e->aNodeId.nodeId, e->zNodeId.nodeId, v->numEdges);	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief create the pointer for keeping a set of the paths (struct compRouteOutput_t)
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct path_set_t * create_path_set ()
    {
    	struct path_set_t * p = g_malloc0 (sizeof (struct path_set_t));
    	if (p == NULL)
    	{
    		DEBUG_PC ("Memory allocation problem");
    		exit (-1);		
    	}
    	return p;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Remove the path set
     *
     * @param p
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2021
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void remove_path_set(struct path_set_t* p)
    {
    	g_assert(p);
    	g_free(p);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Create map of nodes to handle the path computation
     *
     * 	@param mapN
     *  @param g
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void build_map_node (struct map_nodes_t *mapN, struct graph_t *g)
    {
    	//DEBUG_PC ("Construction of the Map of Nodes");               
        for (gint i = 0; i < g->numVertices; i++)
        {	
    		duplicate_node_id (&g->vertices[i].verticeId, &mapN->map[i].verticeId);
            mapN->map[i].distance = INFINITY_COST;
            mapN->map[i].avaiBandwidth = 0.0;
            mapN->map[i].latency = INFINITY_COST;
            mapN->numMapNodes++;
        }
        //DEBUG_PC ("mapNodes formed by %d Nodes", mapN->numMapNodes);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for path of struct compRouteOutputList_t *
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct compRouteOutputList_t * create_route_list ()
    {
    	struct compRouteOutputList_t *p = g_malloc0 (sizeof (struct compRouteOutputList_t));
    	if (p == NULL)
    	{
    		DEBUG_PC ("Memory Allocation Problem");
    		exit (-1);
    	}
    	return p;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for path of struct compRouteOutputItem_t *
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct compRouteOutputItem_t *create_path_item ()
    {
    	struct compRouteOutputItem_t *p = g_malloc0 (sizeof (struct compRouteOutputItem_t));
    	if (p == NULL) 	{
    		DEBUG_PC ("Memory Allocation Problem");
    		exit (-1);
    	}
    	return p;	
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Sort the set of paths according to the metric (1st criteria) and latency (2nd criteria)
     *
     *	@params setP
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void sort_path_set(struct path_set_t* setP) {
    	g_assert(setP);
    	// Sort the paths contained in setP by shotest metric and latency	
    	float epsilon = 0.0000001;
    
    	for (gint i = 0; i < setP->numPaths; i++) {
    		for (gint j = 0; j < (setP->numPaths - i - 1); j++)	{
    			struct compRouteOutputItem_t* path1 = &setP->paths[j];
    			struct compRouteOutputItem_t* path2 = &setP->paths[j + 1];
    			
    			struct compRouteOutputItem_t* pathTmp = create_path_item();
    			// 1st Criteria (avail Bw)
    			if ((path2->availCap - path1->availCap > 0.0) && (fabs(path1->availCap - path2->availCap) > epsilon)) {
    				duplicate_path(path1, pathTmp);
    				duplicate_path(path2, path1);
    				duplicate_path(pathTmp, path2);
    				g_free(pathTmp);
    				continue;
    			}
    			else if ((path1->availCap - path2->availCap > 0.0) && (fabs(path1->availCap - path2->availCap) > epsilon)) {
    				g_free(pathTmp);
    				continue;
    			}
    			// likely the same available bw between path1 and path2
    			else if (fabs(path1->availCap - path2->availCap) < epsilon) {
    				// 2nd criteria: sort path cost
    				if (path1->cost > path2->cost) {
    					duplicate_path(path1, pathTmp);
    					duplicate_path(path2, path1);
    					duplicate_path(pathTmp, path2);
    					g_free(pathTmp);
    					continue;
    				}
    				else if (path1->cost < path2->cost) {
    					g_free(pathTmp);
    					continue;
    				}
    				// 3rd criteria: same path cost, prioritize the one with lowest e2e latency
    				else if (path1->cost == path2->cost) {
    					if ((path2->delay - path1->delay > 0.0) && (fabs(path1->delay - path2->delay) > epsilon)) {
    						g_free(pathTmp);
    						continue;
    					}
    					else if ((path1->delay - path2->delay > 0.0) && (fabs(path1->delay - path2->delay) > epsilon)) {
    						duplicate_path(path1, pathTmp);
    						duplicate_path(path2, path1);
    						duplicate_path(pathTmp, path2);
    						g_free(pathTmp);
    						continue;
    					}
    					// Same bw, same cost and same latency, path1 and path2 are practically the same
    					else if (fabs(path1->delay - path2->delay) < epsilon) {
    						g_free(pathTmp);
    						continue;
    					}
    				}
    			}			
    		}
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Remove first element from the path sets 
     *
     *	@params setP
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void pop_front_path_set (struct path_set_t *setP) {
    	for (gint j = 0; j < setP->numPaths - 1; j++) {
    		struct compRouteOutputItem_t *path1 = &setP->paths[j];
    		struct compRouteOutputItem_t *path2 = &setP->paths[j+1];		
    		duplicate_path (path2, path1);		
    	}
    	setP->numPaths--;	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Add routeElement to the back of the path
     *
     * 	@param rE
     * 	@param p
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void add_routeElement_path_back (struct routeElement_t *rE, struct compRouteOutputItem_t *p)
    {
    	//DEBUG_PC ("p->numRouteElements: %d", p->numRouteElements);
    	p->numRouteElements++;
    	gint index = p->numRouteElements - 1;
    	
    	struct nodes_t *pn = &(p->routeElement[index].aNodeId);
    	struct nodes_t *rEn = &(rE->aNodeId);
    	
    	// duplicate aNodeId
    	duplicate_node_id (rEn, pn);	
    	pn = &(p->routeElement[index].zNodeId);
    	rEn = &(rE->zNodeId);
    	duplicate_node_id (rEn, pn);
    	duplicate_string(p->routeElement[index].aEndPointId, rE->aEndPointId);
    	duplicate_string(p->routeElement[index].zEndPointId, rE->zEndPointId);
    	duplicate_string(p->routeElement[index].linkId, rE->linkId);
    	duplicate_string(p->routeElement[index].aTopologyId, rE->aTopologyId);
    	duplicate_string(p->routeElement[index].zTopologyId, rE->zTopologyId);
    
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief This function compares ap and rootPath. If all the links are equal between both ap and rootPath till the sN, then the link from sN to next node 
     * 	ap is returned
     * 
     * @params ap
     * @params p
     * @params sN
     * @params e
     * 
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gboolean matching_path_rootPath (struct compRouteOutputItem_t *ap, struct compRouteOutputItem_t *rootPath, struct nodes_t *sN, struct edges_t *e) {
    	gint j = 0;
    	gboolean ret = FALSE;
    	while ((j < ap->numRouteElements) && (j < rootPath->numRouteElements)) {
    		if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0) &&
    			//(memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) != 0) &&
    			(memcmp (sN->nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0)) {						
    			duplicate_node_id (&ap->routeElement[j].aNodeId, &e->aNodeId);
    			duplicate_node_id (&ap->routeElement[j].zNodeId, &e->zNodeId);
    			duplicate_string(e->aEndPointId, ap->routeElement[j].aEndPointId);
    			duplicate_string(e->zEndPointId, ap->routeElement[j].zEndPointId);
    			duplicate_string(e->linkId, ap->routeElement[j].linkId);
    			return TRUE;			
    		}		
    		if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0) && 
    			(memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) == 0)) {
    			j++;			
    			continue;			
    		}
    		
    		if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) != 0) || 
    			(memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) != 0)) {
    			//DEBUG_PC ("ap and rootPath not in the same path");
    			return ret;
    		}
    	}	
    	return ret;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief This function is used to modify the graph to be used for running the subsequent SP computations acording to the YEN algorithm principles
     * 
     * @params g
     * @params A
     * @params rootPath
     * @params spurNode
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void modify_targeted_graph (struct graph_t *g, struct path_set_t *A, struct compRouteOutputItem_t * rootPath, struct nodes_t * spurNode) {
    	//DEBUG_PC ("Modify the Targeted graph according to the Yen algorithm principles");
    	for (gint j = 0; j < A->numPaths; j++)
    	{
    		struct compRouteOutputItem_t *ap = &A->paths[j];
    		struct edges_t *e = create_edge ();
    		gboolean ret =  FALSE;
    		ret = matching_path_rootPath (ap, rootPath, spurNode, e);		
    		if (ret == TRUE) {
    			//DEBUG_PC ("Removal %s [%u]--> %s [%u] from the graph", e->aNodeId.nodeId, e->aLinkId, e->zNodeId.nodeId, e->zLinkId);
    			remove_edge_from_graph (g, e);
    			//DEBUG_PC ("Print Resulting Graph");
    			//print_graph (g);
    			g_free (e);			
    		}
    		if (ret == FALSE)
    		{
    			g_free (e);
    			continue;
    		}						
    	}	
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Supporting fucntion to Check if a nodeId is already in the items of a given GList
     * 
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint find_nodeId (gconstpointer data, gconstpointer userdata)
    {
         /** check values */
         g_assert(data != NULL);
         g_assert(userdata != NULL);
     
         struct nodeItem_t *SNodeId = (struct nodeItem_t *)data;
         guchar * nodeId = (guchar *)userdata; 
         
         //DEBUG_PC ("SNodeId (%s) nodeId (%s)", SNodeId->node.nodeId, nodeId);   
            
         if (!memcmp(SNodeId->node.nodeId, nodeId, strlen (SNodeId->node.nodeId)))
         {
    		return (0);
         }
        return -1;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Explores the link between u and v
     * 
     *  @param u
     *  @param v 
     *	@param g
     *	@param s
     *  @param S
     *  @param Q
     *	@param mapNodes
     * 
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint check_link (struct nodeItem_t *u, gint indexGraphU, gint indexGraphV, struct graph_t *g, 
    				struct service_t *s, GList **S, GList **Q, struct map_nodes_t *mapNodes) { 
    	g_assert(g);
    	g_assert(s);
    	g_assert(mapNodes);
    
    	struct targetNodes_t *v = &(g->vertices[indexGraphU].targetedVertices[indexGraphV]);	
        DEBUG_PC("Explored link (u ===> v):");
    	DEBUG_PC("u: %s ====>", u->node.nodeId);
    	DEBUG_PC("====> v: %s", v->tVertice.nodeId);
        
        // v already explored in S? then, discard it
        GList *found = g_list_find_custom (*S, v->tVertice.nodeId, find_nodeId);
        if (found != NULL) {
            DEBUG_PC ("v (%s) in S, discard to explore it!", v->tVertice.nodeId);        
            return 0;
        }
    
    	// Get the set of constraints imposed by the service
    	struct path_constraints_t* path_constraints = get_path_constraints(s);
        gdouble distance_through_u = INFINITY_COST;
        gdouble latency_through_u = INFINITY_COST;	
    	gint i = 0;
    
        // Check bandwidth requirement is fulfillied on edge u --> v    
        gint foundAvailBw = 0;
        gdouble edgeAvailBw = 0.0;
        for (i = 0; i < v->numEdges; i++) {        
            struct edges_t *e = &(v->edges[i]);
    		memcpy (&edgeAvailBw, &(e->availCap), sizeof (gdouble));
    		DEBUG_PC("edge:u ===> v");
            DEBUG_PC ("%s[%s] ===>", u->node.nodeId, e->aEndPointId);
    		DEBUG_PC("====> %s[%s]", v->tVertice.nodeId, e->zEndPointId);
    		DEBUG_PC("edge available bw: %f", edgeAvailBw);
    
            // if network service constraint specifies "bandwidth" needs (assuming coherent units)
    		if (path_constraints->bw == TRUE) {
    			if (edgeAvailBw < path_constraints->bwConstraint) {
    				continue;
    			}
    			else {
    				foundAvailBw = 1;
    				break;
    			}
    		}		
        }
    	// if bw path constraint is specified but not sastified ...	discard the edge
        if ((path_constraints->bw == TRUE) && (foundAvailBw == 0))
        {
            DEBUG_PC ("AvailBw: %f < path_constraint: %f -- Discard Edge", edgeAvailBw, path_constraints->bwConstraint);
    		g_free(path_constraints);
            return 0;    
        } 
    
        gint indexEdge = i; // get the index for the explored edge
        // Update distance, latency and availBw through u to reach v
        gint map_uIndex = get_map_index_by_nodeId (u->node.nodeId, mapNodes);
    	struct map_t *u_map = &mapNodes->map[map_uIndex];
        distance_through_u = u_map->distance + v->edges[indexEdge].cost;
        latency_through_u = u_map->latency + v->edges[indexEdge].delay;    
        gdouble availBw_through_u = 0.0;
    
    	// ingress endpoint (u) is the src of the request
        if (strcmp (u->node.nodeId, s->service_endpoints_id[0].device_uuid) == 0) {
            //DEBUG_PC ("AvailBw %f on %s --> %s", edgeAvailBw, u->node.nodeId, v->tVertice.nodeId);        
            memcpy (&availBw_through_u, &edgeAvailBw, sizeof (gdouble));        
        }
        else {
            // Get the minimum available bandwidth between the src-->u and the new added edge u-->v
            //DEBUG_PC ("Current AvailBw: %f from src to %s", u_map->avaiBandwidth, u->node.nodeId);
            //DEBUG_PC ("AvailBw: %f %s --> %s", edgeAvailBw, u->node.nodeId, v->tVertice.nodeId);
            if (u_map->avaiBandwidth <= edgeAvailBw) {
                memcpy (&availBw_through_u, &u_map->avaiBandwidth, sizeof (gdouble));    
    		}
    		else {
    			memcpy (&availBw_through_u, &edgeAvailBw, sizeof (gdouble));
    		} 
        }     
        // Relax the link according to the pathCost and latency
        gint map_vIndex = get_map_index_by_nodeId (v->tVertice.nodeId, mapNodes);
    	struct map_t *v_map = &mapNodes->map[map_vIndex];
        // If cost dist (u, v) > dist (src, v) relax the link
        if (distance_through_u > v_map->distance) {
            //DEBUG_PC ("dist(src, u) + dist(u, v): %f > dist (src, v): %f --> Discard Link", distance_through_u, v_map->distance);  
            return 0;
        }
        // If dist (src, u) + dist (u, v) = current dist(src, v), then use the latency as discarding criteria
        if ((distance_through_u == v_map->distance) && (latency_through_u > v_map->latency)) {
            //DEBUG_PC ("dist(src, u) + dist(u,v) = current dist(src, v), but latency (src,u) + latency (u, v) > current latency (src, v)");          
            return 0;
        }	
    	// If dist (src, u) + dist (u,v) == current dist(src, v) AND latency (src, u) + latency (u, v) == current latency (src, v), the available bandwidth is the criteria
    	if ((distance_through_u ==  v_map->distance) && (latency_through_u == v_map->latency) && (availBw_through_u < v_map->avaiBandwidth)) {
    		return 0;
    	}    
        DEBUG_PC ("%s --> %s Relaxed", u->node.nodeId, v->tVertice.nodeId);
        DEBUG_PC ("\t AvailBw: %f Mb/s, Cost: %f, Latency: %f ms", availBw_through_u, distance_through_u, latency_through_u);
        
        // Update Q list -- 
        struct nodeItem_t *nodeItem = g_malloc0 (sizeof (struct nodeItem_t));
        if (nodeItem == NULL) {
    		DEBUG_PC ("memory allocation failed\n");
    		exit (-1);    
        }    
        nodeItem->distance = distance_through_u;
    	memcpy(&nodeItem->distance, &distance_through_u, sizeof(gdouble));		     
    	memcpy(&nodeItem->latency, &latency_through_u, sizeof(gdouble));
    	duplicate_node_id (&v->tVertice, &nodeItem->node);	
    	// add node to the Q list
        *Q = g_list_insert_sorted (*Q, nodeItem, sort_by_distance);
        //DEBUG_PC ("%s ADDED to Q (length: %d)", nodeItem->node.nodeId, g_list_length(*Q));    
        
        // Update the mapNodes for the specific reached tv   
        v_map->distance = distance_through_u;
    	memcpy(&v_map->distance, &distance_through_u, sizeof(gdouble));
        memcpy (&v_map->avaiBandwidth, &availBw_through_u, sizeof (gdouble));
        memcpy (&v_map->latency, &latency_through_u, sizeof (gdouble));
        // Duplicate the predecessor edge into the mapNodes 
    	struct edges_t *e1 = &(v_map->predecessor);
    	struct edges_t *e2 = &(v->edges[indexEdge]);
    	duplicate_edge (e1, e2);	
    	DEBUG_PC ("u->v Edge: %s(%s) --> %s(%s)", e2->aNodeId.nodeId, e2->aEndPointId, e2->zNodeId.nodeId, e2->zEndPointId);
    	DEBUG_PC("v-pred aTopology: %s", e2->aTopologyId);
    	DEBUG_PC("v-pred zTopology: %s", e2->zTopologyId);
    
        // Check whether v is dstPEId
    	//DEBUG_PC ("Targeted dstPEId: %s", req->dstPEId.nodeId);
    	//DEBUG_PC ("nodeId added to the map: %s", v_map->verticeId.nodeId);
    	//DEBUG_PC ("Q Length: %d", g_list_length(*Q));
    	g_free(path_constraints);
        return 0;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Check the feasability of a path wrt the constraints imposed by the request in terms of latency
     * 
     *  @param s
     *	@param p
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gboolean check_computed_path_feasability (struct service_t *s, struct compRouteOutputItem_t* p) {	
    	float epsilon = 0.0000001;
    	struct path_constraints_t* pathCons = get_path_constraints(s);
    	gboolean ret = TRUE;
    
    	if (pathCons->latency == TRUE) {
    		if ((pathCons->latencyConstraint - p->delay > 0.0) || (fabs(pathCons->latencyConstraint - p->delay) < epsilon)) {
    			DEBUG_PC("Computed Path (latency: %f) is feasible wrt Connection Demand: %f", p->delay, pathCons->latencyConstraint);
    		}
    		else {
    			DEBUG_PC("Computed Path (latency: %f) is NOT feasible wrt Connection Demand: %f", p->delay, pathCons->latencyConstraint);
    			g_free(pathCons);
    			return FALSE;
    		}
    	}
    	// Other constraints...
    	
    	g_free(pathCons);
    	return ret;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Sorting the GList Q items by distance 
     * 
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint sort_by_distance (gconstpointer a, gconstpointer b)
    {
    	//DEBUG_PC ("sort by distance a and b");	
    	g_assert(a != NULL);
    	g_assert(b != NULL);
    	
    	//DEBUG_PC ("sort by distance a and b");	  
    	struct nodeItem_t *node1 = (struct nodeItem_t *)a;
    	struct nodeItem_t *node2 = (struct nodeItem_t *)b;
    	g_assert (node1);
    	g_assert (node2);
    	 
    	//DEBUG_PC ("a->distance %u; b->distance %u", node1->distance, node2->distance);
    	//DEBUG_PC("a->latency: %f; b->latency: %f", node1->latency, node2->latency);
    	//1st criteria, sorting by lowest distance
    	if (node1->distance > node2->distance)
    		return 1;
    	else if (node1->distance < node2->distance)
    		return 0;
    	if (node1->distance == node2->distance)
    	{
    		if (node1->latency > node2->latency)
    			return 1;
    		else if (node1->latency <= node2->latency)
    			return 0;
    	}
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for graph
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct graph_t * create_graph () {
    	struct graph_t * g = g_malloc0 (sizeof (struct graph_t));
    	if (g == NULL)
    	{
    		DEBUG_PC ("Memory Allocation Problem");
    		exit (-1);
    	}
    	return g;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for mapNodes
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    struct map_nodes_t * create_map_node ()	 {
    	struct map_nodes_t * mN = g_malloc0 (sizeof (struct map_nodes_t));
    	if (mN == NULL)
    	{
    		DEBUG_PC ("Memory allocation failed");
    		exit (-1);
    	}
    	return mN;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Look up for the service in the servieList bound to a serviceUUID
     * 
     * @params serviceUUID
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    
    struct service_t* get_service_for_computed_path(gchar* serviceUUID) {
    	for (gint i = 0; i < serviceList->numServiceList; i++) {
    		struct service_t* s = &(serviceList->services[i]);
    		if (strcmp(s->serviceId.service_uuid, serviceUUID) == 0)
    			return s;
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for struct deviceList_t
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct deviceList_t* create_device_list()
    {
    	struct deviceList_t* dList = g_malloc0(sizeof(struct deviceList_t));
    	if (dList == NULL)
    	{
    		DEBUG_PC("Memory Allocation Failure");
    		exit(-1);
    	}
    	return dList;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for struct linkList_t
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct linkList_t* create_link_list() {
    	struct linkList_t* lList = g_malloc0(sizeof(struct linkList_t));
    	if (lList == NULL)
    	{
    		DEBUG_PC("Memory Allocation Failure");
    		exit(-1);
    	}
    	return lList;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for struct serviceList_t
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct serviceList_t* create_service_list() {
    	struct serviceList_t* sList = g_malloc0(sizeof(struct serviceList_t));
    	if (sList == NULL)
    	{
    		DEBUG_PC("Memory Allocation Failure");
    		exit(-1);
    	}
    	return sList;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the service type
     * 
     * @param type
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_service_type(guint type) {
    	switch (type) {
    	case SERVICE_TYPE_UNKNOWN:
    			DEBUG_PC("Service Type UNKNOWN");
    			break;
    		case SERVICE_TYPE_L3NM:
    			DEBUG_PC("Service Type L3NM");
    			break;
    		case SERVICE_TYPE_L2NM:
    			DEBUG_PC("Service Type L2NM");
    			break;
    		case SERVICE_TYPE_TAPI:
    			DEBUG_PC("Service Type L2NM");
    			break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the port direction
     *
     * @param direction
     *
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_link_port_direction(guint direction)
    {
    	switch (direction) {
    		case LINK_PORT_DIRECTION_BIDIRECTIONAL:
    			//DEBUG_PC("Bidirectional Port Direction");
    			break;
    		case LINK_PORT_DIRECTION_INPUT:
    			//DEBUG_PC("Input Port Direction");
    			break;
    		case LINK_PORT_DIRECTION_OUTPUT:
    			//DEBUG_PC("Output Port Direction");
    			break;
    		case LINK_PORT_DIRECTION_UNKNOWN:
    			//DEBUG_PC("Unknown Port Direction");
    			break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the port termination direction
     *
     * @param direction
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_termination_direction(guint direction)
    {
    	switch (direction) {
    	case TERMINATION_DIRECTION_BIDIRECTIONAL:
    		//DEBUG_PC("Bidirectional Termination Direction");
    		break;
    	case TERMINATION_DIRECTION_SINK:
    		//DEBUG_PC("Input Termination Direction");
    		break;
    	case TERMINATION_DIRECTION_SOURCE:
    		//DEBUG_PC("Output Termination Direction");
    		break;
    	case TERMINATION_DIRECTION_UNKNOWN:
    		//DEBUG_PC("Unknown Termination Direction");
    		break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the termination state
     *
     * @param state 
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_termination_state(guint state)
    {
    	switch (state) {
    	case TERMINATION_STATE_CAN_NEVER_TERMINATE:
    		//DEBUG_PC("Can never Terminate");
    		break;
    	case TERMINATION_STATE_NOT_TERMINATED:
    		DEBUG_PC("Not terminated");
    		break;
    	case TERMINATION_STATE_TERMINATED_SERVER_TO_CLIENT_FLOW:
    		DEBUG_PC("Terminated server to client flow");
    		break;
    	case TERMINATION_STATE_TERMINATED_CLIENT_TO_SERVER_FLOW:
    		DEBUG_PC("Terminated client to server flow");
    		break;
    	case TERMINATION_STATE_TERMINATED_BIDIRECTIONAL:
    		//DEBUG_PC("Terminated bidirectional");
    		break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the capacity unit
     *
     * @param unit
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_capacity_unit(guint unit) {
    
    	switch (unit) {
    		case CAPACITY_UNIT_TB:
    			DEBUG_PC("Unit in TB");
    			break;
    		case CAPACITY_UNIT_TBPS:
    			DEBUG_PC("Unit in TB/s");
    			break;
    		case CAPACITY_UNIT_GB:
    			DEBUG_PC("Unit in GB");
    			break;
    		case CAPACITY_UNIT_GBPS:
    			DEBUG_PC("Unit in GB/s");
    			break;
    		case CAPACITY_UNIT_MB:
    			DEBUG_PC("Unit in MB");
    			break;
    		case CAPACITY_UNIT_MBPS:
    			//DEBUG_PC("Unit in MB/s");
    			break;
    		case CAPACITY_UNIT_KB:
    			DEBUG_PC("Unit in KB");
    			break;
    		case CAPACITY_UNIT_KBPS:
    			DEBUG_PC("Unit in KB/s");
    			break;
    		case CAPACITY_UNIT_GHZ: 
    			DEBUG_PC("Unit in GHz");
    			break;
    		case CAPACITY_UNIT_MHZ:
    			DEBUG_PC("Unit in MHz");
    			break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Friendly function to log the link forwarding direction
     *
     * @param linkFwDir
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_link_forwarding_direction(guint linkFwDir) {
    	switch (linkFwDir) {
    		case LINK_FORWARDING_DIRECTION_BIDIRECTIONAL:
    			DEBUG_PC("BIDIRECTIONAL LINK FORWARDING DIRECTION");
    			break;
    		case LINK_FORWARDING_DIRECTION_UNIDIRECTIONAL:
    			//DEBUG_PC("UNIDIRECTIONAL LINK FORWARDING DIRECTION");
    			break;
    		case  LINK_FORWARDING_DIRECTION_UNKNOWN:
    			DEBUG_PC("UNKNOWN LINK FORWARDING DIRECTION");
    			break;
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Allocate memory for the contextSet
     *
     * @param 
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct contextSet_t* create_contextSet() {
    	struct contextSet_t* c = g_malloc0(sizeof(struct contextSet_t));
    	if (c == NULL) {
    		DEBUG_PC("Memory Allocation Failure");
    		exit(-1);
    	}
    	return c;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Search a specific contextUuid element into the contextSet
     *
     * @param contextUuid
     * @param set
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct context_t* find_contextId_in_set(gchar* contextUuid, struct contextSet_t* set) {
    
    	g_assert(set);
    	//DEBUG_PC("Checking if contextId: %s in in the ContextList??", contextUuid);
    
    	for (gint i = 0; i < set->num_context_set; i++) { 	
    		struct context_t* c = &(set->contextList[i]);
    		//DEBUG_PC("Context Item [%d] Id: %s", i, c->contextId);
    		if (strcmp(contextUuid, c->contextId) == 0) {
    			//DEBUG_PC("contextId: %s is FOUND in the ContextSet_List", contextUuid);
    			return c;
    		}
    	}
    	//DEBUG_PC("contextId: %s NOT FOUND in the ContextSet_List", contextUuid);
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Add a specific context uuid into the context set
     *
     * @param contextUuid
     * @param set
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct context_t* add_contextId_in_set(gchar *contextUuid, struct contextSet_t *set) {
    
    	set->num_context_set++;
    	struct context_t* c = &(set->contextList[set->num_context_set - 1]);
    	duplicate_string(c->contextId, contextUuid);
    	return c;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Find a vertex in a specific graph
     *
     * @param contextUuid
     * @param set
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct vertices_t* find_vertex_in_graph_context(struct graph_t *g, gchar* deviceId) {
    
    	for (gint i = 0; i < g->numVertices; i++)
    	{
    		struct vertices_t* v = &(g->vertices[i]);
    		if (strcmp(v->verticeId.nodeId, deviceId) == 0) {
    			return v;
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Adding a deviceId into a graph
     *
     * @param g
     * @param deviceId
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct vertices_t* add_vertex_in_graph(struct graph_t* g, gchar* deviceId) {
    	g->numVertices++;
    	struct vertices_t* v = &(g->vertices[g->numVertices - 1]);
    	duplicate_string(v->verticeId.nodeId, deviceId);
    	return v;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Construct the graphs (vertices and edges) bound to every individual context
     *
     * @param cSet
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void build_contextSet_deviceList(struct contextSet_t* cSet) {
    	// Check every device their endpoints
    	for (gint i = 0; i < deviceList->numDevices; i++) {
    		struct device_t* d = &(deviceList->devices[i]);
    		//DEBUG_PC("Exploring DeviceId: %s", d->deviceId);
    
    		// Check the associated endPoints
    		for (gint j = 0; j < d->numEndPoints; j++) {
    			struct endPoint_t* eP = &(d->endPoints[j]);
    			// Get endPointId (topology, context, device Id and endpoint uuid)
    			struct endPointId_t* ePid = &(eP->endPointId);  //end point id
    			//DEBUG_PC("   EndPointId: %s || Type: %s", eP->endPointId.endpoint_uuid, d->deviceType);
    			//DEBUG_PC("   TopologyId: %s || ContextId: %s", eP->endPointId.topology_id.topology_uuid, eP->endPointId.topology_id.contextId);
    
    			// Add contextId in ContextSet and the deviceId (+endpoint) into the vertex set
    			struct context_t *c = find_contextId_in_set(eP->endPointId.topology_id.contextId, cSet);
    			if (c == NULL) {
    				//DEBUG_PC("   contextUuid: %s MUST BE ADDED to ContextSet", eP->endPointId.topology_id.contextId);
    				c = add_contextId_in_set(eP->endPointId.topology_id.contextId, cSet);
    			}
    			// Check if the deviceId and endPointUuid are already considered in the graph of the context c
    			struct vertices_t* v = find_vertex_in_graph_context(&c->g, d->deviceId);
    			if (v == NULL) {
    				//DEBUG_PC("  deviceId: %s MUST BE ADDED to the Context Graph", d->deviceId);
    				v = add_vertex_in_graph(&c->g, d->deviceId);
    			}
    		}
    	}
    	//print_contextSet(cSet);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Determine whether a deviceId is in the targetNode list of a specific vertex v
     *
     * @param v
     * @param deviceId
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct targetNodes_t* find_targeted_vertex_in_graph_context(struct vertices_t* v, gchar *deviceId) {
    	for (gint k = 0; k < v->numTargetedVertices; k++) {
    		struct targetNodes_t* w = &(v->targetedVertices[k]);
    		if (strcmp(w->tVertice.nodeId, deviceId) == 0) {
    			return w;
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Add a deviceId a targetNode of a specific vertex v
     *
     * @param v
     * @param deviceId
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct targetNodes_t* add_targeted_vertex_in_graph_context(struct vertices_t* v, gchar* bDeviceId) {
    	v->numTargetedVertices++;
    	struct targetNodes_t* w = &(v->targetedVertices[v->numTargetedVertices - 1]);
    	duplicate_string(w->tVertice.nodeId, bDeviceId);
    	return w;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Returns the structure of a device endpoint bound to a specific deviceId and endPointId
     *
     * @param devId
     * @param endPointUuid
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct endPoint_t* find_device_tied_endpoint(gchar* devId, gchar* endPointUuid) {
    	//DEBUG_PC("devId: %s ePId: %s", devId, endPointUuid);
    	for (gint i = 0; i < deviceList->numDevices; i++) {
    		struct device_t* d = &(deviceList->devices[i]);
    		if (strcmp(d->deviceId, devId) != 0) {
    			continue;
    		}
    		// Iterate over the endpoints tied to the deviceId
    		for (gint j = 0; j < d->numEndPoints; j++) {
    			struct endPoint_t* eP = &(d->endPoints[j]);
    			//DEBUG_PC("looked endPointId: %s", eP->endPointId.endpoint_uuid);
    			if (strcmp(eP->endPointId.endpoint_uuid, endPointUuid) == 0) {
    				return eP;
    			}
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Adding the edge/linnk in the targetedNodes w list
     *
     * @param w
     * @param l
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void add_edge_in_targetedVertice_set(struct targetNodes_t* w, struct link_t* l) {
    	//DEBUG_PC("\t targetedVertex: %s", w->tVertice.nodeId);
    	w->numEdges++;
    	struct edges_t* e = &(w->edges[w->numEdges - 1]);
    	// Copy the link Id UUID
    	duplicate_string(e->linkId, l->linkId);
    
    	// copy the deviceId and endpointsIds (A --> Z)
    	struct link_endpointId_t* aEndpointId = &(l->linkEndPointId[0]);
    	duplicate_string(e->aNodeId.nodeId, aEndpointId->deviceId);
    	duplicate_string(e->aEndPointId, aEndpointId->endPointId);
    	duplicate_string(e->aTopologyId, aEndpointId->topology_id.topology_uuid);
    
    	struct link_endpointId_t* zEndpointId = &(l->linkEndPointId[1]);
    	duplicate_string(e->zNodeId.nodeId, zEndpointId->deviceId);
    	duplicate_string(e->zEndPointId, zEndpointId->endPointId);
    	duplicate_string(e->zTopologyId, zEndpointId->topology_id.topology_uuid);
    
    	// The potential and available capacity is indeed retrieved using aEndpointId in the deviceList
    	struct endPoint_t* eP = find_device_tied_endpoint(aEndpointId->deviceId, aEndpointId->endPointId);
    	if (eP == NULL) {
    		DEBUG_PC("devId: %s endPointUuid: %s NOT in Device List!!--- Weird", aEndpointId->deviceId, aEndpointId->endPointId);
    		exit(-1);
    	}
    	//Potential(total) and available capacity
    	e->unit = eP->potential_capacity.unit;
    	memcpy(&e->totalCap, &eP->potential_capacity.value, sizeof(gdouble));
    	memcpy(&e->availCap, &eP->available_capacity.value, sizeof(gdouble));
    
    	// Copy interdomain local/remote Ids
    	memcpy(e->interDomain_localId, eP->inter_domain_plug_in.inter_domain_plug_in_local_id, 
    		strlen(eP->inter_domain_plug_in.inter_domain_plug_in_local_id));
    	memcpy(e->interDomain_remoteId, eP->inter_domain_plug_in.inter_domain_plug_in_remote_id,
    		strlen(eP->inter_domain_plug_in.inter_domain_plug_in_remote_id));
    
    	// cost value
    	memcpy(&e->cost, &l->cost_characteristics.cost_value, sizeof(gdouble));
    
    	// latency
    	memcpy(&e->delay, &l->latency_characteristics.fixed_latency, sizeof(gdouble));
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Searching a specific edge/link by the linkId(UUID)
     *
     * @param w
     * @param l
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct edges_t* find_edge_in_targetedVertice_set(struct targetNodes_t* w, struct link_t* l) {
    		
    	for (gint i = 0; i < w->numEdges; i++) {
    		struct edges_t* e = &(w->edges[i]);
    		if (strcmp(e->linkId, l->linkId) == 0) {
    			return e;
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief supporting the construction of the graph per context using the explicit
     * contents/info of the link list
     *
     * @param set
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void build_contextSet_linklList(struct contextSet_t* set) {
    	g_assert(set);
    	
    	// for each link in linkList:
    	// 1st- Retrieve endpoints A --> B feauture (context Id, device Id, endpoint Id)
    	// 2st - In the graph associated to the contextId, check wheter A (deviceId) is in the vertices list
    	// o No, this is weird ... exist
    	// o Yes, get the other link endpoint (i.e., B) and check whether it exists. If NOT add it, considering
    	// all the attributes; Otherwise, check whether the link is different from existing edges between A and B
    
    	for (gint j = 0; j < linkList->numLinks; j++) {
    		struct link_t* l = &(linkList->links[j]);
    		// link assumed to be P2P A --> B; I.e. 2 endPoints; 1st specifies A and 2nd specifie B
    		struct link_endpointId_t* aEndpointId = &(l->linkEndPointId[0]);
    		struct topology_id_t* topologyId = &(aEndpointId->topology_id);
    		// get the contextId
    		gchar contextUuid[UUID_CHAR_LENGTH];
    		duplicate_string(contextUuid, topologyId->contextId);
    		//DEBUG_PC("Link: %s in Context: %s", l->linkId, contextUuid);
    
    		// Check first contextUuid exists in the cSet
    		struct context_t* c = find_contextId_in_set(contextUuid, set);
    		if (c == NULL) {
    			DEBUG_PC("ContextId: %s does NOT exist... weird", contextUuid);
    			exit(-1);
    		}
    
    		// get the device ID of A
    		gchar aDeviceId[UUID_CHAR_LENGTH];
    		duplicate_string(aDeviceId, aEndpointId->deviceId);
    
    		struct graph_t* g = &(c->g); // get the graph associated to the context c
    		struct vertices_t* v = find_vertex_in_graph_context(g, aDeviceId);
    		if (v == NULL) {
    			DEBUG_PC("aDeviceId: %s IS NOT IN Vertices of contextId: %s", aDeviceId, contextUuid);
    			exit(-1);
    		}		
    		// get the bEndpointId
    		struct link_endpointId_t* bEndpointId = &(l->linkEndPointId[1]);
    		gchar bDeviceId[UUID_CHAR_LENGTH];
    		duplicate_string(bDeviceId, bEndpointId->deviceId);
    		// Check whether device B is in the targeted Vertices from A (i.e., v)?
    		// If not, add B in the targeted vertices B + create the edge and add it
    		// If B exist, check whether the explored link/edge is already in the list of edges
    		struct targetNodes_t* w = find_targeted_vertex_in_graph_context(v, bDeviceId);
    		if (w == NULL) {
    			//DEBUG_PC("B device [%s] is PEER of A device [%s]", bDeviceId, v->verticeId.nodeId);
    			w = add_targeted_vertex_in_graph_context(v, bDeviceId);
    			add_edge_in_targetedVertice_set(w, l);
    		}
    		else {
    			// w exists, it is needed to check whether the edge (link) should be added
    			struct edges_t* e = find_edge_in_targetedVertice_set(w, l);
    			if (e == NULL) {
    				// Add the link into the list
    				add_edge_in_targetedVertice_set(w, l);
    			}
    			else {
    				DEBUG_PC("The link already exists ...");
    				continue;
    			}
    		}
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Create the set of (distinct) contexts with the deviceList and linkList
     *
     * @param cSet
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void build_contextSet(struct contextSet_t* cSet) {
    	g_assert(cSet);
    	g_assert(deviceList);
    	g_assert(linkList);
    
    	// devices are tied to contexts, i.e. depending on the contextId of the devices
    	build_contextSet_deviceList(cSet);
    
    	// Once the diverse contexts are created and the devices/endpoints asigned to the 
    	// respective graph tied to each context, it is needed to create the edges
    	build_contextSet_linklList(cSet);
    
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Print the contents of the ContextIds
     *
     * @param set
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_contextSet(struct contextSet_t* set) {
    	g_assert(set);
    
    	for (gint i = 0; i < set->num_context_set; i++) {
    		struct context_t* c = &(set->contextList[i]);
    		DEBUG_PC("-------------------------------------------------------------");
    		DEBUG_PC(" Context Id: %s", c->contextId);
    		DEBUG_PC("-------------------------------------------------------------");
    
    		struct graph_t* g = &(c->g);
    		for (gint j = 0; j < g->numVertices; j++) {
    			struct vertices_t* v = &(g->vertices[j]);
    			DEBUG_PC("  Head Device Id: %s", v->verticeId.nodeId);
    			for (gint k = 0; k < v->numTargetedVertices; k++) {
    				struct targetNodes_t* w = &(v->targetedVertices[k]);
    				DEBUG_PC("  [%d] --- Peer Device Id: %s", k, w->tVertice.nodeId);
    				for (gint l = 0; l < w->numEdges; l++) {
    					struct edges_t* e = &(w->edges[l]);
    					DEBUG_PC("   \t link Id: %s", e->linkId);
    					DEBUG_PC("   \t aEndPointId: %s", e->aEndPointId);
    					DEBUG_PC("   \t zEndPointId: %s", e->zEndPointId);
    					DEBUG_PC("   \t Available Capacity: %f, Latency: %f, Cost: %f", e->availCap, e->delay, e->cost);
    					DEBUG_PC("   \t aTopologyId: %s", e->aTopologyId);
    					DEBUG_PC("   \t zTopologyId: %s", e->zTopologyId);
    				}
    			}
    		}
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Check whether src and dst PE nodeId of the req are the same
     *
     *	@param r
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint same_src_dst_pe_nodeid(struct service_t* s)
    {
    	// Check that source PE and dst PE are NOT the same, i.e., different ingress and egress endpoints (iEp, eEp)
    	struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[0]);
    	struct service_endpoints_id_t* eEp = &(s->service_endpoints_id[1]);
    
    	gchar* iEpUUID = iEp->endpoint_uuid;
    	gchar* eEpUUID = eEp->endpoint_uuid;
    	gchar* iDevUUID = iEp->device_uuid;
    	gchar* eDevUUID = eEp->device_uuid;
    
    	// Compare the device uuids
    	if (strcmp(iDevUUID, eDevUUID) != 0) {
    		DEBUG_PC("DIFFERENT --- iDevId: %s and eDevId: %s", iDevUUID, eDevUUID);
    		return 1;
    	}
    	// Compare the endpoints (ports)
    	if (strcmp(iEpUUID, eEpUUID) != 0) {
    		DEBUG_PC("DIFFERENT --- iEpUUID: %s and eEpUUID: %s", iEpUUID, eEpUUID);
    		return 1;
    	}
    	return 0;	
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Handles issues with the route computation
     *
     *	@param route
     *	@param s
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void comp_route_connection_issue_handler (struct compRouteOutput_t *path, struct service_t *s)
    {
    	g_assert(path);
    	g_assert(s);
    
    	// Increase the number of computed routes/paths despite there was an issue to be reported		
    	path->numPaths++;	
    	// Copy the serviceId
    	copy_service_id(&(path->serviceId), &(s->serviceId));
    
    	// copy the service endpoints, in general, there will be 2 (point-to-point network connectivity services)
    	for (gint i = 0; i < s->num_service_endpoints_id; i++) {
    		struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[i]);
    		struct service_endpoints_id_t* oEp = &(path->service_endpoints_id[i]);
    		copy_service_endpoint_id(oEp, iEp);
    	}
    	path->num_service_endpoints_id = s->num_service_endpoints_id;
    	path->noPathIssue = NO_PATH_CONS_ISSUE;
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief released the allocated memory fo compRouteOutputList_t
     *
     *	@param ro
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void destroy_compRouteOutputList (struct compRouteOutputList_t *ro)
    {
    	g_assert (ro);	
    	g_free (ro);	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief creates a copy of the underlying graph
     *
     *	@param originalGraph
     *	@param destGraph
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void duplicate_graph (struct graph_t *originalGraph, struct graph_t *destGraph)	{
    	g_assert (originalGraph);
    	g_assert (destGraph);
    	
    	destGraph->numVertices = originalGraph->numVertices;
    	for (gint i = 0; i < originalGraph->numVertices; i++) {
    		struct vertices_t *oVertex = &(originalGraph->vertices[i]);
    		struct vertices_t *dVertex = &(destGraph->vertices[i]);
    		dVertex->numTargetedVertices = oVertex->numTargetedVertices;		
    		duplicate_node_id (&oVertex->verticeId, &dVertex->verticeId);
    		
    		for (gint j = 0; j < oVertex->numTargetedVertices; j++)	{
    			struct targetNodes_t *oTargetedVertex = &(oVertex->targetedVertices[j]);
    			struct targetNodes_t *dTargetedVertex = &(dVertex->targetedVertices[j]);
    			duplicate_node_id (&oTargetedVertex->tVertice, &dTargetedVertex->tVertice);
    			dTargetedVertex->numEdges = oTargetedVertex->numEdges;
    			
    			for (gint k = 0; k < oTargetedVertex->numEdges; k++) {
    				struct edges_t *oEdge = &(oTargetedVertex->edges[k]);
    				struct edges_t *dEdge = &(dTargetedVertex->edges[k]);
    				duplicate_edge (dEdge, oEdge);						
    			}
    		}	
    	}	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to retrieve from the graph the edge instance associated to the
     * pathLink (pL)
     *
     *	@param pL
     *	@parma g
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct edges_t* get_edge_from_graph_by_linkId(struct pathLink_t* pL, struct graph_t* g) {
    	g_assert(pL);
    	g_assert(g);
    
    	for (gint i = 0; i < g->numVertices; i++) {
    		struct vertices_t* v = &(g->vertices[i]);
    		for (gint j = 0; j < v->numTargetedVertices; j++) {
    			struct targetNodes_t* tv = &(v->targetedVertices[j]);
    			for (gint k = 0; k < tv->numEdges; k++) {
    				struct edges_t* e = &(tv->edges[k]);
    				if (strcmp(e->linkId, pL->linkId) == 0) {
    					return e;
    				}
    			}		
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to retrieve from the graph the reverse edge (rev_e) associated to an edge (e)
     *
     *	@param e
     *	@parma g
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    struct edges_t* get_reverse_edge_from_the_graph(struct edges_t* e, struct graph_t* g) {
    	g_assert(e);
    	g_assert(g);
    
    	for (gint i = 0; i < g->numVertices; i++) {
    		struct vertices_t* v = &(g->vertices[i]);
    		// Check Route Element zNodeId with the v->verticeId
    		if (compare_node_id(&e->zNodeId, &v->verticeId) != 0)
    			continue;
    		// Check Route Element zNodeis with any of reachable targeted vertices from v
    		gboolean foundTargVert = FALSE;
    		gint indexTargVert = -1;
    		for (gint j = 0; j < v->numTargetedVertices; j++) {
    			struct targetNodes_t* tv = &(v->targetedVertices[j]);
    			if (compare_node_id(&e->aNodeId, &tv->tVertice) == 0)
    			{
    				foundTargVert = TRUE;
    				indexTargVert = j;
    				break;
    			}
    		}
    		if (foundTargVert == FALSE) {
    			 continue;
    		}			
    
    		// The targeted vertice is found, then check matching with the endpoints
    		struct targetNodes_t* tv = &(v->targetedVertices[indexTargVert]);
    		for (gint k = 0; k < tv->numEdges; k++) {
    			struct edges_t* rev_e = &(tv->edges[k]);
    			if ((strcmp(rev_e->aEndPointId, e->zEndPointId) == 0) &&
    				(strcmp(rev_e->zEndPointId, e->aEndPointId) == 0)) {
    				return rev_e;
    			}				
    		}
    	}
    	return NULL;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to reflect in the graph the assigned/allocated resources contained in the path p
     * 	considering the needs (e.g., bandwidth) of service s
     *
     *	@param p
     *	@param s
     *	@parma g
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void allocate_graph_resources (struct path_t *p, struct service_t *s, struct graph_t *g)
    {
    	g_assert (p);
    	g_assert (s);
    	g_assert (g);
    	// Retrieve the requested bw by the service
    	struct path_constraints_t* pathCons = get_path_constraints(s);
    
    	for (gint i = 0; i < p->numPathLinks; i++) {
    		struct pathLink_t* pL = &(p->pathLinks[i]);
    		// get the edge associated to the linkId in the graph
    		struct edges_t* e = get_edge_from_graph_by_linkId(pL, g);
    		if (e == NULL) {
    			DEBUG_PC("The linkId: %s is NOT found in the Graph!!!", pL->linkId);
    			exit(-1);
    		}
    		//Update the availBw in the edge
    		gdouble resBw = e->availCap - pathCons->bwConstraint;
    		DEBUG_PC("Updating the Avail Bw @ edge/link: %s", e->linkId);
    		DEBUG_PC("Initial avaiCap @ e/link: %f, demanded Bw: %f, resulting Avail Bw: %f", e->availCap, pathCons->bwConstraint, resBw);
    		memcpy(&e->availCap, &resBw, sizeof(gdouble));
    		DEBUG_PC("Final e/link avail Bw: %f", e->availCap);	
    	}
    	g_free(pathCons);
    	
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to reflect in the graph the assigned/allocated resources contained in the reverse direction of the path p
     * 	considering the needs (e.g., bandwidth) of service s
     *
     *	@param p
     *	@param s
     *	@parma g
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2021
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void allocate_graph_reverse_resources(struct path_t* p, struct service_t * s, struct graph_t* g)
    {
    	g_assert(p);
    	g_assert(s);
    	g_assert(g);
    
    	struct path_constraints_t* pathCons = get_path_constraints(s);
    	for (gint i = 0; i < p->numPathLinks; i++) {
    		struct pathLink_t* pL = &(p->pathLinks[i]);
    		struct edges_t* e = get_edge_from_graph_by_linkId(pL, g);
    		if (e == NULL) {
    			DEBUG_PC("The linkId: %s is NOT found in the Graph!!!", pL->linkId);
    			exit(-1);
    		}
    		struct edges_t* rev_e = get_reverse_edge_from_the_graph(e, g);
    		if (rev_e == NULL) {
    			DEBUG_PC("the reverse edge of linkId: %s is NOT found in the Graph!!!", pL->linkId);
    			exit(-1);
    		}
    		//Update the availBw in the edge
    		gdouble resBw = rev_e->availCap - pathCons->bwConstraint;
    		DEBUG_PC("Updating the Avail Bw @ reverse edge/link: %s", rev_e->linkId);
    		DEBUG_PC("Initial avaiCap @ reverse edge e/link: %f, demanded Bw: %f, resulting Avail Bw: %f", rev_e->availCap, pathCons->bwConstraint, resBw);
    		memcpy(&rev_e->availCap, &resBw, sizeof(gdouble));
    		DEBUG_PC("Final reverse edge e/link avail Bw: %f", rev_e->availCap);
    	}
    	g_free(pathCons);
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief Function used to printall the computed paths for the requested network connectivity services
     *
     *	@param routeList
     *
     * 	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2021
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void print_path_connection_list(struct compRouteOutputList_t* routeList) {
    	g_assert(routeList);
    	for (gint i = 0; i < routeList->numCompRouteConnList; i++) {
    		DEBUG_PC("==================== Service Item: %d ===================", i);
    		struct compRouteOutput_t* rO = &(routeList->compRouteConnection[i]);
    		DEBUG_PC("num service endpoints: %d", rO->num_service_endpoints_id);
    		struct serviceId_t* s = &(rO->serviceId);
    		DEBUG_PC("ContextId: %s, ServiceId: %s", s->contextId, s->service_uuid);
    		DEBUG_PC("ingress --- %s [%s]", rO->service_endpoints_id[0].device_uuid, 
    			rO->service_endpoints_id[0].endpoint_uuid);
    		DEBUG_PC("egress --- %s [%s]", rO->service_endpoints_id[1].device_uuid,
    			rO->service_endpoints_id[1].endpoint_uuid);
    
    		if (rO->noPathIssue == NO_PATH_CONS_ISSUE) {
    			DEBUG_PC("NO PATH SUCCESSFULLY COMPUTED");
    			continue;
    		}
    		// Path
    		DEBUG_PC("Number of paths: %d", rO->numPaths);
    		for (gint j = 0; j < rO->numPaths; j++) {
    			struct path_t* p = &(rO->paths[j]);
    			print_path_t(p);
    		}
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_tools.c
     * 	@brief update statistics for the path computation operations
     *
     *  @param routeConnList
     *	@param d
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2021
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void update_stats_path_comp(struct compRouteOutputList_t* routeConnList, struct timeval d, gint numSuccesPathComp, gint numPathCompIntents) {
    	g_assert(routeConnList);
    
    	total_path_comp_time.tv_sec = total_path_comp_time.tv_sec + d.tv_sec;
    	total_path_comp_time.tv_usec = total_path_comp_time.tv_usec + d.tv_usec;
    	total_path_comp_time = tv_adjust(total_path_comp_time);
    
    	gdouble path_comp_time_msec = (((total_path_comp_time.tv_sec) * 1000) + ((total_path_comp_time.tv_usec) / 1000));
    	gdouble av_alg_comp_time = ((path_comp_time_msec / numSuccesPathComp));
    	DEBUG_PC("\t --- STATS PATH COMP ----");
    	DEBUG_PC("Succesfully Comp: %d | Path Comp Requests: %d", numSuccesPathComp, numPathCompIntents);
    	DEBUG_PC("AV. PATH COMP ALG. TIME: %f ms", av_alg_comp_time);
    
    	for (gint i = 0; i < serviceList->numServiceList; i++) {
    		struct service_t* s = &(serviceList->services[i]);
    		char* eptr;
    		for (gint j = 0; j < s->num_service_constraints; j++) {
    			struct constraint_t* constraints = &(s->constraints[j]);
    			if (strncmp((const char*)(constraints->constraint_type), "bandwidth", 9) == 0) {
    				totalReqBw += (gdouble)(strtod((char*)constraints->constraint_value, &eptr));
    			}
    		}
    	}
    	for (gint k = 0; k < routeConnList->numCompRouteConnList; k++) {
    		struct compRouteOutput_t* rO = &(routeConnList->compRouteConnection[k]);
    		if (rO->noPathIssue == NO_PATH_CONS_ISSUE) {
    			continue;
    		}
    		// Get the requested service bw bound to that computed path
    		struct path_t* p = &(rO->paths[0]);
    		struct service_t* s = get_service_for_computed_path(rO->serviceId.service_uuid);
    		if (s == NULL) {
    			DEBUG_PC("Weird the service associated to a path is not found");
    			exit(-1);
    		}
    		for (gint l = 0; l < s->num_service_constraints; l++) {
    			struct constraint_t* constraints = &(s->constraints[l]);
    			char* eptr;
    			if (strncmp((const char*)(constraints->constraint_type), "bandwidth", 9) == 0) {
    				totalServedBw += (gdouble)(strtod((char*)constraints->constraint_value, &eptr));
    			}
    		}
    	}
    	gdouble avServedRatio = totalServedBw / totalReqBw;
    	DEBUG_PC("AV. Served Ratio: %f", avServedRatio);
    	gdouble avBlockedBwRatio = (gdouble)(1.0 - avServedRatio);
    	DEBUG_PC("AV. BBE: %f", avBlockedBwRatio);
    	return;
    
    }