/* BEGIN_ICS_COPYRIGHT7 ****************************************

Copyright (c) 2015-2020, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

** END_ICS_COPYRIGHT7   ****************************************/

/* [ICS VERSION STRING: unknown] */

#include "topology.h"
#include "topology_internal.h"
#include <stl_helper.h>
#include <stl_convertfuncs.h>


#define	VTIMER_1S		    1000000ull
#define VTIMER_1_MILLISEC           1000ull
#define VTIMER_10_MILLISEC          10000ull
#define VTIMER_100_MILLISEC         100000ull
#define VTIMER_200_MILLISEC         200000ull
#define VTIMER_500_MILLISEC         500000ull
#define RESP_WAIT_TIME		    1000

#define CL_MAX_THREADS              4

typedef struct clListSearchData_s {
   cl_map_item_t      AllListEntry; // key is numeric id
} clListSearchData_t; 

#ifndef __VXWORKS__
typedef struct clThreadContext_s {
   FabricData_t *fabricp; 
   clGraphData_t *graphp; 
   uint32 srcHcaListLength; 
   LIST_ITEM *srcHcaList;
   uint32 threadId; 
   int verbose;
   int quiet; 
   uint32 numVertices; 
   uint8 threadExit; 
   uint64_t sTime, eTime; 
   FSTATUS threadStatus;
   ValidateCLTimeGetCallback_t timeGetCallback;
   uint32 usedSLs;
} clThreadContext_t; 

pthread_mutex_t g_cl_lock; 

static uint32 present_routes = 0, missing_routes = 0; 
static uint32 hops_histogram_entries = 0, hops_histogram_length = 0; 
static uint32 *hops_histogram = NULL; 
static cl_qmap_t hopsHistogramLstMap; 
static cl_qmap_t *routesLstMap; 

#endif

PortData *GetMapEntry(FabricData_t *fabricp, STL_LID lid)
{
	if (lid > TOPLM_LID_MAX) return NULL;
	if (!fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)]) return NULL;
	return fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)][TOPLM_ENTRY_NUM(lid)];
}

FSTATUS SetMapEntry(FabricData_t *fabricp, STL_LID lid, PortData *pd)
{
	if (lid > TOPLM_LID_MAX) return FERROR;
	if (!fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)]) {
		if (!pd) return FSUCCESS;

		fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)] =
			(PortData **)MemoryAllocate2AndClear(sizeof(PortData *)*(TOPLM_ENTRIES), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
		if (!fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)]) {
			// Failed to allocate
			return FERROR;
		}
	}
	fabricp->u.LidMap.LidBlocks[TOPLM_BLOCK_NUM(lid)][TOPLM_ENTRY_NUM(lid)] = pd;
	return FSUCCESS;
}

void FreeLidMap(FabricData_t *fabricp)
{
	int i;
	for (i = 0; i < TOPLM_BLOCKS; i++) {
		if (fabricp->u.LidMap.LidBlocks[i]) {
			MemoryDeallocate(fabricp->u.LidMap.LidBlocks[i]);
			fabricp->u.LidMap.LidBlocks[i] = NULL;
		}
	}
}

#if !defined(VXWORKS) || defined(BUILD_DMC)
static int CompareIOC(IN const uint64 key1, IN const uint64 key2)
{
	IocData *iocCompare = (IocData *)(key1);
	IocData *iocKey = (IocData *)(key2);
	if(iocKey->IocProfile.IocGUID == iocCompare->IocProfile.IocGUID) {
		if(iocKey->ioup->nodep->NodeInfo.NodeGUID == iocCompare->ioup->nodep->NodeInfo.NodeGUID)
			return 0;
		else if(iocKey->ioup->nodep->NodeInfo.NodeGUID < iocCompare->ioup->nodep->NodeInfo.NodeGUID)
			return 1;
		else
			return -1;
	}
	else if(iocKey->IocProfile.IocGUID < iocCompare->IocProfile.IocGUID)
		return 1;
	else
		return -1;
}
#endif

// only FF_LIDARRAY,FF_PMADIRECT,FF_SMADIRECT,FF_DOWNPORTINFO,FF_CABLELOWPAGE
// flags are used, others ignored
FSTATUS InitFabricData(FabricData_t *fabricp, FabricFlags_t flags)
{
	MemoryClear(fabricp, sizeof(*fabricp));
	cl_qmap_init(&fabricp->AllNodes, NULL);
	if (!(flags & FF_LIDARRAY)) {
		cl_qmap_init(&fabricp->u.AllLids, NULL);
	}
	fabricp->ms_timeout = RESP_WAIT_TIME;
	fabricp->flags = flags & (FF_LIDARRAY|FF_PMADIRECT|FF_SMADIRECT|FF_DOWNPORTINFO|FF_CABLELOWPAGE);
	cl_qmap_init(&fabricp->AllSystems, NULL);
	cl_qmap_init(&fabricp->ExpectedNodeGuidMap, NULL);
	QListInitState(&fabricp->AllPorts);
	if (! QListInit(&fabricp->AllPorts)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->ExpectedLinks);
	if (! QListInit(&fabricp->ExpectedLinks)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->AllFIs);
	if (! QListInit(&fabricp->AllFIs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->AllSWs);
	if (! QListInit(&fabricp->AllSWs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->AllVFs);
	if (! QListInit(&fabricp->AllVFs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
#if !defined(VXWORKS) || defined(BUILD_DMC)
	QListInitState(&fabricp->AllIOUs);
	if (! QListInit(&fabricp->AllIOUs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
#endif
	QListInitState(&fabricp->ExpectedFIs);
	if (! QListInit(&fabricp->ExpectedFIs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->ExpectedSWs);
	if (! QListInit(&fabricp->ExpectedSWs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
	QListInitState(&fabricp->ExpectedSMs);
	if (! QListInit(&fabricp->ExpectedSMs)) {
		fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname);
		goto fail;
	}
#if !defined(VXWORKS) || defined(BUILD_DMC)
	cl_qmap_init(&fabricp->AllIOCs, CompareIOC);
#endif
	cl_qmap_init(&fabricp->AllSMs, NULL);

	// MC routes related structures
	QListInitState(&fabricp->AllMcGroups);
	if (!QListInit(&fabricp->AllMcGroups)) {
		fprintf(stderr, "%s: Unable to initialize List of mcast Members\n", g_Top_cmdname);
		goto fail;
	}

        // credit-loop related lists
        cl_qmap_init(&fabricp->map_guid_to_ib_device, NULL); 
        QListInitState(&fabricp->FIs); 
        if (!QListInit(&fabricp->FIs)) {
           fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname); 
           goto fail;
        }
        QListInitState(&fabricp->Switches); 
        if (!QListInit(&fabricp->Switches)) {
           fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname); 
           goto fail;
        }
        cl_qmap_init(&fabricp->Graph.Arcs, NULL); 
        cl_qmap_init(&fabricp->Graph.map_arc_key_to_arc, NULL); 

	return FSUCCESS;

fail:
	if (flags & FF_LIDARRAY) {
		FreeLidMap(fabricp);
	}
	return FERROR;

}

// create SystemData as needed
// add this Node to the appropriate System
// This should only be invoked once per node (eg. not per NodeRecord)
FSTATUS AddSystemNode(FabricData_t *fabricp, NodeData *nodep)
{
	SystemData* systemp;
	cl_map_item_t *mi;
	FSTATUS status;
	uint64 key;

	systemp = (SystemData *)MemoryAllocate2AndClear(sizeof(SystemData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! systemp) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	cl_qmap_init(&systemp->Nodes, NULL);
	systemp->SystemImageGUID = nodep->NodeInfo.SystemImageGUID;
	key = systemp->SystemImageGUID;
	if (! key)
		key = nodep->NodeInfo.NodeGUID;

	// There can be more than 1 node per system, only create 1 SystemData
	mi = cl_qmap_insert(&fabricp->AllSystems, key, &systemp->AllSystemsEntry);
	if (mi != &systemp->AllSystemsEntry)
	{
		MemoryDeallocate(systemp);
		systemp = PARENT_STRUCT(mi, SystemData, AllSystemsEntry);
	}

	nodep->systemp = systemp;
	if (cl_qmap_insert(&systemp->Nodes, nodep->NodeInfo.NodeGUID, &nodep->SystemNodesEntry) != &nodep->SystemNodesEntry)
	{
		fprintf(stderr, "%s: Duplicate NodeGuids found in nodeRecords: 0x%"PRIx64"\n",
					   g_Top_cmdname, nodep->NodeInfo.NodeGUID);
		goto fail;
	}
	status = FSUCCESS;
done:
	return status;

fail:
	status = FERROR;
	goto done;
}

/* build the fabricp->AllPorts, ALLFIs, and AllSWs lists such that
 * AllPorts is sorted by NodeGUID, PortNum
 * AllFIs, ALLSWs, AllIOUs is sorted by NodeGUID
 */
void BuildFabricDataLists(FabricData_t *fabricp)
{
	cl_map_item_t *p;

	// this list may already be filled when parsing Port definitions
	boolean AllPortsIsEmpty = QListIsEmpty(&fabricp->AllPorts);

	for (p=cl_qmap_head(&fabricp->AllNodes); p != cl_qmap_end(&fabricp->AllNodes); p = cl_qmap_next(p)) {
		NodeData *nodep = PARENT_STRUCT(p, NodeData, AllNodesEntry);
		cl_map_item_t *q;

		switch (nodep->NodeInfo.NodeType) {
		case STL_NODE_FI:
			QListInsertTail(&fabricp->AllFIs, &nodep->AllTypesEntry);
			break;
		case STL_NODE_SW:
			QListInsertTail(&fabricp->AllSWs, &nodep->AllTypesEntry);
			break;
		default:
			fprintf(stderr, "%s: Ignoring Unknown node type: 0x%x\n",
					g_Top_cmdname, nodep->NodeInfo.NodeType);
			break;
		}
#if !defined(VXWORKS) || defined(BUILD_DMC)
		if (nodep->ioup) {
			QListInsertTail(&fabricp->AllIOUs, &nodep->ioup->AllIOUsEntry);
		}
#endif

		if (AllPortsIsEmpty) {
			for (q=cl_qmap_head(&nodep->Ports); q != cl_qmap_end(&nodep->Ports); q = cl_qmap_next(q)) {
				PortData *portp = PARENT_STRUCT(q, PortData, NodePortsEntry);
				QListInsertTail(&fabricp->AllPorts, &portp->AllPortsEntry);
			}
		}
	}
}

PortSelector* GetPortSelector(PortData *portp)
{
	ExpectedLink *elinkp = portp->elinkp;
	if (elinkp) {
		if (elinkp->portp1 == portp)
			return elinkp->portselp1;
		if (elinkp->portp2 == portp)
			return elinkp->portselp2;
	}
	return NULL;
}

// Determine if portp and its neighbor are an internal link within a single
// system.  Note that some 3rd party products report SystemImageGuid of 0 in
// which case we can only consider links between the same node as internal links
boolean isInternalLink(PortData *portp)
{
	PortData *neighbor = portp->neighbor;

	return (neighbor
			&& portp->nodep->NodeInfo.SystemImageGUID
				== neighbor->nodep->NodeInfo.SystemImageGUID
			&& (portp->nodep->NodeInfo.SystemImageGUID
				|| portp->nodep->NodeInfo.NodeGUID
					== neighbor->nodep->NodeInfo.NodeGUID));
}

// Determine if portp and its neighbor are an Inter-switch Link
boolean isISLink(PortData *portp)
{
	PortData *neighbor = portp->neighbor;

	return (neighbor
			&& portp->nodep->NodeInfo.NodeType == STL_NODE_SW
			&& neighbor->nodep->NodeInfo.NodeType == STL_NODE_SW);
}

// Determine if portp or its neighbor is an FI
boolean isFILink(PortData *portp)
{
	PortData *neighbor = portp->neighbor;

	return (neighbor
			&& (portp->nodep->NodeInfo.NodeType == STL_NODE_FI
				|| neighbor->nodep->NodeInfo.NodeType == STL_NODE_FI));
}

// The routines below are primarily for use when classifying ExpectedLinks
// which did not resolve properly.  So we want them to be a little loose
// and select a link which is expected to be of the given type based on input
// or where the port resolved for either side is connected to a real link of
// the given type.

// Due to this more inclusive definition, oriented toward verification,
// isExternal is not the same as ! isInternal
// Given this definition both isExternal and isInternal could be true for
// the same resolved or vaguely defined link, such as when the elinkp
// has not specified if its internal or external (default is ! internal)
// and the elink is resolved to two disconnected ports where one is an internal
// link and the other is an external link.
// Note: Currently there is no use case for an isInternalExpectedLink function
// and given the defaulting behavior of elinkp->internal such a function would
// not be implementable under this inclusive definition.
//
// Similarly isISExpectedLink is not the same as ! isFIExpectedLink
// isIS will include a link where expected link or either resolved real link
// have at least 1 switch to switch link, even if other expected or real links
// are FI links.  Similarly isFIExpectedLink will match any link where an FI is
// expected on either side or is found on either side of a resolved real link.
// As such the case of an expected link which is SW-SW which matches two
// unrelated ports where one port is part of a FI-SW link and the other is
// part of a SW-SW link, will return true for both functions.
// Note portselp*->NodeType is optional (default NONE) and can resolve a port
// without matching NodeType.  So it is even possible for an expected link
// of SW-SW to resolve to a FI-SW link (verify reports will report the
// incorrect node types as part of fully verifying the link)

// Determine if elinkp is an external link within a single system.
boolean isExternalExpectedLink(ExpectedLink *elinkp)
{
	return (
			// elinkp was specified as external (or Internal not specified)
			(! elinkp->internal)
			// either real port resolved to the elinkp is an external link
			|| (elinkp->portp1 && ! isInternalLink(elinkp->portp1))
			|| (elinkp->portp2 && ! isInternalLink(elinkp->portp2))
		);
}

// Determine if elinkp is an inter-switch link
boolean isISExpectedLink(ExpectedLink *elinkp)
{
	return (
			// elinkp was expicitly specified as an ISL
			((elinkp->portselp1 && elinkp->portselp1->NodeType == STL_NODE_SW)
			 && (elinkp->portselp2 && elinkp->portselp2->NodeType == STL_NODE_SW))
			// either real port resolved to the elinkp is an ISL link
			|| (elinkp->portp1 && isISLink(elinkp->portp1))
			|| (elinkp->portp2 && isISLink(elinkp->portp2))
		);
}

// Determine if elinkp is an FI link
boolean isFIExpectedLink(ExpectedLink *elinkp)
{
	return (
			// either side of elinkp was specified as an FI
			(elinkp->portselp1 && elinkp->portselp1->NodeType == STL_NODE_FI)
			|| (elinkp->portselp2 && elinkp->portselp2->NodeType == STL_NODE_FI)
			// either real port resolved to a link including an FI
			|| (elinkp->portp1 && isFILink(elinkp->portp1))
			|| (elinkp->portp2 && isFILink(elinkp->portp2))
		);
}

// Lookup PKey
// ignores the Full/Limited bit, only checks low 15 bits for a match
// returns index of pkey in overall table or -1 if not found
// if the Partition Table for the port is not available, returns -1
int FindPKey(PortData *portp, uint16 pkey)
{
	uint16 ix, ix_capacity;
	STL_PKEY_ELEMENT *pPartitionTable = portp->pPartitionTable;

	if (! pPartitionTable)
		return -1;
	ix_capacity = PortPartitionTableSize(portp);
	for (ix = 0; ix < ix_capacity; ix++)
	{
		if ((pPartitionTable[ix].AsReg16 & 0x7FFF) == (pkey & 0x7FFF))
			return ix;
	}
	return -1;
}

// Determine if the given port is a member of the given vFabric
// We don't really have all the right data here, especially if the FM has
// combined multiple vFabrics into the same SL and PKey
// but for most cases, we can safely conclude that if the port has the SL and
// PKey configured it is a member of the vFabric.
// This function will return FALSE if QoS data or SL2SCMap is not available
// or if the port is not Armed/Active.
// Given the current FM implementation (and the dependency on SL2SCMap), this
// routine will return FALSE if invoked for non-endpoints
boolean isVFMember(PortData *portp, VFData_t *pVFData) 
{
	STL_VFINFO_RECORD *pR = &pVFData->record;
	uint8 sl = pR->s1.slBase;
	uint8 slResp = (pR->slResponseSpecified? pR->slResponse: sl);
	uint8 slMcast = (pR->slMulticastSpecified? pR->slMulticast: sl);

	// VF only valid if port initialized
	if (! IsPortInitialized(portp->PortInfo.PortStates))
		return FALSE;
	// there is no saquery to find out if a port is a member of a vfabric
	// so a port is assumed to be a member of a vfabric if:
	//	for the base SL of the vfabric, that port has an SC assigned (SC!=15)
	//	and the port has the VF's pkey

	if (! portp->pQOS || ! portp->pQOS->SL2SCMap)
		return FALSE;

	if (portp->pQOS->SL2SCMap->SLSCMap[sl].SC == 15 &&
		portp->pQOS->SL2SCMap->SLSCMap[slResp].SC == 15 &&
		portp->pQOS->SL2SCMap->SLSCMap[slMcast].SC == 15)
		return FALSE;

	return (-1 != FindPKey(portp, pR->pKey));
}

// count the number of armed/active links in the node
uint32 CountInitializedPorts(FabricData_t *fabricp, NodeData *nodep)
{
	cl_map_item_t *p;
	uint32 count = 0;
	for (p=cl_qmap_head(&nodep->Ports); p != cl_qmap_end(&nodep->Ports); p = cl_qmap_next(p))
	{
		PortData *portp = PARENT_STRUCT(p, PortData, NodePortsEntry);
		if (IsPortInitialized(portp->PortInfo.PortStates))
			count++;
	}
	return count;
}

void PortDataFreeQOSData(FabricData_t *fabricp, PortData *portp)
{
	if (portp->pQOS) {
		QOSData *pQOS = portp->pQOS;

		LIST_ITEM *p;
		int i;
		for (i=0; i< SC2SCMAPLIST_MAX; i++) {
			while (!QListIsEmpty(&pQOS->SC2SCMapList[i])) {
				p = QListTail(&pQOS->SC2SCMapList[i]);
				PortMaskSC2SCMap *pSC2SC = (PortMaskSC2SCMap *)QListObj(p);
				QListRemoveTail(&pQOS->SC2SCMapList[i]);
				MemoryDeallocate(pSC2SC->SC2SCMap);
				pSC2SC->SC2SCMap = NULL;
				MemoryDeallocate(pSC2SC);
			}
		}

		if (pQOS->SL2SCMap)
			MemoryDeallocate(pQOS->SL2SCMap);
		if (pQOS->SC2SLMap)
			MemoryDeallocate(pQOS->SC2SLMap);

		MemoryDeallocate(pQOS);
	}
	portp->pQOS = NULL;
}

void PortDataFreeBufCtrlTable(FabricData_t *fabricp, PortData *portp)
{
	if (portp->pBufCtrlTable) {
		MemoryDeallocate(portp->pBufCtrlTable);
	}
	portp->pBufCtrlTable = NULL;
}


void PortDataFreePartitionTable(FabricData_t *fabricp, PortData *portp)
{
	if (portp->pPartitionTable) {
		MemoryDeallocate(portp->pPartitionTable);
	}
	portp->pPartitionTable = NULL;
}


void PortDataFreeCableInfoData(FabricData_t *fabricp, PortData *portp)
{
	if (portp->pCableInfoData) {
		MemoryDeallocate(portp->pCableInfoData);
	}
	portp->pCableInfoData = NULL;
}

void PortDataFreeCongestionControlTableEntries(FabricData_t *fabricp, PortData *portp)
{
	if (portp->pCongestionControlTableEntries) {
		MemoryDeallocate(portp->pCongestionControlTableEntries);
	}
	portp->pCongestionControlTableEntries = NULL;
}

void AllLidsRemove(FabricData_t *fabricp, PortData *portp)
{
	if (! portp->PortGUID)
		return;	// quietly ignore
	switch (portp->PortInfo.PortStates.s.PortState) {
	default:
	case IB_PORT_DOWN:
	case IB_PORT_INIT:
		// may be in AllLids
		if (FindLid(fabricp, portp->EndPortLID) != portp)
			return;
		break;
	case IB_PORT_ARMED:
	case IB_PORT_ACTIVE:
		// should be in AllLids
		break;
	}
	if (fabricp->flags & FF_LIDARRAY) {
		STL_LID first_lid = portp->EndPortLID;
		STL_LID last_lid = portp->EndPortLID |
					((1<<portp->PortInfo.s1.LMC)-1);
		while (first_lid <= last_lid) {
			SetMapEntry(fabricp, first_lid++, NULL);
		}
	} else {
		cl_qmap_remove_item(&fabricp->u.AllLids, &portp->AllLidsEntry);
	}
	fabricp->lidCount -= (1<<portp->PortInfo.s1.LMC);
}

void PartialLidsRemove(FabricData_t *fabricp,  PortData *portp, uint32 count)
{
	STL_LID first_lid = portp->EndPortLID;
	STL_LID last_lid = portp->EndPortLID + count;
	while (first_lid < last_lid) {
		SetMapEntry(fabricp, first_lid++, NULL);
		fabricp->lidCount--;
	}
}

// add the LID for a non-down port to the LID lists
// does nothing for ports in DOWN or INIT state
FSTATUS AllLidsAdd(FabricData_t *fabricp, PortData *portp, boolean force)
{
	cl_map_item_t *mi;

	if (! portp->PortGUID)
		return FSUCCESS;	// quietly ignore

	// we are less strict about switch port 0.  We have seen odd state's
	// reported and unfortunately SM just passes them along.
	// Since Port 0 is in SM DB it must have a trustable LID (note that
	// for simulator, PortDataStateChanged will only call this for Armed/Active)
	if (IB_PORT_ACTIVE != portp->PortInfo.PortStates.s.PortState
		&& IB_PORT_ARMED != portp->PortInfo.PortStates.s.PortState
		&& ! (portp->nodep->NodeInfo.NodeType == STL_NODE_SW
				&& 0 == portp->PortNum
			   	&& IB_PORT_NOP == portp->PortInfo.PortStates.s.PortState))
		return FSUCCESS;	// quietly ignore
	if (portp->EndPortLID > TOPLM_LID_MAX)
		return FERROR;
	if (fabricp->flags & FF_LIDARRAY) {
		STL_LID perm_first_lid = portp->EndPortLID;
		STL_LID first_lid = portp->EndPortLID;
		STL_LID last_lid = portp->EndPortLID |
					((1<<portp->PortInfo.s1.LMC)-1);
		FSTATUS status = FSUCCESS;

		for(;first_lid <= last_lid;first_lid++) {
			PortData *testportp = GetMapEntry(fabricp, first_lid);
			if(testportp != portp) {
				if(testportp != NULL) {
					status = FDUPLICATE;
					if (! force) {
						PartialLidsRemove(fabricp, portp, first_lid - perm_first_lid);
						return status;
					} else {
						AllLidsRemove(fabricp, testportp);
					}
				}
				fabricp->lidCount++;
				SetMapEntry(fabricp, first_lid, portp);
			}
		}
		return status;
	} else {
		// TBD does not verify potential overlap of lid+(1<<lmc)-1 range
		// lidCount will produce incorrect results in case of such overlap
		mi = cl_qmap_insert(&fabricp->u.AllLids, portp->EndPortLID, &portp->AllLidsEntry);
		if (mi != &portp->AllLidsEntry) {
			if (force) {
				AllLidsRemove(fabricp, PARENT_STRUCT(mi, PortData, AllLidsEntry));
				mi = cl_qmap_insert(&fabricp->u.AllLids, portp->EndPortLID, &portp->AllLidsEntry);
				ASSERT(mi == &portp->AllLidsEntry);
				fabricp->lidCount += (1<<portp->PortInfo.s1.LMC);
			}
			return FDUPLICATE;
		} else {
			fabricp->lidCount += (1<<portp->PortInfo.s1.LMC);
			return FSUCCESS;
		}
	}
}

STL_SCSCMAP * QOSDataLookupSCSCMap(PortData *portp, uint8_t outport, int extended) {
	LIST_ITEM *p;
	PortMaskSC2SCMap *pSC2SC;
	QOSData *pQOS = portp->pQOS;

	if (!pQOS)
		return NULL;
	if ((extended >= SC2SCMAPLIST_MAX) || (extended < 0))
		return NULL;
 
	for (p = QListHead(&pQOS->SC2SCMapList[extended]); p != NULL; p = QListNext(&pQOS->SC2SCMapList[extended], p)) {
		pSC2SC = (PortMaskSC2SCMap *)QListObj(p);

		if (StlIsPortInPortMask(pSC2SC->outports, outport))
			return (pSC2SC->SC2SCMap);
	}
	return NULL;
}

// Add a SCSC table to QOS data
// If a matching SCSC table already exists, add egress to its port mask
// Otherwise, create a new entry in the SCSCMap list for this table
void QOSDataAddSCSCMap(PortData *portp, uint8_t outport, int extended, const STL_SCSCMAP *pSCSC) {
	LIST_ITEM *p;
	QOSData *pQOS = portp->pQOS;
	PortMaskSC2SCMap *pSC2SC2;
	PortMaskSC2SCMap *pEmptySC2SC2 = NULL;
	int entryFound = 0;

	if (!pQOS)
		return;
	if ((extended >= SC2SCMAPLIST_MAX) || (extended < 0))
		return;

	for (p = QListHead(&pQOS->SC2SCMapList[extended]); p != NULL; p = QListNext(&pQOS->SC2SCMapList[extended], p)) {
		pSC2SC2 = (PortMaskSC2SCMap *)QListObj(p);

		if (!memcmp(pSC2SC2->SC2SCMap, pSCSC, sizeof(STL_SCSCMAP))) {
			StlAddPortToPortMask(pSC2SC2->outports, outport);
			entryFound = 1;
		} else if (StlIsPortInPortMask(pSC2SC2->outports, outport)) {
			// the maps don't match but the port does, remove & replace with new map
			StlClearPortInPortMask(pSC2SC2->outports, outport);
			if (StlNumPortsSetInPortMask(pSC2SC2->outports, portp->nodep->NodeInfo.NumPorts) ==0) {
				pEmptySC2SC2 = pSC2SC2;
			}
		}
	}

	if (entryFound) {
		if(pEmptySC2SC2) {
			QListRemoveItem(&pQOS->SC2SCMapList[extended], &pEmptySC2SC2->SC2SCMapListEntry);
		}
		return;
	}

	pSC2SC2 = pEmptySC2SC2;
	if (!pSC2SC2) {
		// never found a matching map, create a new one
		pSC2SC2 = (PortMaskSC2SCMap *)MemoryAllocate2AndClear(sizeof(PortMaskSC2SCMap), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
		if (!pSC2SC2) {
			// memory error
			fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
			return;
		}

		ListItemInitState(&pSC2SC2->SC2SCMapListEntry);
		QListSetObj(&pSC2SC2->SC2SCMapListEntry, pSC2SC2);

		pSC2SC2->SC2SCMap = (STL_SCSCMAP *)MemoryAllocate2AndClear(sizeof(STL_SCSCMAP), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
		if (!pSC2SC2->SC2SCMap) {
			// memory error
			fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
			return;
		}
		QListInsertTail(&pQOS->SC2SCMapList[extended], &pSC2SC2->SC2SCMapListEntry);
	}

	memcpy(pSC2SC2->SC2SCMap, pSCSC, sizeof(STL_SCSCMAP));
	StlAddPortToPortMask(pSC2SC2->outports, outport);
}

// Set new Port Info based on a Set(PortInfo).  For use by fabric simulator
// assumes pInfo already validated and any Noop fields filled in with correct
// values.
// MODIFIED FOR STL
void PortDataStateChanged(FabricData_t *fabricp, PortData *portp)
{
	if (IB_PORT_ACTIVE == portp->PortInfo.PortStates.s.PortState
		|| IB_PORT_ARMED == portp->PortInfo.PortStates.s.PortState) {
		if (FSUCCESS != AllLidsAdd(fabricp, portp, TRUE)) {
			fprintf(stderr, "%s: Overwrote Duplicate LID found in portRecords: PortGUID 0x%016"PRIx64" LID 0x%x Port %u Node %.*s\n",
				g_Top_cmdname,
				portp->PortGUID, portp->EndPortLID,
				portp->PortNum, STL_NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)portp->nodep->NodeDesc.NodeString);
		}
	} else {
		AllLidsRemove(fabricp, portp);
	}

}

// Set new Port Info based on a Set(PortInfo).  For use by fabric simulator
// assumes pInfo already validated and any Noop fields filled in with correct
// values.
// MODIFIED FOR STL
void PortDataSetPortInfo(FabricData_t *fabricp, PortData *portp, STL_PORT_INFO *pInfo)
{
	portp->PortInfo = *pInfo;
	if (portp->PortGUID) {
		portp->EndPortLID = pInfo->LID;
		/* Only port 0 in a switch has a GUID.  It's LID is the switch's LID */
		if (portp->nodep->pSwitchInfo) {
			cl_map_item_t *q;
			NodeData *nodep = portp->nodep;

			nodep->pSwitchInfo->RID.LID = pInfo->LID;
			/* also set EndPortLID in all of switch's PortInfoRecords */
			for (q=cl_qmap_head(&nodep->Ports); q != cl_qmap_end(&nodep->Ports); q = cl_qmap_next(q)) {
				PortData *p = PARENT_STRUCT(q, PortData, NodePortsEntry);
				p->EndPortLID = pInfo->LID;
			}
		}
	}
	PortDataStateChanged(fabricp, portp);
}

// remove Port from lists and free it
// Only removes from AllLids and NodeData.Ports, caller must remove from
// any other lists if called after BuildFabricLists
void PortDataFree(FabricData_t *fabricp, PortData *portp)
{
	NodeData *nodep = portp->nodep;

	if (portp->context && g_Top_FreeCallbacks.pPortDataFreeCallback)
		(*g_Top_FreeCallbacks.pPortDataFreeCallback)(fabricp, portp);

	if (portp->PortGUID)
		AllLidsRemove(fabricp, portp);
	cl_qmap_remove_item(&nodep->Ports, &portp->NodePortsEntry);
	if (portp->pPortCounters)
		MemoryDeallocate(portp->pPortCounters);
	PortDataFreeQOSData(fabricp, portp);
	PortDataFreeBufCtrlTable(fabricp, portp);
	PortDataFreePartitionTable(fabricp, portp);


	PortDataFreeCableInfoData(fabricp, portp);
	PortDataFreeCongestionControlTableEntries(fabricp, portp);
	MemoryDeallocate(portp);
}

FSTATUS PortDataAllocateQOSData(FabricData_t *fabricp, PortData *portp)
{
	QOSData *pQOS;
	int i;

	ASSERT(! portp->pQOS);	// or could free if present
	portp->pQOS = (QOSData *)MemoryAllocate2AndClear(sizeof(QOSData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp->pQOS) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}
	pQOS = portp->pQOS;
	if (portp->nodep->NodeInfo.NodeType == STL_NODE_SW && portp->PortNum) {
		for (i=0; i<SC2SCMAPLIST_MAX; i++) {
		// external switch ports get SC2SC map
			QListInitState(&pQOS->SC2SCMapList[i]);
			if (!QListInit(&pQOS->SC2SCMapList[i])) {
				fprintf(stderr, "%s: Unable to initialize SC2SCMaps member list\n", g_Top_cmdname);
				goto fail;
			}
		}
	} else {
		// HFI and Switch Port 0 get SL2SC and SC2SL
		pQOS->SL2SCMap = (STL_SLSCMAP *)MemoryAllocate2AndClear(sizeof(STL_SLSCMAP), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
		if (! pQOS->SL2SCMap) {
			fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
			goto fail;
		}

		pQOS->SC2SLMap = (STL_SCSLMAP *)MemoryAllocate2AndClear(sizeof(STL_SCSLMAP), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
		if (!pQOS->SC2SLMap) {
			goto fail;
		}
	}

	return FSUCCESS;
fail:
	PortDataFreeQOSData(fabricp, portp);
	return FINSUFFICIENT_MEMORY;
}

FSTATUS PortDataAllocateAllQOSData(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	FSTATUS status = FSUCCESS;

	for (p=QListHead(&fabricp->AllPorts); p != NULL; p = QListNext(&fabricp->AllPorts, p)) {
		PortData *portp = (PortData *)QListObj(p);
		FSTATUS s;
		s = PortDataAllocateQOSData(fabricp, portp);
		if (FSUCCESS != s)
			status = s;
	}
	return status;
}

FSTATUS PortDataAllocateBufCtrlTable(FabricData_t *fabricp, PortData *portp)
{
	ASSERT(! portp->pBufCtrlTable);	// or could free if present
	portp->pBufCtrlTable = MemoryAllocate2AndClear(sizeof(STL_BUFFER_CONTROL_TABLE), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp->pBufCtrlTable) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	return FSUCCESS;
fail:
	//PortDataFreeBufCtrlTable(fabricp, portp);
	return FINSUFFICIENT_MEMORY;
}


FSTATUS PortDataAllocateAllBufCtrlTable(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	FSTATUS status = FSUCCESS;

	for (p=QListHead(&fabricp->AllPorts); p != NULL; p = QListNext(&fabricp->AllPorts, p)) {
		PortData *portp = (PortData *)QListObj(p);
		FSTATUS s;
		s = PortDataAllocateBufCtrlTable(fabricp, portp);
		if (FSUCCESS != s)
			status = s;
	}
	return status;
}

uint16 PortPartitionTableSize(PortData *portp)
{
	NodeData *nodep = portp->nodep;
	if (nodep->NodeInfo.NodeType == STL_NODE_SW && portp->PortNum) {
		// Switch External Ports table size is defined in SwitchInfo
		if (! nodep->pSwitchInfo) {
			// guess the limits, we don't have SwitchInfo
			// or we haven't yet read it from the snapshot while parsing
			return nodep->NodeInfo.PartitionCap;
		} else {
			return nodep->pSwitchInfo->SwitchInfoData.PartitionEnforcementCap;
		}
	} else {
		// Switch Port 0 and FI table size is defined by NodeInfo
		return nodep->NodeInfo.PartitionCap;
	}
}

FSTATUS PortDataAllocatePartitionTable(FabricData_t *fabricp, PortData *portp)
{
	uint16 size;

	ASSERT(! portp->pPartitionTable);	// or could free if present
	size = PortPartitionTableSize(portp);
	portp->pPartitionTable = (STL_PKEY_ELEMENT *)MemoryAllocate2AndClear(sizeof(STL_PKEY_ELEMENT)*size, IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp->pPartitionTable) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	return FSUCCESS;
fail:
	//PortDataFreePartitionTable(fabricp, portp);
	return FINSUFFICIENT_MEMORY;
}

FSTATUS PortDataAllocateAllPartitionTable(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	FSTATUS status = FSUCCESS;

	for (p=QListHead(&fabricp->AllPorts); p != NULL; p = QListNext(&fabricp->AllPorts, p)) {
		PortData *portp = (PortData *)QListObj(p);
		FSTATUS s;
		s = PortDataAllocatePartitionTable(fabricp, portp);
		if (FSUCCESS != s)
			status = s;
	}
	return status;
}


FSTATUS PortDataAllocateCableInfoData(FabricData_t *fabricp, PortData *portp)
{
	uint16 size = STL_CIB_STD_LEN;
	ASSERT(! portp->pCableInfoData);	// or could free if present

	//Data in Low address space of Cable info is also accesed for Cable Health Report
	if (fabricp) {
		if (fabricp->flags & FF_CABLELOWPAGE)
			size = STL_CABLE_INFO_DATA_SIZE * 4;    // 2 blocks of lower page 0 and 2 blocks of upper page 0
	}

	portp->pCableInfoData = MemoryAllocate2AndClear(size, IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp->pCableInfoData) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	return FSUCCESS;
fail:
	//PortDataFreeCableInfoData(fabricp, portp);
	return FINSUFFICIENT_MEMORY;
}

FSTATUS PortDataAllocateAllCableInfo(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	FSTATUS status = FSUCCESS;

	for (p=QListHead(&fabricp->AllPorts); p != NULL; p = QListNext(&fabricp->AllPorts, p)) {
		PortData *portp = (PortData *)QListObj(p);
		FSTATUS s;
		// skip switch port 0
		if (! portp->PortNum)
			continue;
		s = PortDataAllocateCableInfoData(fabricp, portp);
		if (FSUCCESS != s)
			status = s;
	}
	return status;
}

FSTATUS PortDataAllocateCongestionControlTableEntries(FabricData_t *fabricp, PortData *portp)
{
	ASSERT(! portp->pCongestionControlTableEntries);	// or could free if present
	if (! portp->nodep->CongestionInfo.ControlTableCap)
		return FSUCCESS;
	portp->pCongestionControlTableEntries = MemoryAllocate2AndClear(
							sizeof(STL_HFI_CONGESTION_CONTROL_TABLE_ENTRY)
								* STL_NUM_CONGESTION_CONTROL_ELEMENTS_BLOCK_ENTRIES
								* portp->nodep->CongestionInfo.ControlTableCap,
							IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp->pCongestionControlTableEntries) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	return FSUCCESS;
fail:
	//PortDataFreeCongestionControlTableEntries(fabricp, portp);
	return FINSUFFICIENT_MEMORY;
}

FSTATUS PortDataAllocateAllCongestionControlTableEntries(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	FSTATUS status = FSUCCESS;

	for (p=QListHead(&fabricp->AllPorts); p != NULL; p = QListNext(&fabricp->AllPorts, p)) {
		PortData *portp = (PortData *)QListObj(p);
		FSTATUS s;
		// only applicable to HFIs and enhanced switch port 0
		if (portp->nodep->NodeInfo.NodeType == STL_NODE_SW
			&& portp->PortNum)
			continue;
		if (! portp->nodep->CongestionInfo.ControlTableCap)
			continue;
		s = PortDataAllocateCongestionControlTableEntries(fabricp, portp);
		if (FSUCCESS != s)
			status = s;
	}
	return status;
}

// guid is the PortGUID as found in corresponding NodeRecord
// we adjust as needed to account for Switch Ports (only switch port 0 has guid)
PortData* NodeDataAddPort(FabricData_t *fabricp, NodeData *nodep, EUI64 guid, STL_PORTINFO_RECORD *pPortInfo)
{
	PortData *portp;

	portp = (PortData*)MemoryAllocate2AndClear(sizeof(PortData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! portp) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	ListItemInitState(&portp->AllPortsEntry);
	QListSetObj(&portp->AllPortsEntry, portp);
	portp->PortInfo = pPortInfo->PortInfo;
	portp->EndPortLID = pPortInfo->RID.EndPortLID;
	memcpy(portp->LinkDownReasons, pPortInfo->LinkDownReasons, STL_NUM_LINKDOWN_REASONS * sizeof(STL_LINKDOWN_REASON));

	if (nodep->NodeInfo.NodeType == STL_NODE_SW) {
		// a switch only gets 1 port Guid, we save it for switch
		// port 0 (the "virtual management port")
		portp->PortNum = pPortInfo->RID.PortNum;
		portp->PortGUID = portp->PortNum?0:guid;
	} else {
		portp->PortNum = pPortInfo->PortInfo.LocalPortNum;
		portp->PortGUID = guid;
	}
	portp->rate = StlLinkSpeedWidthToStaticRate(
					(portp->PortInfo.LinkSpeed.Active),
					(portp->PortInfo.LinkWidth.Active));
	portp->nodep = nodep;
	//DisplayPortInfoRecord(&pPortInfoRecords[i], 0);
	//printf("process PortNumber: %u\n", portp->PortNum);

	if (cl_qmap_insert(&nodep->Ports, portp->PortNum, &portp->NodePortsEntry) != &portp->NodePortsEntry)
	{
		fprintf(stderr, "%s: Duplicate PortNums found in portRecords: LID 0x%x Port %u Node: %.*s\n",
					   	g_Top_cmdname,
					   	portp->EndPortLID,
					   	portp->PortNum, STL_NODE_DESCRIPTION_ARRAY_SIZE,
						(char*)nodep->NodeDesc.NodeString);
		MemoryDeallocate(portp);
		goto fail;
	}
	if (FSUCCESS != AllLidsAdd(fabricp, portp, FALSE))
	{
		fprintf(stderr, "%s: Duplicate LIDs found in portRecords: PortGUID 0x%016"PRIx64" LID 0x%x Port %u Node %.*s\n",
			   	g_Top_cmdname,
			   	portp->PortGUID, portp->EndPortLID,
			   	portp->PortNum, STL_NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)nodep->NodeDesc.NodeString);
		cl_qmap_remove_item(&nodep->Ports, &portp->NodePortsEntry);
		MemoryDeallocate(portp);
		goto fail;
	}
	//DisplayPortInfoRecord(pPortInfo, 0);

	return portp;

fail:
	return NULL;
}

FSTATUS NodeDataSetSwitchInfo(NodeData *nodep, STL_SWITCHINFO_RECORD *pSwitchInfo)
{
	ASSERT(! nodep->pSwitchInfo);
	nodep->pSwitchInfo = (STL_SWITCHINFO_RECORD*)MemoryAllocate2AndClear(sizeof(STL_SWITCHINFO_RECORD), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! nodep->pSwitchInfo) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		return FERROR;
	}
	// leave the extra vendor specific fields zeroed
	nodep->pSwitchInfo->RID.LID = pSwitchInfo->RID.LID;
	nodep->pSwitchInfo->SwitchInfoData = pSwitchInfo->SwitchInfoData;
	return FSUCCESS;
}

// TBD - routines to add/set IOC and IOU information

NodeData *FabricDataAddNode(FabricData_t *fabricp, STL_NODE_RECORD *pNodeRecord, boolean *new_nodep)
{
	NodeData *nodep = (NodeData*)MemoryAllocate2AndClear(sizeof(NodeData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	cl_map_item_t *mi;
	boolean new_node = TRUE;

	if (! nodep) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}
	//printf("process NodeRecord LID: 0x%x\n", pNodeRecords->RID.s.LID);
	//DisplayNodeRecord(pNodeRecord, 0);

	cl_qmap_init(&nodep->Ports, NULL);

        nodep->NodeInfo = pNodeRecord->NodeInfo;

	nodep->NodeDesc = pNodeRecord->NodeDesc;

	// zero out port specific fields, the PortData will handle these
	nodep->NodeInfo.PortGUID=0;
	nodep->NodeInfo.u1.s.LocalPortNum=0;
	ListItemInitState(&nodep->AllTypesEntry);
	QListSetObj(&nodep->AllTypesEntry, nodep);

	// when sweeping we get 1 NodeRecord per port on a node, so only save
	// 1 NodeData structure per node and discard the duplicates
	mi = cl_qmap_insert(&fabricp->AllNodes, nodep->NodeInfo.NodeGUID, &nodep->AllNodesEntry);
	if (mi != &nodep->AllNodesEntry)
	{
		MemoryDeallocate(nodep);
		nodep = PARENT_STRUCT(mi, NodeData, AllNodesEntry);
		new_node = FALSE;
	}

	if (new_node) {
		if (FSUCCESS != AddSystemNode(fabricp, nodep)) {
			cl_qmap_remove_item(&fabricp->AllNodes, &nodep->AllNodesEntry);
			MemoryDeallocate(nodep);
			goto fail;
		}
	}

	if (new_nodep)
		*new_nodep = new_node;
	return nodep;

fail:
	return NULL;
}


#ifdef PRODUCT_OPENIB_FF


McGroupData *FabricDataAddMCGroup(FabricData_t *fabricp, struct omgt_port *port, int quiet, IB_MCMEMBER_RECORD *pMCGRecord,
		boolean *new_nodep, FILE *verbose_file)
{
	FSTATUS  status;
	McGroupData *mcgroupp = (McGroupData*)MemoryAllocate2AndClear(sizeof(McGroupData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	boolean new_node = TRUE;

	if (! mcgroupp) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		return NULL;
	}

	// init list of McGroupMembers
	QListInitState(&mcgroupp->AllMcGroupMembers);
	if ( !QListInit(&mcgroupp->AllMcGroupMembers)) {
		fprintf(stderr, "%s: Unable to initialize MCGroup member list\n", g_Top_cmdname);
		MemoryDeallocate(mcgroupp);
		return NULL;
	}

	// init list of switches in a group
	QListInitState(&mcgroupp->EdgeSwitchesInGroup);
	if ( !QListInit(&mcgroupp->EdgeSwitchesInGroup)) {
		fprintf(stderr, "%s: Unable to initialize list of Switches in a MC group\n", g_Top_cmdname);
		MemoryDeallocate(mcgroupp);
		return NULL;
	}
	mcgroupp->MGID = pMCGRecord->RID.MGID;
	mcgroupp->MLID = MCAST16_TO_MCAST32(pMCGRecord->MLID);


	ListItemInitState(&mcgroupp->AllMcGMembersEntry);
	QListSetObj(&mcgroupp->AllMcGMembersEntry, mcgroupp);

	// search members of the group using opasaquery -o mcmember -m nodep->MLID
	// and add them to the list of group members
	status = GetAllMCGroupMember(fabricp, mcgroupp, port, quiet, verbose_file);

	if (status != FSUCCESS) {
		fprintf(stderr, "%s: Unable to get MCGroup member list\n", g_Top_cmdname);
		MemoryDeallocate(mcgroupp);
		if (status == FINSUFFICIENT_MEMORY)
			fprintf(stderr, "%s: Insufficient memory to allocate MC Member\n", g_Top_cmdname);
			return NULL;
	}

	// this part needs to be optimized
	LIST_ITEM *p;
	boolean found = FALSE;
	p=QListHead(&fabricp->AllMcGroups);

// insert everything in the fabric structure ordered by MGID
	while (!found && p != NULL) {
		McGroupData *pMCG = (McGroupData *)QListObj(p);
		if ( pMCG->MLID > mcgroupp->MLID)
			p = QListNext(&fabricp->AllMcGroups, p);
		else {
			// insert mcmember element
				QListInsertNext(&fabricp->AllMcGroups,p, &mcgroupp->AllMcGMembersEntry);
				found = TRUE;
		}
	} //end while

	if (!found)
			QListInsertTail(&fabricp->AllMcGroups, &mcgroupp->AllMcGMembersEntry);

	if (new_nodep)
		*new_nodep = new_node;

	return mcgroupp;
}

#endif


FSTATUS AddEdgeSwitchToGroup(FabricData_t *fabricp, McGroupData *mcgroupp, NodeData *groupswitch, uint8 SWentryport)
{
	LIST_ITEM *p;
	boolean found;
	FSTATUS status;

	// this linear insertion needs to be optimized
	found = FALSE;
	p=QListHead(&mcgroupp->EdgeSwitchesInGroup);
	while (!found && (p != NULL)) {
		McEdgeSwitchData *pSW = (McEdgeSwitchData *)QListObj(p);
		if	(pSW->NodeGUID == groupswitch->NodeInfo.NodeGUID)
			found = TRUE;
		else
			p = QListNext(&mcgroupp->EdgeSwitchesInGroup, p);
	}
	if (!found) {
			McEdgeSwitchData *mcsw = (McEdgeSwitchData*)MemoryAllocate2AndClear(sizeof(McEdgeSwitchData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
			if (! mcsw) {
				status = FINSUFFICIENT_MEMORY;
				return status;
			}
			mcsw->NodeGUID = groupswitch->NodeInfo.NodeGUID;
			mcsw->pPort = FindPortGuid(fabricp, groupswitch->NodeInfo.NodeGUID );
			mcsw->EntryPort = SWentryport;
			QListSetObj(&mcsw->McEdgeSwitchEntry, mcsw);
			QListInsertTail(&mcgroupp->EdgeSwitchesInGroup, &mcsw->McEdgeSwitchEntry);
	}
	return FSUCCESS;
}

FSTATUS FabricDataAddLink(FabricData_t *fabricp, PortData *p1, PortData *p2)
{
	// The Link Records will be reported in both directions
	// so discard duplicates
	if (p1->neighbor == p2 && p2->neighbor == p1)
		goto fail;
	if (p1->neighbor) {
		fprintf(stderr, "%s: Skipping Duplicate Link Record reference to port: LID 0x%x Port %u Node %.*s\n",
			   	g_Top_cmdname,
			   	p1->EndPortLID,
			   	p1->PortNum, NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p1->nodep->NodeDesc.NodeString);
		goto fail;
	}
	if (p2->neighbor) {
		fprintf(stderr, "%s: Skipping Duplicate Link Record reference to port: LID 0x%x Port %u Node %.*s\n",
			   	g_Top_cmdname,
			   	p2->EndPortLID,
			   	p2->PortNum, NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p2->nodep->NodeDesc.NodeString);
		goto fail;
	}
	//DisplayLinkRecord(pLinkRecord, 0);
	p1->neighbor = p2;
	p2->neighbor = p1;
	// for consistency between runs, we always report the "from" port
	// as the one with lower numbered NodeGUID/PortNum
	if (p1->nodep->NodeInfo.NodeGUID != p2->nodep->NodeInfo.NodeGUID) {
		if (p1->nodep->NodeInfo.NodeGUID < p2->nodep->NodeInfo.NodeGUID) {
			p1->from = 1;
			p2->from = 0;
		} else {
			p1->from = 0;
			p2->from = 1;
		}
	} else {
		if (p1->PortNum < p2->PortNum) {
			p1->from = 1;
			p2->from = 0;
		} else {
			p1->from = 0;
			p2->from = 1;
		}
	}
	if (p1->rate != p2->rate) {
		fprintf(stderr, "\n%s: Warning: Ignoring Inconsistent Active Speed/Width for link between:\n", g_Top_cmdname);
		fprintf(stderr, "  %4s 0x%016"PRIx64" %3u %s %.*s\n",
				StlStaticRateToText(p1->rate),
				p1->nodep->NodeInfo.NodeGUID,
				p1->PortNum,
				StlNodeTypeToText(p1->nodep->NodeInfo.NodeType),
				NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p1->nodep->NodeDesc.NodeString);
		fprintf(stderr, "  %4s 0x%016"PRIx64" %3u %s %.*s\n",
				StlStaticRateToText(p2->rate),
				p2->nodep->NodeInfo.NodeGUID,
				p2->PortNum,
				StlNodeTypeToText(p2->nodep->NodeInfo.NodeType),
				NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p2->nodep->NodeDesc.NodeString);
	}
	++(fabricp->LinkCount);
	if (! isInternalLink(p1))
		++(fabricp->ExtLinkCount);
	if (isFILink(p1))
		++(fabricp->FILinkCount);
	if (isISLink(p1))
		++(fabricp->ISLinkCount);
	if (! isInternalLink(p1)&& isISLink(p1))
		++(fabricp->ExtISLinkCount);

	return FSUCCESS;

fail:
	return FERROR;
}

FSTATUS FabricDataAddLinkRecord(FabricData_t *fabricp, STL_LINK_RECORD *pLinkRecord)
{
	PortData *p1, *p2;

	//printf("process LinkRecord LID: 0x%x\n", pLinkRecord->RID.s.LID);
	p1 = FindLidPort(fabricp, pLinkRecord->RID.FromLID, pLinkRecord->RID.FromPort);
	if (! p1) {
		fprintf(stderr, "%s: Can't find \"From\" Lid 0x%x Port %u: Skipping\n",
				g_Top_cmdname,
				pLinkRecord->RID.FromLID, pLinkRecord->RID.FromPort);
		return FERROR;
	}
	p2 = FindLidPort(fabricp, pLinkRecord->ToLID, pLinkRecord->ToPort);
	if (! p2) {
		fprintf(stderr, "%s: Can't find \"To\" Lid 0x%x Port %u: Skipping\n",
				g_Top_cmdname,
				pLinkRecord->ToLID, pLinkRecord->ToPort);
		return FERROR;
	}
	return FabricDataAddLink(fabricp, p1, p2);
}

// This allows a link to be removed, its mainly a helper for manual
// construction of topologies which need to "undo links" they created
FSTATUS FabricDataRemoveLink(FabricData_t *fabricp, PortData *p1)
{
	PortData *p2 = p1->neighbor;

	if (! p1->neighbor)
		goto fail;
	if (p1 == p2) {
		fprintf(stderr, "%s: Corrupted Topology, port linked to self: LID 0x%x Port %u Node %.*s\n",
			   	g_Top_cmdname,
			   	p1->EndPortLID,
			   	p1->PortNum, NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p1->nodep->NodeDesc.NodeString);
		goto fail;
	}
	if (p2->neighbor != p1) {
		fprintf(stderr, "%s: Corrupted Topology, port linked inconsistently: LID 0x%x Port %u Node %.*s\n",
			   	g_Top_cmdname,
			   	p1->EndPortLID,
			   	p1->PortNum, NODE_DESCRIPTION_ARRAY_SIZE,
				(char*)p1->nodep->NodeDesc.NodeString);
		goto fail;
	}
	--(fabricp->LinkCount);
	if (! isInternalLink(p1))
		--(fabricp->ExtLinkCount);
	if (isFILink(p1))
		--(fabricp->FILinkCount);
	if (isISLink(p1))
		--(fabricp->ISLinkCount);
	if (! isInternalLink(p1)&& isISLink(p1))
		--(fabricp->ExtISLinkCount);
	p1->rate = 0;
	p1->neighbor = NULL;
	p1->from = 0;
	p2->rate = 0;
	p2->neighbor = NULL;
	p2->from = 0;

	return FSUCCESS;

fail:
	return FERROR;
}
#if !defined(VXWORKS) || defined(BUILD_DMC)
// remove Ioc from lists and free it
// Only removes from AllIOCs and IouData.Iocs, caller must remove from
// any other lists if called after BuildFabricLists
void IocDataFree(FabricData_t *fabricp, IocData *iocp)
{
	IouData *ioup = iocp->ioup;

	if (iocp->context && g_Top_FreeCallbacks.pIocDataFreeCallback)
		(*g_Top_FreeCallbacks.pIocDataFreeCallback)(fabricp, iocp);

	QListRemoveItem(&ioup->Iocs, &iocp->IouIocsEntry);
	cl_qmap_remove_item(&fabricp->AllIOCs, &iocp->AllIOCsEntry);
	if (iocp->Services)
		MemoryDeallocate(iocp->Services);
	MemoryDeallocate(iocp);
}

// Free all parsed Iocs for this Iou
// Only removes Iocs from AllIOCs and IouData.Iocs, caller must remove from
// any other lists if called after BuildFabricLists
void IouDataFreeIocs(FabricData_t *fabricp, IouData *ioup)
{
	LIST_ITEM *p;

	// this list is sorted/keyed by NodeGUID
	for (p=QListHead(&ioup->Iocs); p != NULL; p = QListHead(&ioup->Iocs)) {
		IocDataFree(fabricp, (IocData*)QListObj(p));
	}
}

// remove Iou from NodeData.ioup and free it and its IOCs
// Only removes Iocs from AllIOCs and IouData.Iocs, caller must remove from
// any other lists if called after BuildFabricLists
void IouDataFree(FabricData_t *fabricp, IouData *ioup)
{
	if (ioup->context && g_Top_FreeCallbacks.pIouDataFreeCallback)
		(*g_Top_FreeCallbacks.pIouDataFreeCallback)(fabricp, ioup);

	IouDataFreeIocs(fabricp, ioup);
	if (ioup->nodep && ioup->nodep->ioup)
		ioup->nodep->ioup = NULL;
	MemoryDeallocate(ioup);
}
#endif
// Free all ports for this node
// Only removes ports from AllLids and NodeData.Ports, caller must remove from
// any other lists if called after BuildFabricLists
void NodeDataFreePorts(FabricData_t *fabricp, NodeData *nodep)
{
	cl_map_item_t *p;

	// this list is sorted/keyed by NodeGUID
	for (p=cl_qmap_head(&nodep->Ports); p != cl_qmap_end(&nodep->Ports); p = cl_qmap_head(&nodep->Ports)) {
		PortDataFree(fabricp, PARENT_STRUCT(p, PortData, NodePortsEntry));
	}
}


void NodeDataFreeSwitchData(FabricData_t *fabricp, NodeData *nodep)
{
	uint8_t i;

	if (nodep->switchp) {
		SwitchData *switchp = nodep->switchp;

		if (switchp->LinearFDB)
			MemoryDeallocate(switchp->LinearFDB);
		if (switchp->PortGroupFDB)
			MemoryDeallocate(switchp->PortGroupFDB);
		if (switchp->PortGroupElements)
			MemoryDeallocate(switchp->PortGroupElements);
		for (i = 0; i < STL_NUM_MFT_POSITIONS_MASK; ++i)
			if (switchp->MulticastFDB[i])
				MemoryDeallocate(switchp->MulticastFDB[i]);


		MemoryDeallocate(switchp);
	}
	nodep->switchp = NULL;
}


FSTATUS NodeDataAllocateFDB(FabricData_t *fabricp, NodeData *nodep,
							uint32 LinearFDBSize) {

	if (NodeDataSwitchResizeLinearFDB(nodep, LinearFDBSize) != FSUCCESS) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		return FINSUFFICIENT_MEMORY;
	}


	return FSUCCESS;
}

FSTATUS NodeDataAllocateSwitchData(FabricData_t *fabricp, NodeData *nodep,
				uint32 LinearFDBSize, uint32 MulticastFDBSize)
{
	SwitchData *switchp;

	if (nodep->switchp)	{
		// Free prior switch data
		NodeDataFreeSwitchData(fabricp, nodep);
	}

	nodep->switchp = (SwitchData *)MemoryAllocate2AndClear(sizeof(SwitchData), IBA_MEM_FLAG_PREMPTABLE, MYTAG);
	if (! nodep->switchp) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		goto fail;
	}

	switchp = nodep->switchp;

	if (NodeDataAllocateFDB(fabricp, nodep, LinearFDBSize) != FSUCCESS) {
		goto fail;
	}

	if (NodeDataSwitchResizeMcastFDB(nodep, MulticastFDBSize) != FSUCCESS) {
		goto fail;
	}

	// These are for the new STL PG Adaptative routing. Note that we allocate
	// in multiples of full "blocks" which simplifies sending PG MADs to the
	// switches and supports up to 64 ports per port group. Supporting more
	// than 64 ports will require a different memory management technique.
	switchp->PortGroupSize = ROUNDUP(nodep->pSwitchInfo->SwitchInfoData.PortGroupCap, NUM_PGT_ELEMENTS_BLOCK);
	switchp->PortGroupElements = 
		MemoryAllocate2AndClear(switchp->PortGroupSize * 
			sizeof(STL_PORTMASK), IBA_MEM_FLAG_PREMPTABLE, MYTAG);



	return FSUCCESS;
fail:
	NodeDataFreeSwitchData(fabricp, nodep);
	return FINSUFFICIENT_MEMORY;
}



//----------------------------------------------------------------------------

FSTATUS NodeDataSwitchResizeMcastFDB(NodeData * nodep, uint32 newMfdbSize)
{
	int errStatus = FINSUFFICIENT_MEMORY;
	int i;
	STL_PORTMASK * newMfdb[STL_NUM_MFT_POSITIONS_MASK] = {NULL};
	assert(nodep->NodeInfo.NodeType == STL_NODE_SW && nodep->switchp && nodep->pSwitchInfo);

	STL_SWITCH_INFO * switchInfo = &nodep->pSwitchInfo->SwitchInfoData;

	STL_LID newMtop = switchInfo->MulticastFDBTop;

	if (newMfdbSize > 0) {
		newMfdbSize = MIN(newMfdbSize, switchInfo->MulticastFDBCap);

		for (i = 0; i < STL_NUM_MFT_POSITIONS_MASK; ++i) {
			newMfdb[i] = (STL_PORTMASK*) MemoryAllocate2AndClear(newMfdbSize * sizeof(STL_PORTMASK),
				IBA_MEM_FLAG_PREMPTABLE, MYTAG);
			if (!newMfdb[i]) goto fail;
		}

		if (switchInfo->MulticastFDBTop >= STL_LID_MULTICAST_BEGIN) {
			size_t size = MIN(newMfdbSize, switchInfo->MulticastFDBTop - STL_LID_MULTICAST_BEGIN + 1);

			for (i = 0; i < STL_NUM_MFT_POSITIONS_MASK; ++i) {
				if (!nodep->switchp->MulticastFDB[i])
					continue;

				memcpy(newMfdb[i], nodep->switchp->MulticastFDB[i], size * sizeof(STL_PORTMASK));
				newMtop = MIN((size - 1) + STL_LID_MULTICAST_BEGIN, newMtop);
			}
		}
	}

	if (newMfdbSize) {
		for (i = 0; i < STL_NUM_MFT_POSITIONS_MASK; ++i) {
			if (nodep->switchp->MulticastFDB[i]) {
				MemoryDeallocate(nodep->switchp->MulticastFDB[i]);
				nodep->switchp->MulticastFDB[i] = NULL;
			}
		}

		memcpy(nodep->switchp->MulticastFDB, newMfdb, sizeof(newMfdb));
		switchInfo->MulticastFDBTop = newMtop;

		if (newMtop >= STL_LID_MULTICAST_BEGIN)
			nodep->switchp->MulticastFDBSize = newMtop + 1;
		else
			nodep->switchp->MulticastFDBSize = newMfdbSize + STL_LID_MULTICAST_BEGIN;

	}

	return FSUCCESS;
fail:
	for (i = 0; i < STL_NUM_MFT_POSITIONS_MASK; ++i) {
		if (newMfdb[i]) {
			MemoryDeallocate(newMfdb[i]);
			newMfdb[i] = NULL;
		}
	}

	return errStatus;
}

FSTATUS NodeDataSwitchResizeLinearFDB(NodeData * nodep, uint32 newLfdbSize)
{
	int errStatus = FINSUFFICIENT_MEMORY;
	uint32 newPgdbSize;
	STL_LINEAR_FORWARDING_TABLE * newLfdb = NULL;
	STL_PORT_GROUP_FORWARDING_TABLE * newPgdb = NULL;
	DEBUG_ASSERT(nodep->NodeInfo.NodeType == STL_NODE_SW && nodep->switchp && nodep->pSwitchInfo);

	STL_SWITCH_INFO * switchInfo = &nodep->pSwitchInfo->SwitchInfoData;

	STL_LID newLtop = switchInfo->LinearFDBTop;

	if (newLfdbSize > 0 &&
		switchInfo->RoutingMode.Enabled == STL_ROUTE_LINEAR) {
		newLfdbSize = MIN(newLfdbSize, switchInfo->LinearFDBCap);
		newLfdb = (STL_LINEAR_FORWARDING_TABLE*) MemoryAllocate2AndClear(
			ROUNDUP(newLfdbSize, MAX_LFT_ELEMENTS_BLOCK), IBA_MEM_FLAG_PREMPTABLE, MYTAG);

		if (!newLfdb) goto fail;

		memset(newLfdb, 0xff, ROUNDUP(newLfdbSize, MAX_LFT_ELEMENTS_BLOCK));
		if (nodep->switchp->LinearFDB) {
			size_t size = MIN(newLfdbSize, switchInfo->LinearFDBTop + 1);
			memcpy(newLfdb, nodep->switchp->LinearFDB, size * sizeof(PORT));
			newLtop = MIN(size - 1, newLtop);
		}

		// Port Group Forwarding table same as Linear FDB but capped
		// at PortGroupFDBCap (for early STL1 HW hardcoded cap of 8kb)
		newPgdbSize = MIN(newLfdbSize,
					  switchInfo->PortGroupFDBCap ? switchInfo->PortGroupFDBCap : DEFAULT_MAX_PGFT_LID+1);
		newPgdb = (STL_PORT_GROUP_FORWARDING_TABLE*) MemoryAllocate2AndClear(
			ROUNDUP(newPgdbSize, NUM_PGFT_ELEMENTS_BLOCK), IBA_MEM_FLAG_PREMPTABLE, MYTAG);

		if (!newPgdb) goto fail;

		memset(newPgdb, 0xff, ROUNDUP(newPgdbSize, NUM_PGFT_ELEMENTS_BLOCK));
		if (nodep->switchp->PortGroupFDB) {
			size_t size = MIN(newPgdbSize, switchInfo->LinearFDBTop + 1);
			memcpy(newPgdb, nodep->switchp->PortGroupFDB, size * sizeof(PORT));
		}
	}

	if (newLfdb) {
		STL_LINEAR_FORWARDING_TABLE * oldLfdb = nodep->switchp->LinearFDB;
		nodep->switchp->LinearFDB = newLfdb;
		nodep->switchp->LinearFDBSize = newLfdbSize;
		switchInfo->LinearFDBTop = newLtop;
		MemoryDeallocate(oldLfdb);
	}

	if (newPgdb) {
		STL_PORT_GROUP_FORWARDING_TABLE * oldPgdb = nodep->switchp->PortGroupFDB;
		nodep->switchp->PortGroupFDB = newPgdb;
		nodep->switchp->PortGroupFDBSize = newPgdbSize;
		MemoryDeallocate(oldPgdb);
	}

	return FSUCCESS;
fail:
	if (newLfdb) {
		MemoryDeallocate(newLfdb);
		newLfdb = NULL;
	}

	if (newPgdb) {
		MemoryDeallocate(newPgdb);
		newPgdb = NULL;
	}

	return errStatus;
}

FSTATUS NodeDataSwitchResizeFDB(NodeData * nodep, uint32 newLfdbSize, uint32 newMfdbSize)
{
	if ((NodeDataSwitchResizeLinearFDB(nodep, newLfdbSize) != FSUCCESS) ||
		(NodeDataSwitchResizeMcastFDB(nodep, newMfdbSize) != FSUCCESS)) {
		fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
		return FINSUFFICIENT_MEMORY;
	}
	return FSUCCESS;
}

// remove Node from lists and free it
// Only removes node from AllNodes and SystemData.Nodes
// Only removes ports from AllLids and NodeData.Ports
// Only removes Iou from NodeData.ioup
// Only removes Iocs from AllIOCs and IouData.Iocs, caller must remove from
// any other lists if called after BuildFabricLists
void NodeDataFree(FabricData_t *fabricp, NodeData *nodep)
{
	if (nodep->context && g_Top_FreeCallbacks.pNodeDataFreeCallback)
		(*g_Top_FreeCallbacks.pNodeDataFreeCallback)(fabricp, nodep);

	cl_qmap_remove_item(&nodep->systemp->Nodes, &nodep->SystemNodesEntry);
	if (cl_qmap_count(&nodep->systemp->Nodes) == 0) {
		if (nodep->systemp->context && g_Top_FreeCallbacks.pSystemDataFreeCallback)
			(*g_Top_FreeCallbacks.pSystemDataFreeCallback)(fabricp, nodep->systemp);

		cl_qmap_remove_item(&fabricp->AllSystems, &nodep->systemp->AllSystemsEntry);
		MemoryDeallocate(nodep->systemp);
	}
	cl_qmap_remove_item(&fabricp->AllNodes, &nodep->AllNodesEntry);
	NodeDataFreePorts(fabricp, nodep);
#if !defined(VXWORKS) || defined(BUILD_DMC)
	if (nodep->ioup)
		IouDataFree(fabricp, nodep->ioup);
#endif
	if (nodep->pSwitchInfo)
		MemoryDeallocate(nodep->pSwitchInfo);
	NodeDataFreeSwitchData(fabricp, nodep);
	MemoryDeallocate(nodep);
}

// remove all Nodes from lists and free them
// Only removes nodes from AllNodes and SystemData.Nodes
// Only removes ports from AllLids and NodeData.Ports
// Only removes Iou from NodeData.ioup
// Only removes Iocs from AllIOCs and IouData.Iocs, caller must remove from
// any other lists if called after BuildFabricLists
void NodeDataFreeAll(FabricData_t *fabricp)
{
	cl_map_item_t *p;

	// this list is sorted/keyed by NodeGUID
	for (p=cl_qmap_head(&fabricp->AllNodes); p != cl_qmap_end(&fabricp->AllNodes); p = cl_qmap_head(&fabricp->AllNodes)) {
		NodeDataFree(fabricp, PARENT_STRUCT(p, NodeData, AllNodesEntry));
	}
}

// remove SM from lists and free it
// Only removes SM from AllSMs, caller must remove from
// any other lists if called after BuildFabricLists
void SMDataFree(FabricData_t *fabricp, SMData *smp)
{
	if (smp->context && g_Top_FreeCallbacks.pSMDataFreeCallback)
		(*g_Top_FreeCallbacks.pSMDataFreeCallback)(fabricp, smp);

	cl_qmap_remove_item(&fabricp->AllSMs, &smp->AllSMsEntry);
	MemoryDeallocate(smp);
}

// remove all SMs from lists and free them
// Only removes SMs from AllSMs, caller must remove from
// any other lists if called after BuildFabricLists
void SMDataFreeAll(FabricData_t *fabricp)
{
	cl_map_item_t *p;

	for (p=cl_qmap_head(&fabricp->AllSMs); p != cl_qmap_end(&fabricp->AllSMs); p = cl_qmap_head(&fabricp->AllSMs)) {
		SMDataFree(fabricp, PARENT_STRUCT(p, SMData, AllSMsEntry));
	}
}

void MCGroupFree(FabricData_t *fabricp, McGroupData *mcgroupp)
{
	LIST_ITEM *p;

	while (!QListIsEmpty(&mcgroupp->AllMcGroupMembers)) {
		p = QListTail(&mcgroupp->AllMcGroupMembers);
		McGroupData *group = (McGroupData *)QListObj(p);
		QListRemoveTail(&mcgroupp->AllMcGroupMembers);
		MemoryDeallocate(group);
	}

	QListRemoveItem(&fabricp->AllMcGroups, &mcgroupp->AllMcGMembersEntry);
	MemoryDeallocate(mcgroupp);
}

void MCDataFreeAll(FabricData_t *fabricp)
{
	LIST_ITEM *p;
	while (!QListIsEmpty(&fabricp->AllMcGroups)) {
		p = QListTail(&fabricp->AllMcGroups);
		MCGroupFree(fabricp, (McGroupData *)QListObj(p));
	}

}

void VFDataFreeAll(FabricData_t *fabricp)
{
	LIST_ITEM *i;
	for (i = QListHead(&fabricp->AllVFs); i;) {
		LIST_ITEM *next = QListNext(&fabricp->AllVFs, i);
		MemoryDeallocate(QListObj(i));
		i = next;
	}
}

void CableDataFree(CableData *cablep)
{
	if (cablep->length)
		MemoryDeallocate(cablep->length);
	if (cablep->label)
		MemoryDeallocate(cablep->label);
	if (cablep->details)
		MemoryDeallocate(cablep->details);
	cablep->length = cablep->label = cablep->details = NULL;
}

void PortSelectorFree(PortSelector *portselp)
{
	if (portselp->NodeDesc)
		MemoryDeallocate(portselp->NodeDesc);
	if (portselp->details)
		MemoryDeallocate(portselp->details);
	MemoryDeallocate(portselp);
}

// remove Link from lists and free it
void ExpectedLinkFree(FabricData_t *fabricp, ExpectedLink *elinkp)
{
	if (elinkp->portp1 && elinkp->portp1->elinkp == elinkp)
		elinkp->portp1->elinkp = NULL;
	if (elinkp->portp2 && elinkp->portp2->elinkp == elinkp)
		elinkp->portp2->elinkp = NULL;
	if (ListItemIsInAList(&elinkp->ExpectedLinksEntry))
		QListRemoveItem(&fabricp->ExpectedLinks, &elinkp->ExpectedLinksEntry);
	if (elinkp->portselp1)
		PortSelectorFree(elinkp->portselp1);
	if (elinkp->portselp2)
		PortSelectorFree(elinkp->portselp2);
	if (elinkp->details)
		MemoryDeallocate(elinkp->details);
	CableDataFree(&elinkp->CableData);
	MemoryDeallocate(elinkp);
}

// remove all Links from lists and free them
void ExpectedLinkFreeAll(FabricData_t *fabricp)
{
	LIST_ITEM *p;

	// free all link data
	for (p=QListHead(&fabricp->ExpectedLinks); p != NULL;) {
		LIST_ITEM *nextp = QListNext(&fabricp->ExpectedLinks, p);
		ExpectedLinkFree(fabricp, (ExpectedLink *)QListObj(p));
		p = nextp;
	}
}

// remove Expected Node from lists and free it
void ExpectedNodeFree(FabricData_t *fabricp, ExpectedNode *enodep, QUICK_LIST *listp)
{
	if (enodep->nodep && enodep->nodep->enodep == enodep)
		enodep->nodep->enodep = NULL;
	if (ListItemIsInAList(&enodep->ExpectedNodesEntry))
		QListRemoveItem(listp, &enodep->ExpectedNodesEntry);
	if (enodep->NodeGUID)
		cl_qmap_remove(&fabricp->ExpectedNodeGuidMap, enodep->NodeGUID);
	if (enodep->NodeDesc)
		MemoryDeallocate(enodep->NodeDesc);
	if (enodep->details)
		MemoryDeallocate(enodep->details);
	if (enodep->ports) {
		int i;
		for (i = 0; i < enodep->portsSize; ++i) {
			if (enodep->ports[i])
				MemoryDeallocate(enodep->ports[i]);
		}
		MemoryDeallocate(enodep->ports);
	}
	MemoryDeallocate(enodep);
}

// remove all Expected Nodes from lists and maps and free them
void ExpectedNodesFreeAll(FabricData_t *fabricp, QUICK_LIST *listp)
{
	LIST_ITEM *p;

	// free all link data
	for (p=QListHead(listp); p != NULL;) {
		LIST_ITEM *nextp = QListNext(listp, p);
		ExpectedNodeFree(fabricp, (ExpectedNode *)QListObj(p), listp);
		p = nextp;
	}
}

// remove Expected SM from lists and free it
void ExpectedSMFree(FabricData_t *fabricp, ExpectedSM *esmp)
{
	if (esmp->smp && esmp->smp->esmp == esmp)
		esmp->smp->esmp = NULL;
	if (ListItemIsInAList(&esmp->ExpectedSMsEntry))
		QListRemoveItem(&fabricp->ExpectedSMs, &esmp->ExpectedSMsEntry);
	if (esmp->NodeDesc)
		MemoryDeallocate(esmp->NodeDesc);
	if (esmp->details)
		MemoryDeallocate(esmp->details);
	MemoryDeallocate(esmp);
}

// remove all Expected SMs from lists and free them
void ExpectedSMsFreeAll(FabricData_t *fabricp)
{
	LIST_ITEM *p;

	// free all link data
	for (p=QListHead(&fabricp->ExpectedSMs); p != NULL;) {
		LIST_ITEM *nextp = QListNext(&fabricp->ExpectedSMs, p);
		ExpectedSMFree(fabricp, (ExpectedSM *)QListObj(p));
		p = nextp;
	}
}

void DestroyFabricData(FabricData_t *fabricp)
{
	if (fabricp->context && g_Top_FreeCallbacks.pFabricDataFreeCallback)
		(*g_Top_FreeCallbacks.pFabricDataFreeCallback)(fabricp);

	ExpectedLinkFreeAll(fabricp);	// ExpectedLinks
	ExpectedNodesFreeAll(fabricp, &fabricp->ExpectedFIs);	// ExpectedFIs
	ExpectedNodesFreeAll(fabricp, &fabricp->ExpectedSWs);	// ExpectedSWs
	ExpectedSMsFreeAll(fabricp);	// ExpectedSMs

	SMDataFreeAll(fabricp); // SMs
	NodeDataFreeAll(fabricp);	// Nodes, Ports, IOUs, Systems
	VFDataFreeAll(fabricp);

	if (fabricp->flags & FF_LIDARRAY)
		FreeLidMap(fabricp);

	// make sure no stale pointers in lists, etc
	// also clear counters and flags
	MemoryClear(fabricp, sizeof(*fabricp));
}

#ifndef __VXWORKS__

static uint8 CLListUint32Find(cl_qmap_t *arrayMap, uint32 entry) 
{ 
   uint8 found = 0; 
   cl_map_item_t *mi; 
   
   mi = cl_qmap_get(arrayMap, entry); 
   if (mi != cl_qmap_end(arrayMap)) {
      found = 1;;
   }
   return found;
}

static uint8 CLListIntFind(cl_qmap_t *arrayMap, int entry) 
{ 
   uint8 found = 0; 
   cl_map_item_t *mi; 
   
   mi = cl_qmap_get(arrayMap, entry); 
   if (mi != cl_qmap_end(arrayMap)) {
      found = 1;;
   }
   return found;
}

static FSTATUS CLListUint32Add(cl_qmap_t *arrayMap, uint32 entry) 
{ 
   FSTATUS status = FSUCCESS; 
   cl_map_item_t *mi; 
   clListSearchData_t *clListp; 
   
   if (CLListUint32Find(arrayMap, entry)) 
      return status; 
   
   clListp = (clListSearchData_t *)MemoryAllocate2AndClear(sizeof(clListSearchData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG);        
   if (!clListp) {
      status = FINSUFFICIENT_MEMORY; 
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
   } else {
      mi = cl_qmap_insert(arrayMap, entry, &clListp->AllListEntry); 
      if (mi != &clListp->AllListEntry) {
         fprintf(stderr, "%s: Duplicate entry found in numeric list map: %d\n", g_Top_cmdname, entry); 
         MemoryDeallocate(clListp); 
         clListp = PARENT_STRUCT(mi, clListSearchData_t, AllListEntry);
      }
   }
   
   return status;
}

static FSTATUS CLListIntAdd(cl_qmap_t *arrayMap, int entry) 
{ 
   FSTATUS status = FSUCCESS; 
   cl_map_item_t *mi; 
   clListSearchData_t *clListp; 
   
   if (CLListIntFind(arrayMap, entry)) 
      return status; 
   
   clListp = (clListSearchData_t *)MemoryAllocate2AndClear(sizeof(clListSearchData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG);        
   if (!clListp) {
      status = FINSUFFICIENT_MEMORY; 
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
   } else {
      mi = cl_qmap_insert(arrayMap, entry, &clListp->AllListEntry); 
      if (mi != &clListp->AllListEntry) {
         fprintf(stderr, "%s: Duplicate entry found in numeric list map: %d\n", g_Top_cmdname, entry); 
         MemoryDeallocate(clListp); 
         clListp = PARENT_STRUCT(mi, clListSearchData_t, AllListEntry);
      }
   }
   
   return status;
}

//PYTHON: def ib_connection_source_to_str (conn) :
//NOTA BENE: NOTE THAT THIS FUNCTION IS NOT THREAD SAFE!
static char* ib_connection_source_to_str(FabricData_t *fabricp, clConnData_t *connp) 
{ 
   cl_map_item_t *mi; 
   clDeviceData_t * device1p,*device2p; 
   static char str[2*NODE_DESCRIPTION_ARRAY_SIZE+11]; 
   
   /*
   return '%s port %d' % \
          (fabric.map_guid_to_ib_device[conn.guid1].name, conn.port1)
   */
   mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, connp->FromDeviceGUID); 
   if (mi == cl_qmap_end(&fabricp->map_guid_to_ib_device)) 
      return NULL; 
   device1p = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
   
   mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, connp->ToDeviceGUID); 
   if (mi == cl_qmap_end(&fabricp->map_guid_to_ib_device)) 
      return NULL; 
   device2p = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
   
   snprintf(str, 2*NODE_DESCRIPTION_ARRAY_SIZE+10, "%s:%d to %s:%d VL:%d",
           device1p->nodep->NodeDesc.NodeString, connp->FromPortNum, 
           device2p->nodep->NodeDesc.NodeString, connp->ToPortNum,
           connp->VL.VL); 
   return str;
}

static void CLThreadSleep(uint64_t sleep_time) 
{ 
   struct timespec   ts; 
   struct timespec   ts_actual; 
   long              micro_seconds; 
   
   ts.tv_sec       = (time_t)(sleep_time / 1000000ULL); 
   micro_seconds   = (long)(sleep_time % 1000000ULL); 
   ts.tv_nsec      = micro_seconds * 1000; 
   (void)nanosleep(&ts, &ts_actual); 
   
   return;
}

static clArcData_t* CLGraphFindIdArc(clGraphData_t *graphp, uint32 arcId) 
{ 
   cl_map_item_t *mi; 
   
   mi = cl_qmap_get(&graphp->Arcs, arcId); 
   if (mi != cl_qmap_end(&graphp->Arcs)) {
      clArcData_t *arcp = PARENT_STRUCT(mi, clArcData_t, AllArcIdsEntry); 
      return arcp;
   }
   
   return NULL;
}

void CLGraphDataFree(clGraphData_t *graphp, void *context) 
{ 
   #define DEF_MAX_FREE_ENTRY   5000
   int i; 
   cl_map_item_t *mi;
   uint32_t arcListCount, arcCount = 0; 
   ValidateCreditLoopRoutesContext_t *cp = (ValidateCreditLoopRoutesContext_t *)context; 
   
   
   if (!graphp) 
      return;
    
   // free all arc entries
   arcListCount = cl_qmap_count(&graphp->Arcs);
   for (mi = cl_qmap_head(&graphp->Arcs); 
        mi != cl_qmap_end(&graphp->Arcs); 
        mi = cl_qmap_head(&graphp->Arcs), arcCount++) {
      clArcData_t *arcp; 
      
      if (NULL != (arcp = PARENT_STRUCT(mi, clArcData_t, AllArcIdsEntry))) {
         cl_qmap_remove_item(&graphp->Arcs, &arcp->AllArcIdsEntry); 
         MemoryDeallocate(arcp);
      }
      if (arcListCount >= DEF_MAX_FREE_ENTRY) {
         if (arcCount % PROGRESS_FREQ == 0 || arcCount == 0) {
            if (!cp->quiet) 
               ProgressPrint(FALSE, "Deallocated %6d of %6d Arcs...", arcCount, arcListCount);
         }
      }
   }

   // clear line used to display progress report
   if (!cp->quiet && arcListCount >= DEF_MAX_FREE_ENTRY)
      ProgressPrint(TRUE, "Done Deallocating All Arcs");
   
   // free all vertices associated with the graph
   if (graphp->Vertices) {
      for (i = 0; i < graphp->NumVertices; i++) {
         if (graphp->Vertices[i]) {
            if (graphp->Vertices[i]->Inbound)
               MemoryDeallocate(graphp->Vertices[i]->Inbound);
            if (graphp->Vertices[i]->Outbound)
               MemoryDeallocate(graphp->Vertices[i]->Outbound);
            MemoryDeallocate(graphp->Vertices[i]);
         }
         if (graphp->NumVertices >= DEF_MAX_FREE_ENTRY) {
            if (i % PROGRESS_FREQ == 0 || i == 0) {
               if (!cp->quiet) 
                  ProgressPrint(FALSE, "Deallocated %6d of %6d Vertices...", i, graphp->NumVertices);
            }
         }
      }

      // clear line used to display progress report
      if (!cp->quiet && graphp->NumVertices >= DEF_MAX_FREE_ENTRY)
         ProgressPrint(TRUE, "Done Deallocating All Vertices");
      MemoryDeallocate(graphp->Vertices);
   }
}

static void CLDeviceDataFreeAll(FabricData_t *fabricp, void *context) 
{
   #define DEF_MAX_FREE_DEV   5000
   int i, j;
   cl_map_item_t * mi,*ri; 
   uint32 deviceCount = 0, deviceListCount, routeCount = 0;
// uint32 routeListCount;
   ValidateCreditLoopRoutesContext_t *cp = (ValidateCreditLoopRoutesContext_t *)context; 
   
   deviceListCount = cl_qmap_count(&fabricp->map_guid_to_ib_device);
   // free all device entries
   for (mi = cl_qmap_head(&fabricp->map_guid_to_ib_device); 
        mi != cl_qmap_end(&fabricp->map_guid_to_ib_device); 
        mi = cl_qmap_head(&fabricp->map_guid_to_ib_device), deviceCount++) {
      clDeviceData_t *ccDevicep; 
      
      if (NULL == (ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry)))
         break;
      else {
         cl_qmap_remove_item(&fabricp->map_guid_to_ib_device, &ccDevicep->AllDevicesEntry); 
         // free all connections associated with the device
         for (i = 0; i < CREDIT_LOOP_DEVICE_MAX_CONNECTIONS; i++) {
            for (j = 0; j < STL_MAX_VLS; j++) {
               if (ccDevicep->Connections[i][j])
                  MemoryDeallocate(ccDevicep->Connections[i][j]);
            }
         }
         
//       routeListCount = cl_qmap_count(&ccDevicep->map_dlid_to_route);
         // free all routes associated with the device
         for (ri = cl_qmap_head(&ccDevicep->map_dlid_to_route), routeCount = 0; 
              ri != cl_qmap_end(&ccDevicep->map_dlid_to_route); 
              ri = cl_qmap_head(&ccDevicep->map_dlid_to_route), routeCount++) {
            clRouteData_t *ccRoutep; 
            
            if (NULL == (ccRoutep = PARENT_STRUCT(ri, clRouteData_t, AllRoutesEntry)))
               break;
            else {
               cl_qmap_remove_item(&ccDevicep->map_dlid_to_route, &ccRoutep->AllRoutesEntry); 
               MemoryDeallocate(ccRoutep);
            }

            /* 
              if (routeCount % PROGRESS_FREQ == 0 || routeCount == 0) {
               if (!cp->quiet) 
                  ProgressPrint(FALSE, "Deallocated %6d of %6d Routes...", routeCount, routeListCount);
            } 
            */ 
         }
         
         if (deviceListCount >= DEF_MAX_FREE_DEV) {
            if (deviceCount % PROGRESS_FREQ == 0 || deviceCount == 0) {
               if (!cp->quiet) 
                  ProgressPrint(FALSE, "Deallocated %6d of %6d Devices...", deviceCount, deviceListCount);
            }
         }

         // free device
         MemoryDeallocate(ccDevicep);
      }
   }

   // clear line used to display progress report
   if (!cp->quiet && deviceListCount >= DEF_MAX_FREE_DEV)
      ProgressPrint(TRUE, "Done Deallocating All Routes");
}

static FSTATUS CLGetDynamicArray(void **array, uint32 *arrayEntries, uint32 entryLength, uint32 newEntryIndice, int verbose) 
{ 
   #define ARRAY_INCR_NUM_ENTRIES  1000
   
   FSTATUS status = FERROR; 
   
   uint32 newEntries; 
   void *newBufferp; 
   
   
   if (array && arrayEntries) {
      status = FSUCCESS; 
      if (!*array || newEntryIndice >= *arrayEntries) {
         if (verbose >= 6) 
            printf("Old size: array=%p, *array=%p, arrayEntries=%d, entryLength=%d , newEntryIndice=%d\n", 
                   array, *array, *arrayEntries, entryLength, newEntryIndice); 
         
         // array has not been initialized, set entries to default values
         if (!*array) {
            //printf("\tALLOC:\n");
            *arrayEntries = 0;
         }
         
         // extend the buffer by an addition fixed number of entries  
         newEntries = *arrayEntries + ARRAY_INCR_NUM_ENTRIES; 
         newBufferp = MemoryAllocate2AndClear(newEntries * entryLength, IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
         if (!newBufferp) {
            status = FINSUFFICIENT_MEMORY; 
            fprintf(stderr, "%s: Unable to resize the buffer\n", g_Top_cmdname);
         } else {
            // replicate the old array into the new array
            if (*array) {
               //printf("\tRESIZE:\n");
               memcpy(newBufferp, *array, (*arrayEntries * entryLength)); 
               MemoryDeallocate(*array);
            }
            // point to new array
            *array = newBufferp; 
            *arrayEntries = newEntries; 
            //status = FSUCCESS;
         }
         if (verbose >= 6) 
            printf("New size: array=%p, arrayEntries=%d, array size=%d\n", *array, *arrayEntries, (*arrayEntries * entryLength));
      }
   }
   
   return status;
}

static clDeviceData_t* CLFindGuidDevice(FabricData_t *fabricp, uint64 guid) 
{ 
   cl_map_item_t *mi; 
   
   mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, guid); 
   if (mi != cl_qmap_end(&fabricp->map_guid_to_ib_device)) {
      clDeviceData_t *ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
      return ccDevicep;
   }
   return NULL;
}

//PYTHON: def lookup_vertex (self, ref) :
static clVertixData_t* CLGraphFindConnVertex(clGraphData_t *graphp, clConnData_t *ccConnp) 
{ 
   cl_map_item_t *mi; 
   clVertixData_t *ccVertexp = NULL; 
   
   if (graphp->Vertices) {
      mi = cl_qmap_get(&graphp->map_conn_to_vertex_conn, (uint64)ccConnp); 
      if (mi != cl_qmap_end(&graphp->map_conn_to_vertex_conn)) {
         ccVertexp = PARENT_STRUCT(mi, clVertixData_t, AllVerticesEntry);
      }
   }
   return ccVertexp;
}

//PYTHON: def add_vertex (self, ref) :
static int CLGraphDataAddVertex(clGraphData_t *graphp, clConnData_t *ccConnp, int verbose) 
{ 
   cl_map_item_t *mi; 
   
   //PYTHON: vertex = self.map_ref_to_vertex.get(ref)
   /* CJKING: Addtional thought required on how to map a connection
    * to a Vertex.  Currently, doing linear search via CLGraphFindConnVertex()
    */
   clVertixData_t *ccVertexp = CLGraphFindConnVertex(graphp, ccConnp); 
   
   //PYTHON: if vertex :
   if (ccVertexp) {
      if (verbose >= 5) 
         fprintf(stderr, "Warning, Vector Id %d already exist\n", ccVertexp->Id); 
      return ccVertexp->Id;
   } else {
      ccVertexp = (clVertixData_t *)MemoryAllocate2AndClear(sizeof(clVertixData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG);        
      if (!ccVertexp) {
         fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
      } else {
         //PYTHON: id = len(self.vertices)
         //PYTHON: vertex = Vertex(id, ref)
         ccVertexp->Id = graphp->NumVertices; 
         ccVertexp->Connection = ccConnp; 
         if (!graphp->Vertices) 
            cl_qmap_init(&graphp->map_conn_to_vertex_conn, NULL); 
         if (!CLGetDynamicArray((void **)&graphp->Vertices, &graphp->VerticesLength, 
                                sizeof(graphp->Vertices[0]), ccVertexp->Id, verbose)) {
            //PYTHON: self.vertices.append(vertex)
            graphp->Vertices[ccVertexp->Id] = ccVertexp; 
            //PYTHON: self.map_ref_to_vertex[ref] = vertex
            mi = cl_qmap_insert(&graphp->map_conn_to_vertex_conn, (uint64)ccConnp, &ccVertexp->AllVerticesEntry); 
            if (mi != &ccVertexp->AllVerticesEntry) {
               //if (verbose >= 4) {
               fprintf(stderr, "%s: Duplicate connection found in vertix map: %p\n", g_Top_cmdname, ccVertexp->Connection); 
               //}
               MemoryDeallocate(ccVertexp); 
               ccVertexp = PARENT_STRUCT(mi, clVertixData_t, AllVerticesEntry);
            }
            //PYTHON: self.num_vertices += 1
            graphp->NumVertices++; 
            graphp->NumActiveVertices++; 
            if (verbose >= 4) 
               printf("Adding vector Id %d\n", ccVertexp->Id); 
            return ccVertexp->Id;
         } else {
            MemoryDeallocate(ccVertexp);
         }
      }
   }
   
   return -1;
}

//PYTHON: def get_arc_key (source, sink) :
static clArcData_t* CLGraphFindSrcSinkArc(clGraphData_t *graphp, uint32 source, uint32 sink) 
{ 
   cl_map_item_t *mi; 
   clArcData_t *arcp = NULL; 
   
   if (cl_qmap_count(&graphp->map_arc_key_to_arc)) {
      clArcData_t arc; 
      
      arc.u1.s.Source = source; 
      arc.u1.s.Sink = sink; 
      mi = cl_qmap_get(&graphp->map_arc_key_to_arc, arc.u1.AsReg64); 
      if (mi != cl_qmap_end(&graphp->map_arc_key_to_arc)) {
         arcp = PARENT_STRUCT(mi, clArcData_t, AllArcsEntry);
      }
   }
   return arcp;
}

//PYTHON: def del_arc (self, id) :
static void CLGraphDataDelArc(clGraphData_t *graphp, int arcId) 
{ 
   uint32 i; 
   //LIST_ITEM *p;
   cl_map_item_t *mi; 
   clArcData_t *ccArcp = NULL; 
   
   
   if (arcId < 0) 
      return; 
   
   
   //PYTHON: arc = self.arcs[id]
   mi = cl_qmap_get(&graphp->Arcs, arcId); 
   if (mi != cl_qmap_end(&graphp->Arcs)) {
      ccArcp = PARENT_STRUCT(mi, clArcData_t, AllArcIdsEntry); 
      if (ccArcp) {
         clArcData_t *ssArcp; 
         
         // remove arc entry from arc IDs map
         cl_qmap_remove_item(&graphp->Arcs, &ccArcp->AllArcIdsEntry); 
         // remove arc entry from arc Source/Sink map
         if ((ssArcp = CLGraphFindSrcSinkArc(graphp, ccArcp->Source, ccArcp->Sink))) 
            cl_qmap_remove_item(&graphp->map_arc_key_to_arc, &ssArcp->AllArcsEntry);
      }
   }
   
   if (!ccArcp) {
      fprintf(stderr, "Warning, failed to find arc with id %d\n", arcId); 
      exit(0);
   } else {
      /*
       * process up-link references
       */
      //PYTHON: self.vertices[arc.source].outbound.remove(id)
      for (i = 0; i < graphp->Vertices[ccArcp->Source]->OutboundCount; i++) {
         if (arcId == graphp->Vertices[ccArcp->Source]->Outbound[i]) {
            graphp->Vertices[ccArcp->Source]->Outbound[i] = -1; 
            //PYTHON: self.vertices[arc.source].refcount -= 1
            graphp->Vertices[ccArcp->Source]->RefCount--; 
            graphp->Vertices[ccArcp->Source]->OutboundInuseCount--;
         }
      }
      //PYTHON: if self.vertices[arc.source].refcount == 0 :        
      if (graphp->Vertices[ccArcp->Source]->RefCount == 0) 
         graphp->NumActiveVertices--;       //PYTHON:   self.num_vertices -= 1
      
      /*
       * remove down-link references
       */ 
      //PYTHON: self.vertices[arc.sink].inbound.remove(id)
      for (i = 0; i < graphp->Vertices[ccArcp->Sink]->InboundCount; i++) {
         if (arcId == graphp->Vertices[ccArcp->Sink]->Inbound[i]) {
            graphp->Vertices[ccArcp->Sink]->Inbound[i] = -1; 
            //PYTHON: self.vertices[arc.sink].refcount -= 1
            graphp->Vertices[ccArcp->Sink]->RefCount--; 
            graphp->Vertices[ccArcp->Sink]->InboundInuseCount--;
         }
      }
      //PYTHON: if self.vertices[arc.sink].refcount == 0 :
      if (graphp->Vertices[ccArcp->Sink]->RefCount == 0) 
         graphp->NumActiveVertices--;   //PYTHON: self.num_vertices -= 1
      
      //PYTHON: self.num_arcs -= 1
      graphp->NumArcs--; 
      MemoryDeallocate(ccArcp);
   }
}

//PYTHON: def add_arc (self, source, sink) :
static int CLGraphDataAddArc(clGraphData_t *graphp, uint32 source, uint32 sink, int verbose) 
{ 
   int arcId = -1; 
   cl_map_item_t *mi; 
   
   
   //PYTHON: if source == sink :
   if (source == sink) 
      fprintf(stderr, "Warning, Ignoring arc with same source and sink of %d\n", source); 
   else if (source >= graphp->NumVertices)   //PYTHON: if source < 0 or source >= len(self.vertices) :
      fprintf(stderr, "Warning, Ignoring out of range source vertex %d\n", source); 
   else if (sink >= graphp->NumVertices)       //PYTHON: if sink < 0 or sink >= len(self.vertices) :
      fprintf(stderr, "Warning, Ignoring out of range sink vertex %d\n", sink); 
   else {
      //PYTHON: arc_key = get_arc_key(source, sink)
      clArcData_t *ccArcp = CLGraphFindSrcSinkArc(graphp, source, sink); 
      
      if (ccArcp) {
         //PYTHON: id = self.map_arc_key_to_arc[arc_key]
         arcId = ccArcp->Id; 
         if (verbose >= 6) 
            fprintf(stderr, "Warning, arc %d from %d to %d already exists\n", arcId, source, sink);
      } else {
         ccArcp = (clArcData_t *)MemoryAllocate2AndClear(sizeof(clArcData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG);        
         if (!ccArcp) {
            fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
         } else {
            FSTATUS status; 
            
            //PYTHON: id = len(self.arcs)
            ccArcp->Id = cl_qmap_count(&graphp->Arcs); 
            //PYTHON: self.arcs.append(Arc(self, source, sink))
            ccArcp->Source = source; 
            ccArcp->Sink = sink; 
            mi = cl_qmap_insert(&graphp->Arcs, ccArcp->Id, &ccArcp->AllArcIdsEntry); 
            if (mi != &ccArcp->AllArcIdsEntry) {
               //if (verbose >= 4) {
               fprintf(stderr, "%s: Duplicate connection found in arc ID map: %d\n", g_Top_cmdname, ccArcp->Id); 
               //}
               //MemoryDeallocate(ccArcp);
               //ccArcp = PARENT_STRUCT(mi, clArcData_t, AllArcsEntry);
            }
            //PYTHON: self.map_arc_key_to_arc[arc_key] = id
            ccArcp->u1.s.Source = source; 
            ccArcp->u1.s.Sink = sink; 
            mi = cl_qmap_insert(&graphp->map_arc_key_to_arc, ccArcp->u1.AsReg64, &ccArcp->AllArcsEntry); 
            if (mi != &ccArcp->AllArcsEntry) {
               //if (verbose >= 4) {
               fprintf(stderr, "%s: Duplicate connection found in arc source-sink map: 0x%016"PRIx64"\n", g_Top_cmdname, ccArcp->u1.AsReg64); 
               //}
               MemoryDeallocate(ccArcp); 
               ccArcp = PARENT_STRUCT(mi, clArcData_t, AllArcsEntry);
            }
            
            if (!(status = CLGetDynamicArray((void **)&graphp->Vertices[source]->Outbound, 
                                             &graphp->Vertices[source]->OutboundLength, 
                                             sizeof(int), graphp->Vertices[source]->OutboundCount, verbose))) {
               //PYTHON: self.vertices[source].outbound.append(id)
               graphp->Vertices[source]->Outbound[graphp->Vertices[source]->OutboundCount] = ccArcp->Id; 
               graphp->Vertices[source]->OutboundCount++; 
               graphp->Vertices[source]->OutboundInuseCount++; 
               //PYTHON: self.vertices[source].refcount += 1
               graphp->Vertices[source]->RefCount++;
            }
            
            if (!status) {
               if (!(status = CLGetDynamicArray((void **)&graphp->Vertices[sink]->Inbound, 
                                                &graphp->Vertices[sink]->InboundLength, 
                                                sizeof(int), graphp->Vertices[sink]->InboundCount, verbose))) {
                  //PYTHON: self.vertices[sink].inbound.append(id)
                  graphp->Vertices[sink]->Inbound[graphp->Vertices[sink]->InboundCount] = ccArcp->Id; 
                  graphp->Vertices[sink]->InboundCount++; 
                  graphp->Vertices[sink]->InboundInuseCount++; 
                  //PYTHON: self.vertices[sink].refcount += 1
                  graphp->Vertices[sink]->RefCount++;
               }
            }
            
            if (status) 
               (void)CLGraphDataDelArc(graphp, ccArcp->Id); 
            else {
               arcId = ccArcp->Id; 
               //PYTHON: self.num_arcs += 1
               graphp->NumArcs++; 
               if (verbose >= 4) 
                  printf("Add arc from %d to %d\n", source, sink);
            }
         }
      }
   }
   
   return arcId;
}

//PYTHON: def build_routing_graph (fabric) :
static void* CLFabricDataBuildRouteGraphThread(void *context) 
{ 
   FabricData_t *fabricp = ((clThreadContext_t *)context)->fabricp; 
   int verbose = ((clThreadContext_t *)context)->verbose; 
   FSTATUS status = FSUCCESS; 
   uint32 hops = 0, l = 0; 
   LIST_ITEM *dstHcaLstp;
   LIST_ITEM *srcHcaLstp; 
   uint32 thrdId = ((clThreadContext_t *)context)->threadId; 
   ValidateCLTimeGetCallback_t timeGetCallback = ((clThreadContext_t *)context)->timeGetCallback;
   uint32 usedSLs = ((clThreadContext_t*)context)->usedSLs;
   //PYTHON: for src_hfi in fabric.hfis :
   for (srcHcaLstp = ((clThreadContext_t *)context)->srcHcaList;
         l < ((clThreadContext_t *)context)->srcHcaListLength;
        srcHcaLstp = QListNext(&fabricp->FIs, srcHcaLstp), l++) {
      clDeviceData_t *src_hfip = (clDeviceData_t *)QListObj(srcHcaLstp); 
      
      if (verbose >= 4) {
         timeGetCallback(&((clThreadContext_t *)context)->sTime, &g_cl_lock); 
         printf("[%d] START build all routes from %s (SLID 0x%08x)\n", 
                (int)thrdId, src_hfip->nodep->NodeDesc.NodeString, src_hfip->Lid);
      }
      
      //PYTHON: for dst_hfi in fabric.hfis :
      for (dstHcaLstp = QListHead(&fabricp->FIs); dstHcaLstp != NULL; dstHcaLstp = QListNext(&fabricp->FIs, dstHcaLstp)) {
         clDeviceData_t *dst_hfip = (clDeviceData_t *)QListObj(dstHcaLstp); 
         
         if (srcHcaLstp != dstHcaLstp) {
            uint8 sl;
            for (sl = 0; sl < STL_MAX_SLS; sl++) { // loop over SLs
               if (usedSLs != 0) {
                  if (!((usedSLs >> sl) & 1)) {
                     continue;
                  }
               } else {
                  if (sl != 0) continue;
               }
               uint32 links;
               uint16 slid, dlid;
               int this_vertex_id;
               uint32 previous_vertex_id = 0;              //PYTHON: previous_vertex_id = None
               clDeviceData_t *hfip = src_hfip;            //PYTHON: hfi = src_hfip;
               clConnData_t *this_connection = NULL;
               clConnData_t *previous_connection = NULL;  //PYTHON: previous_connection = None
               uint8 sc = 0, previous_sc = 0;
            
               //PYTHON: slid = src_hfi.lid
               //PYTHON: dlid = dst_hfi.lid
               //PYTHON: links = 0
               slid = src_hfip->Lid;
               dlid = dst_hfip->Lid;
               links = 0; 
               if (verbose >= 4)
                  printf("[%d]Build route from %s to %s (SLID 0x%04x to DLID 0x%04x):\n",
                         (int)thrdId, src_hfip->nodep->NodeDesc.NodeString, dst_hfip->nodep->NodeDesc.NodeString, slid, dlid); 

               // get global lock
               pthread_mutex_lock(&g_cl_lock); 

               //PYTHON: while hfi != dst_hfi :
               while (hfip != dst_hfip) {
                  cl_map_item_t *mi; 
                  PortData *portp = NULL; 
                  clRouteData_t *ccRoutep = NULL; 
                  //PYTHON: if dlid in hfi.routes :
                  mi = cl_qmap_get(&hfip->map_dlid_to_route, dlid);
                  if (mi != cl_qmap_end(&hfip->map_dlid_to_route))
                     ccRoutep = PARENT_STRUCT(mi, clRouteData_t, AllRoutesEntry);

                  if (ccRoutep) {
                     //PYTHON: port = hfi.routes[dlid]
                     portp = ccRoutep->portp;
                     if (verbose >= 4)
                        printf("  [%d] GUID 0x%016"PRIx64": use port %3.3u\n", (int)thrdId, hfip->nodep->NodeInfo.NodeGUID, portp->PortNum);
                  } else {
                     if (verbose >= 4)
                        printf("  [%d] GUID 0x%016"PRIx64": no route to DLID 0x%04x\n", (int)thrdId, hfip->nodep->NodeInfo.NodeGUID, dlid); 
                     break;
                  }
                  // look up the sc
                  if (usedSLs) {
                     // if this is the 1st hop, use the SL2SC table
                     if (hfip == src_hfip) {
                        if (portp->pQOS && portp->pQOS->SL2SCMap) {
                           sc = portp->pQOS->SL2SCMap->SLSCMap[sl].SC;
                        }
                     } else if (previous_connection) {
                     // SC to SC' transition
                        if (!hfip->nodep) {
                           fprintf(stderr, "[%d] Error, no node data found for GUID 0x%016"PRIx64"\n", (int)thrdId, previous_connection->ToDeviceGUID);
                           status = FERROR;
                           break;
                        }
                        if (hfip->nodep->NodeInfo.NodeType == STL_NODE_SW) { // should always be a switch
                           mi = cl_qmap_get(&hfip->nodep->Ports, previous_connection->ToPortNum);
                           if (mi == cl_qmap_end(&hfip->nodep->Ports)) {
                              fprintf(stderr, "[%d] Error, unable to find port %d for GUID 0x%016"PRIx64"\n", (int)thrdId, previous_connection->ToPortNum, previous_connection->ToDeviceGUID);
                              status = FERROR;
                              break;
                           }
                           PortData *prev_port = PARENT_STRUCT(mi, PortData, NodePortsEntry);
                           if (prev_port && prev_port->pQOS && !(QListIsEmpty(&prev_port->pQOS->SC2SCMapList[0]))){
                             STL_SCSCMAP *pSCSC = QOSDataLookupSCSCMap(prev_port, portp->PortNum, 0);
                             if (pSCSC) {
                                 sc = pSCSC->SCSCMap[previous_sc].SC;
                              }
                           }
                        }
                     }
                  }
                  //PYTHON: this_connection = hfi.connections[port]
                  this_connection = hfip->Connections[portp->PortNum][sc];
                  if (!this_connection){ // no connection found for this port/vl, broken route
                     fprintf(stderr, "[%d] Warning, broken route for GUID 0x%016"PRIx64" port %3.3u\n", (int)thrdId, hfip->nodep->NodeInfo.NodeGUID, portp->PortNum);
                     break;
                  }
 
                  //PYTHON: graph.add_vertex(this_connection)
                  if (-1 == (this_vertex_id = CLGraphDataAddVertex(&fabricp->Graph, this_connection, verbose))) {
                     break;
                  }
                  //PYTHON: if previous_connection :
                  if (previous_connection) {
                     //PYTHON: graph.add_arc(previous_vertex_id, this_vertex_id)
                     if (-1 == CLGraphDataAddArc(&fabricp->Graph, previous_vertex_id, this_vertex_id, verbose)) {
                        status = FERROR; 
                        break;
                     }
                  }

                  //PYTHON: guid = this_connection.guid2
                  //PYTHON: hfi = fabric.map_guid_to_ib_device[guid]
                  if (!(hfip = CLFindGuidDevice(fabricp, this_connection->ToDeviceGUID))) {
                     fprintf(stderr, "[%d] Error, no device found for GUID 0x%016"PRIx64" to DLID 0x%04x\n", (int)thrdId, this_connection->ToDeviceGUID, dlid); 
                     status = FERROR;
                     break;
                  }

                  //PYTHON: links += 1
                  //PYTHON: previous_connection = this_connection
                  //PYTHON: previous_vertex_id = this_vertex_id
                  links++;
                  previous_connection = this_connection;
                  previous_vertex_id = this_vertex_id;
                  previous_sc = sc;
               } // end while loop

               // release global lock
               pthread_mutex_unlock(&g_cl_lock);

               //PYTHON: if hfi == dst_hfi :
               if (hfip == dst_hfip) {
                  //PYTHON: present_routes += 1
                  //PYTHON: hops = links - 1
                  present_routes++;
                  hops = links - 1;

                  if (verbose >= 4)
                     printf("  [%d] %d links traversed, %d hops\n", (int)thrdId, links, hops);

                  // get global lock
                  pthread_mutex_lock(&g_cl_lock);

                  if (!CLGetDynamicArray((void **)&hops_histogram, &hops_histogram_length, sizeof(uint32), hops, verbose)) {
                     //PYTHON: if hops in hops_histogram
                     //CJKING: if (CLListUint32Find(hops, hops_histogram, hops_histogram_entries, verbose))
                     if (CLListUint32Find(&hopsHistogramLstMap, hops))
                        hops_histogram[hops]++;    //PYTHON: hops_histogram[hops] += 1
                     else {
                        //CJKING: Add entry to search list for hops_histogram array
                        if (!CLListUint32Add(&hopsHistogramLstMap, hops))
                           hops_histogram[hops] = 1;  //PYTHON: histogram[hops] = 1
                        else {
                           status = FERROR;
                           pthread_mutex_unlock(&g_cl_lock);
                           break;
                        }
                     }
                     hops_histogram_entries = MAX(hops_histogram_entries, hops);
                  }

                  // release global lock
                  pthread_mutex_unlock(&g_cl_lock);
                  } else {
                  missing_routes += 1;    //PYTHON: missing_routes += 1
               }
            }
         }
      }
      
      if (verbose >= 4) {
         timeGetCallback(&((clThreadContext_t *)context)->eTime, &g_cl_lock); 
         printf("[%d] END build all routes from %s (SLID 0x%08x); elapsed time(usec)=%d, (sec)=%d\n", 
                (int)thrdId, src_hfip->nodep->NodeDesc.NodeString, src_hfip->Lid, 
                (int)(((clThreadContext_t *)context)->eTime - ((clThreadContext_t *)context)->sTime), 
                ((int)(((clThreadContext_t *)context)->eTime - ((clThreadContext_t *)context)->sTime)) / CL_TIME_DIVISOR); 
         
         printf("[%d] %d of %d nodes completed\n", 
                 (int)thrdId, l+1, ((clThreadContext_t *)context)->srcHcaListLength);
      } else {
	 if (l%PROGRESS_FREQ == 0 || l == 0) {
            // get global lock
            pthread_mutex_lock(&g_cl_lock); 
            if (!((clThreadContext_t *)context)->quiet)
               ProgressPrint(FALSE, "[%d]Processed %6d of %6d Routes...",
                               (int)thrdId, l+1, ((clThreadContext_t *)context)->srcHcaListLength);
            // release global lock
            pthread_mutex_unlock(&g_cl_lock); 
         }
      }
      
      (void)CLThreadSleep(VTIMER_1_MILLISEC);
   }
   
   ((clThreadContext_t *)context)->threadExit = 1; 
   ((clThreadContext_t *)context)->threadStatus = status; 
   
   return NULL;
}

//PYTHON: def split_out_vertex (self, new_graph, vertex) :
static FSTATUS CLGraphDataSplitOutVertex(clGraphData_t *graphp, clGraphData_t *newGraphp, clVertixData_t *vertexp, int verbose) 
{ 
   FSTATUS status = FSUCCESS; 
   int ii, nn, new_vertex_id, new_sink_id, new_source_id; 
   uint32 neighborsLength = 0, neighborsCount = 0; 
   clVertixData_t * sink,*source; 
   clArcData_t *arcp; 
   clVertixData_t **neighbors = NULL; 
   
   
   //PYTHON: if vertex.refcount :
   if (vertexp->RefCount) {
      //PYTHON: new_vertex_id = new_graph.add_vertex(vertex.ref)
      new_vertex_id = CLGraphDataAddVertex(newGraphp, vertexp->Connection, verbose); 
      
      //PYTHON: neighbors = []
      /* initialization of neighbors array handled via CLGetDynamicArray() */
      
      //PYTHON: for arc_id in vertex.outbound :
      for (ii = 0; ii < vertexp->OutboundCount; ii++) {
         if (vertexp->Outbound[ii] >= 0 && (arcp = CLGraphFindIdArc(graphp, vertexp->Outbound[ii]))) {
            //PYTHON: sink = self.vertices[self.arcs[arc_id].sink]
            sink = graphp->Vertices[arcp->Sink]; 
            
            if (CLGetDynamicArray((void **)&neighbors, &neighborsLength, sizeof(neighbors), neighborsCount, verbose)) {
               status = FERROR; 
               break;
            }
            
            //PYTHON: neighbors.append(sink)
            neighbors[neighborsCount++] = sink; 
            
            //PYTHON: new_sink_id = new_graph.add_vertex(sink.ref)
            if (-1 == (new_sink_id = CLGraphDataAddVertex(newGraphp, sink->Connection, verbose))) {
               status = FERROR; 
               break;
            }
            //PYTHON: new_graph.add_arc(new_vertex_id, new_sink_id)
            if (-1 == CLGraphDataAddArc(newGraphp, new_vertex_id, new_sink_id, verbose)) {
               status = FERROR; 
               break;
            }
         }
      }
      
      //PYTHON: for arc_id in vertex.outbound :
      for (ii = 0; ii < vertexp->OutboundCount; ii++) {
         //PYTHON: self.del_arc(arc_id)
         if (vertexp->Outbound[ii] >= 0) 
            (void)CLGraphDataDelArc(graphp, vertexp->Outbound[ii]);
      }
      
      //PYTHON: for arc_id in vertex.inbound :
      for (ii = 0; ii < vertexp->InboundCount; ii++) {
         if (vertexp->Inbound[ii] >= 0 && (arcp = CLGraphFindIdArc(graphp, vertexp->Inbound[ii]))) {
            //PYTHON: source = self.vertices[self.arcs[arc_id].source]
            source = graphp->Vertices[arcp->Source]; 
            if (CLGetDynamicArray((void **)&neighbors, &neighborsLength, sizeof(neighbors), neighborsCount, verbose)) {
               status = FERROR; 
               break;
            }
            //PYTHON: neighbors.append(source)
            neighbors[neighborsCount++] = source; 
            
            //PYTHON: new_source_id = new_graph.add_vertex(source.ref)
            if (-1 == (new_source_id = CLGraphDataAddVertex(newGraphp, source->Connection, verbose))) {
               status = FERROR; 
               break;
            }
            
            //PYTHON: new_graph.add_arc(new_source_id, new_vertex_id)
            if (-1 == CLGraphDataAddArc(newGraphp, new_source_id, new_vertex_id, verbose)) {
               status = FERROR; 
               break;
            }
         }
      }
      
      //PYTHON: for arc_id in vertex.inbound :
      for (ii = 0; ii < vertexp->InboundCount; ii++) {
         //PYTHON: self.del_arc(arc_id)
         if (vertexp->Inbound[ii] >= 0) 
            (void)CLGraphDataDelArc(graphp, vertexp->Inbound[ii]);
      }
      
      //PYTHON: for n in neighbors :
      for (nn = 0; nn < neighborsCount; nn++) {
         //PYTHON: self.split_out_vertex(new_graph, n)
         CLGraphDataSplitOutVertex(graphp, newGraphp, neighbors[nn], verbose);
      }
   }
   
   // deallocate dynamic array
   MemoryDeallocate(neighbors); 
   
   return status;
}

static void CLDijkstraFreeArray(void **array, uint32 nRows) 
{ 
   int i; 
   
   if (array) {
      for (i = 0; i < nRows; i++) {
         if (array[i]) 
            MemoryDeallocate(array[i]);
      }
      MemoryDeallocate(array);
   }
}

static void** CLDijkstraAllocArray(uint32 nRows, uint32 nCols, uint32 entryLenth) 
{ 
   int i; 
   void **array; 
   
   array = MemoryAllocate2AndClear(nRows * sizeof(void *), IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
   if (!array) 
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
   else {
      for (i = 0; i < nRows; i++) {
         array[i] = MemoryAllocate2AndClear(nCols * entryLenth, IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
         if (!array[i]) {
            fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
            (void)CLDijkstraFreeArray(array, i); 
            return NULL;
         }
      }
   }
   
   return array;
}

static clVertixDataDistance_t* CLDijkstraAddVertexDistance(cl_qmap_t *mapp, clVertixData_t *vertixp, int verbose) 
{ 
   cl_map_item_t *mi; 
   clVertixDataDistance_t *entryp = NULL; 
   
   if (mapp && vertixp) {
      entryp = (clVertixDataDistance_t *)MemoryAllocate2AndClear(sizeof(clVertixDataDistance_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
      if (!entryp) 
         fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
      else {
         entryp->vertixp = vertixp; 
         mi = cl_qmap_insert(mapp, vertixp->Id, &entryp->AllVerticesEntry); 
         if (verbose >= 4) 
            printf("Add distance vertix Id %d\n", vertixp->Id); 
         if (mi != &entryp->AllVerticesEntry) {
            MemoryDeallocate(entryp); 
            entryp = NULL; 
            fprintf(stderr, "Error, failed to add vertix Id %d\n", vertixp->Id);
         }
      }
   }
   
   return entryp;
}

static int CLDijkstraVertexPopDistance(cl_qmap_t *mapp) 
{ 
   uint32 id = -1; 
   cl_map_item_t *mi; 
   clVertixDataDistance_t *entryp; 
   
   mi = cl_qmap_head(mapp); 
   if (mi != cl_qmap_end(mapp)) {
      if (NULL != (entryp = PARENT_STRUCT(mi, clVertixDataDistance_t, AllVerticesEntry))) {
         id = entryp->vertixp->Id; 
         cl_qmap_remove_item(mapp, &entryp->AllVerticesEntry); 
         MemoryDeallocate(entryp);
      }
   }
   
   return id;
}

//PYTHON: def get_distance (distances, src, dst) :
static FSTATUS CLDijkstraGetDistance(uint32 **distances, uint32 nRows, uint32 nCols, uint32 src, uint32 dst, uint32 *value) 
{ 
   FSTATUS status = FSUCCESS; 
   
   //PYTHON: if distances.get(src) == None :
   //PYTHON:    return infinity
   *value = DIJKSTRA_INFINITY; 
   if (distances == NULL) 
      status = FERROR; 
   else if (src >= nRows || dst >= nCols) {
      status = FINVALID_PARAMETER; 
      fprintf(stderr, "Warning, invalid distance parameters: src %d, dst %d\n", src, dst);
   } else {
      //PYTHON: return distances.get(src).get(dst)
      *value = distances[src][dst];
   }
   
   return status;
}

//PYTHON: def check_distances_and_routes (graph, distances, routes) :
static int CLDijkstraCheckDistancesAndRoutes(clGraphData_t *graphp, uint32 **distances, uint32 **routes, uint32 nRows, uint32 nCols, uint32 verbose) 
{ 
   uint32 ii, jj, errors = 0;      //PYTHON: errors = 0
   clVertixData_t * i,*j; 
   
   //PYTHON: for i in graph.vertices :
   for (ii = 0; ii < graphp->VerticesLength; ii++) {
      //PYTHON: if i.refcount :
      if ((i = graphp->Vertices[ii]) && i->RefCount) {
         //PYTHON: for j in graph.vertices :
         for (jj = 0; jj < graphp->VerticesLength; jj++) {
            uint32 d, distance; 
            
            //PYTHON: if j.refcount :
            if ((j = graphp->Vertices[jj]) && j->RefCount) {
               uint32 k, found; 
               
               d = 0;      //PYTHON: d = 0                    
               k = i->Id;  //PYTHON: k = i.id
               
               //PYTHON: while j.id in routes[k] :
               //CJKING: while ((found = CLListUint32Find(j->Id, routes[k], nCols, verbose))) {
               while ((found = CLListUint32Find(routesLstMap, j->Id))) {
                  uint32 l; 
                  
                  l = routes[k][j->Id];   //PYTHON: l = routes[k][j.id]
                  d++;                    //PYTHON: d += 1
                                          //PYTHON: if l == j.id :
                  if (l == j->Id) 
                     break; 
                  k = l;                  //PYTHON: k = l
               }
               //PYTHON: else :
               //PYTHON: d = infinity
               if (!found) 
                  d = DIJKSTRA_INFINITY; 
               
               //PYTHON: distance = get_distance(distances, i.id, j.id)
               if (FERROR == CLDijkstraGetDistance(distances, nRows, nCols, i->Id, j->Id, &distance)) 
                  return -1; 
               if (distance == d) {        //if distance == d :
                  if (verbose >= 4) 
                     printf("Check route from %d to %d of %d hops: OK\n", i->Id, j->Id, distance);
               } else {
                  errors++;        //PYTHON: errors += 1
                  if (verbose >= 4) 
                     fprintf(stderr, "Warning, check route from %d to %d of %d hops: failed, got %d hops\n", 
                             i->Id, j->Id, distance, d);
               }
            }
         }
      }
   }
   
   if (verbose >= 4) 
      printf("Check route summary : %d dependency graph routes have errors\n", errors); 
   
   return errors;
}

FSTATUS CLFabricDataDestroy(FabricData_t *fabricp, void *context) 
{ 
   (void)CLDeviceDataFreeAll(fabricp, context); 
   (void)CLGraphDataFree(&fabricp->Graph, context); 
   memset(&fabricp->Graph, 0, sizeof(fabricp->Graph)); 
   
   cl_qmap_init(&fabricp->map_guid_to_ib_device, NULL); 
   QListInitState(&fabricp->FIs); 
   if (!QListInit(&fabricp->FIs)) {
      fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname); 
      goto fail;
   }
   QListInitState(&fabricp->Switches); 
   if (!QListInit(&fabricp->Switches)) {
      fprintf(stderr, "%s: Unable to initialize List\n", g_Top_cmdname); 
      goto fail;
   }
   cl_qmap_init(&fabricp->Graph.Arcs, NULL); 
   cl_qmap_init(&fabricp->Graph.map_arc_key_to_arc, NULL); 
   cl_qmap_init(&fabricp->Graph.map_conn_to_vertex_conn, NULL); 
   
   return FSUCCESS; 
   
fail:
   return FERROR;
}

//PYTHON: def add_device (fabric, guid, lid, hfi, name) :
NodeData* CLDataAddDevice(FabricData_t *fabricp, NodeData *nodep, STL_LID lid, int verbose, int quiet) 
{ 
   cl_map_item_t *mi; 
   clDeviceData_t *ccDevicep; 
   
   if (nodep) {
      NodeData *p; 
      
      //PYTHON: device = fabric.map_guid_to_ib_device.get(guid)
      mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, nodep->NodeInfo.NodeGUID); 
      if (mi != cl_qmap_end(&fabricp->map_guid_to_ib_device)) {
         ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
         if (verbose >= 5) 
            fprintf(stderr, "%s: Duplicate NodeGuids found in nodeRecords: 0x%"PRIx64"\n", 
                    g_Top_cmdname, nodep->NodeInfo.NodeGUID); 
         
         p = ccDevicep->nodep; 
         //PYTHON: if device.guid != guid or device.lid != lid or
         //PYTHON:    device.hfi != hfi or device.name != name :
         if (ccDevicep->Lid != lid || 
             p->NodeInfo.NodeGUID != nodep->NodeInfo.NodeGUID || 
             p->NodeInfo.NodeType != nodep->NodeInfo.NodeType || 
             strcmp((char *)p->NodeDesc.NodeString, (char *)nodep->NodeDesc.NodeString)) {
             if (verbose >= 5) 
                fprintf(stderr, "Warning, Duplicate device (%s, 0x%016"PRIx64", LID 0x%08x, %s) differs from (%s, 0x%016"PRIx64", LID 0x%04x, %s) with same GUID\n", 
                        (char *)nodep->NodeDesc.NodeString, nodep->NodeInfo.NodeGUID, lid, StlNodeTypeToText(nodep->NodeInfo.NodeType), 
                        (char *)p->NodeDesc.NodeString, p->NodeInfo.NodeGUID, ccDevicep->Lid, StlNodeTypeToText(p->NodeInfo.NodeType));
         }
         return nodep;
      }
      
      //PYTHON: device = IbDevice(fabric, guid, lid, hfi, name)
      ccDevicep = (clDeviceData_t *)MemoryAllocate2AndClear(sizeof(clDeviceData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
      if (!ccDevicep) {
         fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
         return NULL;
      }
      
      ccDevicep->nodep = nodep; 
      ccDevicep->Lid = lid; 
      cl_qmap_init(&ccDevicep->map_dlid_to_route, NULL); 
      //PYTHON: fabric.devices.append(device)
      //PYTHON: fabric.map_guid_to_ib_device[guid] = device
      mi = cl_qmap_insert(&fabricp->map_guid_to_ib_device, nodep->NodeInfo.NodeGUID, &ccDevicep->AllDevicesEntry); 
      if (mi != &ccDevicep->AllDevicesEntry) {
         MemoryDeallocate(ccDevicep); 
         ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
         fprintf(stderr, "Error, failed to add %s %s with GUID 0x%016"PRIx64" and LID 0x%08x\n", 
                 StlNodeTypeToText(nodep->NodeInfo.NodeType), 
                 (char *)nodep->NodeDesc.NodeString, 
                 nodep->NodeInfo.NodeGUID, 
                 lid); 
         return NULL;
      } else {
         ListItemInitState(&ccDevicep->AllDeviceTypesEntry); 
         QListSetObj(&ccDevicep->AllDeviceTypesEntry, ccDevicep); 
         
         if (nodep->NodeInfo.NodeType == STL_NODE_FI) 
            QListInsertTail(&fabricp->FIs, &ccDevicep->AllDeviceTypesEntry);       //PYTHON: fabric.hfis.append(device)
         else 
            QListInsertTail(&fabricp->Switches, &ccDevicep->AllDeviceTypesEntry);   //PYTHON: fabric.switches.append(device)
         if (verbose >= 4 && !quiet) 
            ProgressPrint(FALSE, "Add %s %s with GUID 0x%016"PRIx64" and LID 0x%08x", 
                   StlNodeTypeToText(nodep->NodeInfo.NodeType), 
                   (char *)nodep->NodeDesc.NodeString, 
                   nodep->NodeInfo.NodeGUID, 
                   lid);
      }
      return nodep;
   }
   
   return NULL;
}

//PYTHON: def add_connection (fabric, guid1, port1, guid2, port2, rate) :
FSTATUS CLDataAddConnection(FabricData_t *fabricp, PortData *portp1, PortData *portp2, clConnPathData_t *pathInfo, uint8 sc, int verbose, int quiet) 
{ 
   FSTATUS status = FSUCCESS; 
   cl_map_item_t *mi; 
   clConnData_t *ccConnp; 
   clDeviceData_t *ccDevicep; 
   
   
   //PYTHON: ib_device1 = fabric.map_guid_to_ib_device.get(guid1)
   mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, portp1->nodep->NodeInfo.NodeGUID); 
   if (mi == cl_qmap_end(&fabricp->map_guid_to_ib_device)) {
      fprintf(stderr, "Error, add connection for missing device GUID 0x%016"PRIx64"\n", portp1->nodep->NodeInfo.NodeGUID); 
      return FINVALID_PARAMETER;
   }
   ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
   
   if (portp1->PortNum >= CREDIT_LOOP_DEVICE_MAX_CONNECTIONS) {
      fprintf(stderr, "Error, port number %d exceeds maximum ports supported by a device\n", portp1->PortNum); 
      return FINVALID_PARAMETER;
   }
   
   //PYTHON: connection = ib_device1.connections.get(port1)
   ccConnp = ccDevicep->Connections[portp1->PortNum][sc];
   if (ccConnp) {
      //PYTHON: if connection.guid1 != guid1 or connection.port1 != port1 or
      //PYTHON:    connection.guid2 != guid2 or connection.port2 != port2 or
      //PYTHON:    connection.rate != rate :
      if ((ccConnp->FromDeviceGUID != portp1->nodep->NodeInfo.NodeGUID || ccConnp->FromPortNum != portp1->PortNum) || 
          (ccConnp->ToDeviceGUID != portp2->nodep->NodeInfo.NodeGUID || ccConnp->ToPortNum != portp2->PortNum) || 
          ((ccConnp->Rate && portp1->rate) && (ccConnp->Rate != portp1->rate))) {
         status = FERROR; 
         fprintf(stderr, "Error, new connection (0x%016"PRIx64":%3.3u to 0x%016"PRIx64":%3.3u at rate %s) differs from (0x%016"PRIx64":%3.3u to 0x%016"PRIx64":%3.3u at rate %s)\n", 
                 ccConnp->FromDeviceGUID, ccConnp->FromPortNum, ccConnp->ToDeviceGUID, ccConnp->ToPortNum, StlStaticRateToText(ccConnp->Rate), 
                 portp1->nodep->NodeInfo.NodeGUID, portp1->PortNum, portp2->nodep->NodeInfo.NodeGUID, portp2->PortNum, StlStaticRateToText(portp1->rate));
      }
   } else {
      //PYTHON: ib_device1.connections[port1] = IbConnection(fabric, guid1, port1, guid2, port2, rate)
      ccConnp = ccDevicep->Connections[portp1->PortNum][sc] =
         (clConnData_t *)MemoryAllocate2AndClear(sizeof(clConnData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
      
      if (!ccConnp) {
         status = FINSUFFICIENT_MEMORY; 
         fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
      } else {
         //PYTHON: fabric.connections += 1
         fabricp->ConnectionCount++; 
         ccConnp->FromDeviceGUID = portp1->nodep->NodeInfo.NodeGUID;         // GUID of the HFI, TFI, switch
         ccConnp->FromPortNum = portp1->PortNum; 
         ccConnp->ToDeviceGUID = portp2->nodep->NodeInfo.NodeGUID;           // GUID of the HFI, TFI, switch
         ccConnp->ToPortNum = portp2->PortNum; 
         ccConnp->PathInfo = *pathInfo;
         if (portp1->pQOS)
            ccConnp->VL = portp1->pQOS->SC2VLMaps[Enum_SCVLt].SCVLMap[sc];
         
         if (verbose >= 4 && !quiet) 
            ProgressPrint(FALSE, "Add connection 0x%016"PRIx64":%3.3u to 0x%016"PRIx64":%3.3u at rate %s",
                   portp1->nodep->NodeInfo.NodeGUID, portp1->PortNum, portp2->nodep->NodeInfo.NodeGUID,
                   portp2->PortNum, StlStaticRateToText(portp1->rate));
      }
   }
 
   return status;
}

//PYTHON: add_route(fabric, slid, dlid, route_sguid, route_sport)
FSTATUS CLDataAddRoute(FabricData_t *fabricp, STL_LID slid, STL_LID dlid, PortData *sportp, int verbose, int quiet) 
{ 
   FSTATUS status = FSUCCESS; 
   cl_map_item_t *mi; 
   clDeviceData_t *ccDevicep; 
   clRouteData_t *ccRoutep = NULL; 
   
   
   //PYTHON: ib_device = fabric.map_guid_to_ib_device.get(guid)
   //PYTHON: if not ib_device :
   mi = cl_qmap_get(&fabricp->map_guid_to_ib_device, sportp->nodep->NodeInfo.NodeGUID); 
   if (mi == cl_qmap_end(&fabricp->map_guid_to_ib_device)) {
      fprintf(stderr, "Warning, add connection for missing device GUID 0x%016"PRIx64"\n", sportp->nodep->NodeInfo.NodeGUID); 
      return FNOT_FOUND;
   }
   ccDevicep = PARENT_STRUCT(mi, clDeviceData_t, AllDevicesEntry); 
   
   //PYTHON: route = ib_device.routes.get(dlid)
   mi = cl_qmap_get(&ccDevicep->map_dlid_to_route, dlid); 
   if (mi != cl_qmap_end(&ccDevicep->map_dlid_to_route)) 
      ccRoutep = PARENT_STRUCT(mi, clRouteData_t, AllRoutesEntry); 
   
   if (ccRoutep){
      if (ccRoutep->portp != sportp) {
         status = FERROR; 
         fprintf(stderr, "Error, new route entry from SLID 0x%08x to DLID 0x%08x at 0x%016"PRIx64" has port %3.3u not expected port %3.3u\n", 
                 slid, dlid, sportp->nodep->NodeInfo.NodeGUID, sportp->PortNum, ccRoutep->portp->PortNum);
      }
   } else {
      ccRoutep = (clRouteData_t *)MemoryAllocate2AndClear(sizeof(clRouteData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
      
      if (!ccRoutep) {
         status = FINSUFFICIENT_MEMORY; 
         fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
      } else {
         //PYTHON: ib_device.routes[dlid] = port
         ccRoutep->portp = sportp; 
         mi = cl_qmap_insert(&ccDevicep->map_dlid_to_route, dlid, &ccRoutep->AllRoutesEntry); 
         if (mi != &ccRoutep->AllRoutesEntry) {
            if (verbose >= 4) {
               fprintf(stderr, "%s: Duplicate DLID found in routes map: 0x%08x\n", g_Top_cmdname, dlid);
            }
            MemoryDeallocate(ccRoutep); 
            ccRoutep = PARENT_STRUCT(mi, clRouteData_t, AllRoutesEntry);
         } else {
            //PYTHON: fabric.routes += 1
            fabricp->RouteCount++; 
            if (verbose >= 4 && !quiet) 
               ProgressPrint(FALSE, "Add route SLID 0x%08x to DLID 0x%08x at GUID 0x%016"PRIx64" port %3.3u",
                      slid, dlid, sportp->nodep->NodeInfo.NodeGUID, sportp->PortNum);
         }
      }
   }
   
   return status;
}

//PYTHON: def IbFabric.summary (self, name) :
void CLFabricSummary(FabricData_t *fabricp, const char *name,
                     ValidateCLFabricSummaryCallback_t callback,
                     uint32 totalPaths, uint32 totalBadPaths,
                     void *context) 
{ 
   callback(fabricp, name, totalPaths, totalBadPaths, context);
}

//PYTHON: def Graph.summary (self, name) :
void CLGraphDataSummary(clGraphData_t *graphp, const char *name, ValidateCLDataSummaryCallback_t callback, void *context) 
{ 
   callback(graphp, name, context);
}

FSTATUS CLFabricDataBuildRouteGraph(FabricData_t *fabricp,  ValidateCLRouteSummaryCallback_t routeSummaryCallback, ValidateCLTimeGetCallback_t timeGetCallback, void *context, uint32 usedSLs) 
{ 
   FSTATUS status = FSUCCESS; 
   uint32 i = 0, l = 0, t, threadsActive = 0; 
   LIST_ITEM *srcHcaLstp; 
   pthread_t threadId; 
   uint64_t sTime = 0, eTime = 0; 
   ValidateCreditLoopRoutesContext_t *cp = (ValidateCreditLoopRoutesContext_t *)context; 
   int xmlFmt = (cp->format == 1) ? 1 : 0; 
   int verbose = (xmlFmt) ? 0 : cp->detail; 
   int singleThreaded = 1;
   uint32 maxHcaListEntry = QListCount(&fabricp->FIs) / CL_MAX_THREADS; 
   clThreadContext_t *threadContexts; 
   
   
   cl_qmap_init(&hopsHistogramLstMap, NULL); 
   if (hops_histogram != NULL) 
      memset(hops_histogram, 0, hops_histogram_length); 
   
   threadContexts = 
      (clThreadContext_t *)MemoryAllocate2AndClear(sizeof(clThreadContext_t) * CL_MAX_THREADS, IBA_MEM_FLAG_PREMPTABLE, MYTAG); 
   
   if (!threadContexts) {
      status = FINSUFFICIENT_MEMORY; 
      fprintf(stderr, "%s: Unable to allocate thread contexts\n", g_Top_cmdname); 
      return status;
   }
   
   if (!xmlFmt && verbose >= 4) {
      timeGetCallback(&sTime, &g_cl_lock); 
      printf("START build graphical layout of all the routes\n"); 
   }
   
   pthread_mutex_init(&g_cl_lock, NULL); 
   
   //PYTHON: for src_hfi in fabric.hfis :
   if ((singleThreaded) || (QListCount(&fabricp->FIs) < 100)) {
      threadContexts[0].srcHcaList = QListHead(&fabricp->FIs); 
      
      // determine whether to start worker thread
      threadContexts[0].fabricp = fabricp; 
      threadContexts[0].verbose = verbose; 
      threadContexts[0].quiet = cp->quiet; 
      threadContexts[0].threadId = 0; 
      threadContexts[0].srcHcaListLength = QListCount(&fabricp->FIs);
      threadContexts[0].timeGetCallback = timeGetCallback; 
      threadContexts[0].usedSLs = usedSLs;
      threadsActive++; 
      pthread_create(&threadId, NULL, CLFabricDataBuildRouteGraphThread, &threadContexts[0]);
   } else {
      for (srcHcaLstp = QListHead(&fabricp->FIs); srcHcaLstp != NULL; srcHcaLstp = QListNext(&fabricp->FIs, srcHcaLstp)) {
         // set first hfi entry to hfi list
         if (l == 0) 
            threadContexts[i].srcHcaList = srcHcaLstp; 
         l++; 
         
         // determine whether to start worker thread
         if (i < CL_MAX_THREADS - 1 && l >= maxHcaListEntry) {
            l = 0; 
            threadContexts[i].fabricp = fabricp; 
            threadContexts[i].verbose = verbose; 
            threadContexts[i].quiet = cp->quiet; 
            threadContexts[i].threadId = i; 
            threadContexts[i].srcHcaListLength = maxHcaListEntry;
            threadContexts[i].timeGetCallback = timeGetCallback;
            threadContexts[i].usedSLs = usedSLs;
            threadsActive++; 
            pthread_create(&threadId, NULL, CLFabricDataBuildRouteGraphThread, &threadContexts[i++]);
         }
      }
      
      // start last worker thread
      threadContexts[i].fabricp = fabricp; 
      threadContexts[i].verbose = verbose; 
      threadContexts[i].quiet = cp->quiet; 
      threadContexts[i].threadId = i; 
      threadContexts[i].srcHcaListLength = maxHcaListEntry + (QListCount(&fabricp->FIs) % CL_MAX_THREADS);
      threadContexts[i].timeGetCallback = timeGetCallback;
      threadContexts[i].usedSLs = usedSLs;
      threadsActive++; 
      pthread_create(&threadId, NULL, CLFabricDataBuildRouteGraphThread, &threadContexts[i]);
   }
   
   //
   // wait for worker threads to complete processing
   while (threadsActive > 0) {
      (void)CLThreadSleep(VTIMER_1_MILLISEC); 
      for (t = 0; t < CL_MAX_THREADS; t++) {
         if (threadContexts[t].threadExit) {
            threadContexts[t].threadExit = 0; 
            threadsActive--; 
         }
      }
   }

   // clear line used to display progress report
   if (!cp->quiet)
      ProgressPrint(TRUE, "Done Building Graphical Layout of All Routes");
   
   if (!xmlFmt && verbose >= 4) {
      timeGetCallback(&eTime, &g_cl_lock); 
      printf("END build graphical layout of all the routes; elapsed time(usec)=%d, (sec)=%d\n", 
             (int)(eTime - sTime), ((int)(eTime - sTime)) / CL_TIME_DIVISOR); 
   }
   
   //
   // report routes summary
   if (hops_histogram != NULL) {
      if (verbose >= 4) 
         routeSummaryCallback(present_routes, missing_routes, hops_histogram_entries, hops_histogram, context); 
      MemoryDeallocate(hops_histogram);
   }
   MemoryDeallocate(threadContexts); 
   
   return status;
}

//PYTHON: Graph.def split (self) :
clGraphData_t* CLGraphDataSplit(clGraphData_t *graphp, int verbose) 
{ 
   uint32 vv; 
   clGraphData_t *newGraphp; 
   clVertixData_t *rootVertexp = NULL;     //PYTHON: root = None
   
   
   //PYTHON: for vertex in self.vertices :
   for (vv = 0; vv < graphp->NumVertices; vv++) {
      clVertixData_t *vertexp = graphp->Vertices[vv]; 
      
      //PYTHON: if vertex.refcount :
      if (vertexp->RefCount) {
         rootVertexp = vertexp;      //PYTHON: root = vertex
         break;
      }
   }
   
   //PYTHON: if not root :
   if (!rootVertexp) 
      return NULL; 
   
   //PYTHON: new_graph = Graph()
   newGraphp = (clGraphData_t *)MemoryAllocate2AndClear(sizeof(clGraphData_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG);        
   if (!newGraphp) {
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname);
   } else {
      cl_qmap_init(&newGraphp->Arcs, NULL); 
      cl_qmap_init(&newGraphp->map_arc_key_to_arc, NULL); 
      //PYTHON: self.split_out_vertex(new_graph, root);
      CLGraphDataSplitOutVertex(graphp, newGraphp, rootVertexp, verbose);
   }
   return newGraphp;
}

void CLGraphDataPrune(clGraphData_t *graphp, ValidateCLTimeGetCallback_t timeGetCallback, int verbose, int quiet) 
{ 
   uint32 ii, vv, progress = 1, round = 0; 
   uint64_t sTime = 0, eTime = 0; 
   
   if (verbose >= 4 && !quiet) {
      timeGetCallback(&sTime, &g_cl_lock); 
      ProgressPrint(TRUE, "START pruning of graphical layout of all the routes");
   }
   
   //PYTHON: progress = 1;
   //PYTHON: round = 0;
   //PYTHON: while progress :
   while (progress) {          // DEBUG_CODE --- Possible infinite loop, look into later????
      progress = 0; 
      //PYTHON: for vertex in self.vertices :
      for (vv = 0; vv < graphp->NumVertices; vv++) {
         clVertixData_t *vertexp = graphp->Vertices[vv]; 
         
         //PYTHON: if vertex.refcount :
         if (vertexp->RefCount) {
            //PYTHON: if len(vertexp->outbound) == 0 :
            if (vertexp->OutboundInuseCount == 0) {
               //PYTHON: for arc_id in vertexp->inbound :
               for (ii = 0; ii < vertexp->InboundCount; ii++) {
                  if (vertexp->Inbound[ii] >= 0) {
                     if (verbose >= 4 && !quiet) 
                        ProgressPrint(FALSE, "Remove arc id %d since vertex id %d is pure sink", 
                               vertexp->Inbound[ii], vertexp->Id); 
                     //PYTHON: self.del_arc(arc_id);
                     CLGraphDataDelArc(graphp, vertexp->Inbound[ii]); 
                     //PYTHON: progress += 1
                     progress++;
                  }
               }
            }
            
            //PYTHON: if len(vertexp->inbound) == 0 :
            if (vertexp->InboundInuseCount == 0) {
               //PYTHON: for arc_id in vertex.outbound :
               for (ii = 0; ii < vertexp->OutboundCount; ii++) {
                  if (vertexp->Outbound[ii] >= 0) {
                     if (verbose >= 4 && !quiet) 
                        ProgressPrint(FALSE, "Remove arc id %d since vertex id %d is pure source", 
                               vertexp->Outbound[ii], vertexp->Id); 
                     //PYTHON: self.del_arc(arc_id);
                     CLGraphDataDelArc(graphp, vertexp->Outbound[ii]); 
                     //PYTHON: progress += 1
                     progress++;
                  }
               }
            }
         }
      }
      
      //PYTHON: round += 1
      round++; 
      if (verbose >= 4 && !quiet) 
         ProgressPrint(TRUE, "Graph pruning round %d : deleted %d arcs", round, progress);
   }
   
   if (verbose >= 4 && !quiet) {
      timeGetCallback(&eTime, &g_cl_lock); 
      ProgressPrint(TRUE, "END pruning of graphical layout of all the routes; elapsed time(usec)=%d, (sec)=%d", 
             (int)(eTime - sTime), ((int)(eTime - sTime)) / CL_TIME_DIVISOR);
   }
}

void CLDijkstraFreeDistancesAndRoutes(clDijkstraDistancesAndRoutes_t *drp) 
{ 
   if (drp->distances) 
      (void)CLDijkstraFreeArray((void **)drp->distances, drp->nRows); 
   if (drp->routes) 
      (void)CLDijkstraFreeArray((void **)drp->routes, drp->nRows); 
   memset(drp, 0, sizeof(clDijkstraDistancesAndRoutes_t));
}

//PYTHON: def find_distances_and_routes_dijkstra (graph) :
FSTATUS CLDijkstraFindDistancesAndRoutes(clGraphData_t *graphp, clDijkstraDistancesAndRoutes_t *respData, int verbose) 
{ 
   FSTATUS status = FERROR; 
   int c; 
   uint32 nRows, nCols;
   uint32 ii, jj, d, current = 0; 
   uint32 next, numVertices, maxDistance; 
   uint32 **distances = NULL,**routes = NULL; 
   clVertixData_t *i = NULL,*j = NULL; 
   uint32 *dist = NULL,*route = NULL,*previous = NULL; 
   cl_qmap_t previousLstMap; 
   
   
   // allocate persistant multidimensional arrays and temporary buffers
   if (!graphp || !respData) 
      return FINVALID_PARAMETER;
   nRows = graphp->VerticesLength;
   nCols = graphp->VerticesLength; 
   if (!(distances    = (uint32 **)CLDijkstraAllocArray(nRows, nCols, sizeof(uint32))) ||  //PYTHON: distances = {}
            !(routes       = (uint32 **)CLDijkstraAllocArray(nRows, nCols, sizeof(uint32))) ||  //PYTHON: routes = {}
            !(dist         = MemoryAllocate2AndClear(nCols * sizeof(uint32), IBA_MEM_FLAG_PREMPTABLE, MYTAG)) || 
            !(route        = MemoryAllocate2AndClear(nCols * sizeof(uint32), IBA_MEM_FLAG_PREMPTABLE, MYTAG)) || 
            !(previous     = MemoryAllocate2AndClear(nCols * sizeof(uint32), IBA_MEM_FLAG_PREMPTABLE, MYTAG)) || 
            !(routesLstMap = MemoryAllocate2AndClear(nRows * sizeof(cl_qmap_t), IBA_MEM_FLAG_PREMPTABLE, MYTAG))) {
      status = FINSUFFICIENT_MEMORY; 
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
      goto fail;
   }
   
   //PYTHON: num_vertices = graph.num_vertices
   numVertices = graphp->NumVertices; 
   if (verbose >= 4) 
      printf("Calculating distances for %d vertices in graph\n", numVertices); 
   
   //PYTHON: for i in graph.vertices :
   for (ii = 0; ii < graphp->VerticesLength; ii++) {
      cl_qmap_t todo; 
      
      //PYTHON: if i.refcount :
      if ((i = graphp->Vertices[ii]) && i->RefCount) {
         //PYTHON: dist = {}
         memset(dist, 0, sizeof(nCols * sizeof(uint32))); 
         //PYTHON: previous = {}
         memset(previous, 0, sizeof(nCols * sizeof(uint32))); 
         //PYTHON: todo = []
         cl_qmap_init(&todo, NULL); 
         cl_qmap_init(&routesLstMap[i->Id], NULL); 
         // CJKING: Note, in order to save time, purposely not deallocating memory
         // allocated for each list entry.  May need to visit this later for extremely
         // large fabrics.
         cl_qmap_init(&previousLstMap, NULL); 
         
         //PYTHON: for j in graph.vertices :
         for (jj = 0; jj < graphp->VerticesLength; jj++) {
            if ((j = graphp->Vertices[jj]) && j->RefCount) {
               dist[j->Id] = DIJKSTRA_INFINITY; 
               //PYTHON: todo.append(j.id)
               if (!CLDijkstraAddVertexDistance(&todo, j, verbose)) 
                  goto fail; 
               //PYTHON: previous[j.id] = None
               previous[j->Id] = 0;
            }
         }
         
         //PYTHON: dist[i.id] = 0
         dist[i->Id] = 0; 
         //PYTHON: previous[i.id] = i.id
         previous[i->Id] = i->Id; 
         //CJKING: Add entry to search list for previous array 
         if (CLListUint32Add(&previousLstMap, i->Id)) 
            goto fail; 
         
         //current = i
         current = ii; 
         
         //while todo :
         while (cl_qmap_count(&todo)) {
            /*CJKING: Not certain of the purpose of this code in the script.

              PYTHON: global vertex_dist
              PYTHON: vertex_dist = dist
              PYTHON: todo.sort(vertex_distance_compare)
              PYTHON: vertex_dist = {}
            */
            
            //PYTHON: current = todo.pop(0)
            if (-1 == (current = CLDijkstraVertexPopDistance(&todo))) {
               if (verbose >= 3) 
                  fprintf(stderr, "Warning, pop of vertices list failed\n"); 
               break;
            }
            
            //PYTHON: if dist[current] == infinity :
            if (verbose >= 4 && dist[current] == DIJKSTRA_INFINITY) {
               fprintf(stderr, "Warning, distance of current id %d should not be infinite for fully connected graph\n", current);
            }
            
            //PYTHON: for c in graph.vertices[current].outbound :
            for (c = 0; c < graphp->Vertices[current]->OutboundCount; c++) {
               clVertixData_t *j; 
               clArcData_t *arcp; 
               
               if (graphp->Vertices[current]->Outbound[c] >= 0 && (arcp = CLGraphFindIdArc(graphp, graphp->Vertices[current]->Outbound[c]))) {
                  //PYTHON: j = graph.vertices[graph.arcs[c].sink]
                  j = graphp->Vertices[arcp->Sink]; 
                  
                  //PYTHON: if not j :
                  if (!j) 
                     fprintf(stderr, "Warning, cannot follow connection for vertex %d\n", current); 
                  else {  //PYTHON: elif j.id in todo :   /* CJKING: Unclear about this elif.  Might use the following */
                     /* else if (CLDijkstraIsVertexDistance(&todo, j->Id))          */
                     //PYTHON: d = dist[current] + 1
                     d = dist[current] + 1; 
                     //PYTHON: if d < dist[j.id] :
                     if (d < dist[j->Id]) {
                        if (verbose >= 4) 
                           printf("Distance from %d to %d reduces from %d to %d (via %d)\n", 
                                  i->Id, j->Id, dist[j->Id], d, current); 
                        //PYTHON: dist[j.id] = d
                        dist[j->Id] = d; 
                        //PYTHON: previous[j.id] = current
                        previous[j->Id] = current; 
                        //CJKING: Add entry to search list for previous array 
                        if (CLListUint32Add(&previousLstMap, current)) 
                           goto fail;
                     }
                  }
               }
            }
         }
         
         //PYTHON: route = {}
         memset(route, 0, sizeof(nCols * sizeof(uint32))); 
         //PYTHON: for j in graph.vertices :
         for (jj = 0; jj < graphp->VerticesLength; jj++) {
            //PYTHON: if j.refcount and dist[j.id] != infinity :
            if ((j = graphp->Vertices[jj]) && j->RefCount && (dist[j->Id] != DIJKSTRA_INFINITY)) {
               if (verbose >= 4) 
                  printf("Route backwards from vertex %d\n", j->Id); 
               
               //PYTHON: current = j.id
               current = j->Id; 
               //PYTHON: next = current
               next = current; 
               
               //PYTHON: while current != i.id :
               while (current != i->Id) {
                  //next = current
                  next = current; 
                  //PYTHON: if not current in previous :
                  //CJKING: if (!CLListUint32Find(current, previous, nCols, verbose)) {
                  if (!CLListUint32Find(&previousLstMap, current)) {
                     if (verbose >= 5) 
                        fprintf(stderr, "Warning, cannot route backwards from vertex %d to %d at %d\n", 
                                j->Id, i->Id, current);
                  }
                  
                  //PYTHON: current = previous[current]
                  current = previous[current]; 
                  if (verbose >= 4 && next != j->Id) 
                     printf("\tvia %d\n", next);
               }
               
               if (verbose >= 4) 
                  printf("\tto %d (next is %d)\n", i->Id, next); 
               //PYTHON: route[j.id] = next
               route[j->Id] = next; 
               //CJKING: Add entry to search list for route array 
               if (CLListUint32Add(&routesLstMap[i->Id], next)) 
                  goto fail;
            }
         }
         
         //PYTHON: routes[i.id] = route
         //PYTHON: distances[i.id] = dist
         memcpy(routes[i->Id], route, nCols); 
         memcpy(distances[i->Id], dist, nCols);
      }
   }
   
   // recompute distance to self for shortest loop (instead of 0)
   //PYTHON: for i in graph.vertices :
   for (ii = 0; ii < graphp->VerticesLength; ii++) {
      //PYTHON: if i.refcount :
      if ((i = graphp->Vertices[ii]) && i->RefCount) {
         uint32 route = 0;       //PYTHON: route = None
         
         //PYTHON: d = infinity
         d = DIJKSTRA_INFINITY; 
         
         //PYTHON: for c in graph.vertices[i.id].outbound :
         for (c = 0; c < graphp->Vertices[i->Id]->OutboundCount; c++) {
            clVertixData_t *j; 
            clArcData_t *arcp; 
            
            if (graphp->Vertices[i->Id]->Outbound[c] >= 0 && (arcp = CLGraphFindIdArc(graphp, graphp->Vertices[i->Id]->Outbound[c]))) {
               //PYTHON: j = graph.vertices[graph.arcs[c].sink]
               j = graphp->Vertices[arcp->Sink]; 
               //PYTHON: if not j :
               if (!j) 
                  fprintf(stderr, "Warning, cannot follow connection for vertex %d\n", i->Id); 
               else if (distances[j->Id][i->Id] < d) { //PYTHON: elif distances[j.id][i.id] < d :
                                                       //PYTHON: d = distances[j.id][i.id] + 1
                  d = distances[j->Id][i->Id] + 1; 
                  //PYTHON: route = j.id
                  route = j->Id;
               }
            }
         }
         
         //PYTHON: if d == infinity :
         if (d == DIJKSTRA_INFINITY) {
            if (verbose >= 5) 
               printf("Self-distance for %d is infinite (no routes to self)\n", i->Id);
         } else {
            //PYTHON: distances[i.id][i.id] = d
            distances[i->Id][i->Id] = d; 
            //PYTHON: routes[i.id][i.id] = route
            routes[i->Id][i->Id] = route; 
            if (verbose >= 4) 
               printf("Self-distance for %d is %d via %d\n", i->Id, d, route);
         }
      }
   }
   
   //PYTHON: max_distance = 0
   maxDistance = 0; 
   //PYTHON: for i in graph.vertices :
   for (ii = 0; ii <  graphp->VerticesLength; ii++) {
      if ((i = graphp->Vertices[ii])) {
         //PYTHON: for j in graph.vertices :
         for (jj = 0; jj < graphp->VerticesLength; jj++) {
            //PYTHON: if i.refcount and j.refcount :
            if (i->RefCount && ((j = graphp->Vertices[jj]) && j->RefCount)) {
               //PYTHON: d = get_distance(distances, i.id, j.id)
               if (FERROR == (status = CLDijkstraGetDistance(distances, nRows, nCols, i->Id, j->Id, &d))) {
                  fprintf(stderr, "Error, unable to get distance from vertex %d to vertex %d\n", i->Id, j->Id); 
                  goto fail;
               }
               
               if (verbose >= 5 && d == DIJKSTRA_INFINITY) 
                  fprintf(stderr, "Warning, no path from %d to %d\n", i->Id, j->Id); 
               else if (verbose >= 4 && d != DIJKSTRA_INFINITY) 
                  printf("Distance from vertex %d to vertex %d is %d arcs\n", 
                         i->Id, j->Id, d); 
               
               //PYTHON: if d > max_distance and d != infinity :
               if (d > maxDistance && d != DIJKSTRA_INFINITY) 
                  maxDistance = d;    //PYTHON: max_distance = d
            }
         }
      }
   }
   
   if (verbose >= 4) 
      printf("Maximum distance is %d\n", maxDistance); 
   
   // deallocate temporary buffers
   if (dist) 
      MemoryDeallocate(dist); 
   if (route) 
      MemoryDeallocate(route); 
   if (previous) 
      MemoryDeallocate(previous); 
   
   //PYTHON: check_distances_and_routes(graph, distances, routes)
   CLDijkstraCheckDistancesAndRoutes(graphp, distances, routes, nRows, nCols, verbose); 
   
   // initialize response data
   respData->distances = distances; 
   respData->routes = routes; 
   respData->nRows = nRows; 
   respData->nCols = nCols; 
   
   return FSUCCESS; 
   
fail:
   
   // deallocate multidimensional arrays
   (void)CLDijkstraFreeArray((void **)distances, nRows); 
   (void)CLDijkstraFreeArray((void **)routes, nRows); 
   
   // deallocate temporary buffers
   if (dist) 
      MemoryDeallocate(dist); 
   if (route) 
      MemoryDeallocate(route); 
   if (previous) 
      MemoryDeallocate(previous); 
   
   return status;
}

//PYTHON: def find_cycles (graph, distances, routes, fn) :
void CLDijkstraFindCycles(FabricData_t *fabricp, 
                          clGraphData_t *graphp, 
                          clDijkstraDistancesAndRoutes_t *drp, 
                          ValidateCLLinkSummaryCallback_t linkSummaryCallback, 
                          ValidateCLLinkStepSummaryCallback_t linkStepSummaryCallback, 
                          ValidateCLPathSummaryCallback_t pathSummaryCallback, 
                          void *context) 
{ 
   uint32 ii, dii, next_id, cycles = 0;        //PYTHON: cycles = 0
   int done_vertices_entries = 0, *done_vertices = NULL; 
   uint32 cycle_histogram_entries = 0, *cycle_histogram = NULL, *distance_list = NULL; 
   clVertixData_t *i = NULL; 
   ValidateCreditLoopRoutesContext_t *cp = (ValidateCreditLoopRoutesContext_t *)context; 
   int xmlFmt = (cp->format == 1) ? 1 : 0; 
   int verbose = (xmlFmt) ? 0 : cp->detail; 
   cl_qmap_t cycleHistogramLstMap, doneVerticesLstMap; 
   
   
   if (!(done_vertices     = MemoryAllocate2AndClear(drp->nCols * sizeof(int), IBA_MEM_FLAG_PREMPTABLE, MYTAG)) ||  //PYTHON: done_vertices = []
       !(cycle_histogram   = MemoryAllocate2AndClear(drp->nCols * sizeof(uint32), IBA_MEM_FLAG_PREMPTABLE, MYTAG)) ||  //PYTHON: cycle_histogram = {}
       !(distance_list     = MemoryAllocate2AndClear(drp->nCols * sizeof(uint32), IBA_MEM_FLAG_PREMPTABLE, MYTAG))) {
      fprintf(stderr, "%s: Unable to allocate memory\n", g_Top_cmdname); 
      goto done;
   }
   
   cl_qmap_init(&doneVerticesLstMap, NULL); 
   cl_qmap_init(&cycleHistogramLstMap, NULL); 
   memset(done_vertices, -1, sizeof(*done_vertices) * drp->nCols); 
   
   //PYTHON: for i in graph.vertices :
   for (ii = 0; ii < graphp->VerticesLength; ii++) {
      //PYTHON: if i.refcount and not i.id in done_vertices :
      if ((i = graphp->Vertices[ii]) && 
          (i->RefCount && !CLListIntFind(&doneVerticesLstMap, i->Id))) {   //CJKING: (i->RefCount && !ListIntFind(i->Id, done_vertices, done_vertices_entries, verbose))) {
                                                                           //PYTHON: dii = get_distance(distances, i.id, i.id)
         if (FERROR == CLDijkstraGetDistance(drp->distances, drp->nRows, drp->nCols, i->Id, i->Id, &dii)) {
            fprintf(stderr, "Error, unable to find cycles: distances not specified\n"); 
            goto done;
         }
         
         //PYTHON: if dii != infinity :
         if (dii != DIJKSTRA_INFINITY) {
            int indent = cp->indent;  //4;
            uint32 step; 
            clVertixData_t *j; 
            
            if (dii || verbose >= 4) {
               (void)linkSummaryCallback(i->Id, ib_connection_source_to_str(fabricp, i->Connection), dii, 1, indent, context); 
            }
            
            j = i;      //PYTHON: j = i
            cycles++;   //PYTHON: cycles += 1
            
            //PYTHON: if dii in cycle_histogram :
            //CJKING: if (CLListUint32Find(dii, cycle_histogram, drp->nCols, verbose))
            if (CLListUint32Find(&cycleHistogramLstMap, dii)) 
               cycle_histogram[dii]++;     //PYTHON: cycle_histogram[dii] += 1
            else {
               //CJKING: Add entry to search list for cycle_histogram array 
               if (CLListUint32Add(&cycleHistogramLstMap, dii)) 
                  goto done; 
               else 
                  cycle_histogram[dii] = 1;   //PYTHON: cycle_histogram[dii] = 1
            }
            cycle_histogram_entries = MAX(cycle_histogram_entries, dii); 
            
            //PYTHON: if routes :
            if (drp->routes) {
               //PYTHON: for step in range(0, dii) :
               for (step = 0; step < dii; step++) {
                  if (verbose >= 2)
					  (void)linkStepSummaryCallback(j->Id, ib_connection_source_to_str(fabricp, j->Connection), step, 1, indent + 4, context);
                  if (verbose >= 3)
                      (void)pathSummaryCallback(fabricp, j->Connection, indent + 8, context);                   
                  // insert XML token to indicate the end of link step summary section
                  if (xmlFmt) 
                     (void)linkStepSummaryCallback(0, 0, 0, 0, indent + 4, context); 
                  
                  done_vertices[done_vertices_entries++] = j->Id; //PYTHON: done_vertices.append(j.id)                        
                                                                  //CJKING: Add entry to search list for done_vertices array 
                  if (CLListIntAdd(&doneVerticesLstMap, j->Id)) 
                     goto done; 
                  next_id = drp->routes[j->Id][i->Id];            //PYTHON: next_id = routes[j.id][i.id]                        
                  j = graphp->Vertices[next_id];                  //PYTHON: j = graph.vertices[next_id]
               }
            } else {
               uint8 found = 0; 
               
               //PYTHON: for step in range(0, dii) :
               for (step = 0; step < dii; step++) {
                  uint32 jj; 
                  
                  if (verbose >= 2)
					  (void)linkStepSummaryCallback(j->Id, ib_connection_source_to_str(fabricp, j->Connection), step, 1, indent + 4, context); 
                  if (verbose >= 3)
                      (void)pathSummaryCallback(fabricp, j->Connection, indent + 8, context); 
                  // insert XML token to indicate the end of link step summary section
                  if (xmlFmt) 
                     (void)linkStepSummaryCallback(0, 0, 0, 0, indent + 4/*indent*/, context); 
                  
                  done_vertices[done_vertices_entries++] = j->Id;     //PYTHON: done_vertices.append(j.id)
                                                                      //CJKING: Add entry to search list for done_vertices array 
                  if (CLListIntAdd(&doneVerticesLstMap, j->Id)) 
                     goto done; 
                  found = 0;                        
                  memset(distance_list, 0, sizeof(drp->nCols * sizeof(uint32)));    //PYTHON: distance_list = []
                  
                  //PYTHON: for arc_id in j.outbound :
                  for (jj = 0; jj < j->OutboundCount; jj++) {
                     clVertixData_t *k; 
                     clArcData_t *arcp; 
                     
                     if (!(j->Outbound[jj] >= 0 && (arcp = CLGraphFindIdArc(graphp, j->Outbound[jj])))) {
                        fprintf(stderr, "Warning, arc not available cannot follow connection for vertex %d\n", jj); 
                        continue;
                     }
                     
                     //PYTHON: k = graph.vertices[graph.arcs[arc_id].sink]
                     k = graphp->Vertices[arcp->Sink]; 
                     //PYTHON: if k.refcount :
                     if (k->RefCount) {
                        uint32 dki; 
                        
                        //PYTHON: dki = get_distance(distances, k.id, i.id)
                        if (FERROR == CLDijkstraGetDistance(drp->distances, drp->nRows, drp->nCols, k->Id, i->Id, &dki)) {
                           fprintf(stderr, "Error, unable to find cycles: distances not specified\n"); 
                           goto done;
                        }
                        /*
                          #print '  %d: vertex %d via arc %d to vertex %d' % \
                          #      (step, j.id, arc_id, k.id)
                          found = 1
                          j = k
                        */
                        //PYTHON: if dki != infinity :
                        if (dki != DIJKSTRA_INFINITY) 
                           distance_list[dki] = dki;   //PYTHON: distance_list.append(dki)
                        
                        if (dki != DIJKSTRA_INFINITY &&                 //PYTHON: if dki != infinity and
                            ((dki == dii - step - 1) ||                 //PYTHON:    ((dki == dii - step - 1) or
                             (k->Id == i->Id && step == dii - 1))) {    //PYTHON:     (k.id == i.id and step == dii - 1))
                                                                        //printf("  %d: vertex %d via arc %d to vertex %d\N", step, j->Id, arc_id, k->Id);
                           found = 1;  //PYTHON: found = 1
                           j = k;      //PYTHON: j = k
                        }
                        break;
                     }
                  }
                  //PYTHON: if not found :
                  if (!found) {
                     char *s = ib_connection_source_to_str(fabricp, i->Connection);
                     printf("  %d: could not find cycle for vertex %d (%s) on step %d of %d (warning), looking for distance %d\n", 
                            step, i->Id, (s)?(s):"<null>", step, dii, dii - step - 1); 
                     break;
                  }
               }
            }
            
            // insert XML token to indicate the end of link summary section
            if (xmlFmt) 
               (void)linkSummaryCallback(0, 0, 0, 0, cp->indent/*0*/, context);
         }
      }
   }
   
   if (verbose >= 4) {
      if (!cycles) 
         printf("Routes are deadlock free!\n"); 
      else {
         uint32 c; 
         
         printf("Deadlock - dependency graph routes contain %d cycle(s):\n", cycles); 
         //PYTHON: for c in cycle_histogram :
         for (c = 0; c < cycle_histogram_entries; c++) printf("  %d cycle(s) of circumference %d\n", cycle_histogram[c], c);
      }
   }
   
done:
   
   if (done_vertices) 
      MemoryDeallocate(done_vertices); 
   if (cycle_histogram) 
      MemoryDeallocate(cycle_histogram); 
   if (distance_list) 
      MemoryDeallocate(distance_list);
}

#endif
