/******************************************************************************* ** *A digraphs.c GAP package Digraphs Julius Jonusas ** James Mitchell ** Wilf A. Wilson ** Michael Young ** ** Copyright (C) 2014-17 - Julius Jonusas, James Mitchell, Wilf A. Wilson, ** Michael Young ** ** This file is free software, see the digraphs/LICENSE. **
*******************************************************************************/
#include"digraphs.h"
#include <stdbool.h> // for false, true, bool #include <stdint.h> // for uint64_t #include <stdlib.h> // for NULL, free #include <string.h> // for memcpy
#include"bliss-includes.h"// for bliss stuff #include"cliques.h"// for FuncDIGRAPHS_FREE_CLIQUES_DATA #include"digraphs-config.h"// for DIGRAPHS_WITH_INCLUDED_BLISS #include"digraphs-debug.h"// for DIGRAPHS_ASSERT #include"homos.h"// for FuncHomomorphismDigraphsFinder #include"planar.h"// for FUNC_IS_PLANAR, . . . #include"safemalloc.h"// for safe_malloc
Int DigraphNrVertices(Obj D) { return LEN_LIST(FuncOutNeighbours(0L, D));
}
staticInt RNamOutNeighbours = 0;
Obj FuncOutNeighbours(Obj self, Obj D) { if (!RNamOutNeighbours) {
RNamOutNeighbours = RNamName("OutNeighbours");
} if (CALL_1ARGS(IsDigraph, D) != True) {
ErrorQuit("expected a digraph, not a %s", (Int) TNAM_OBJ(D), 0L);
} elseif (IsbPRec(D, RNamOutNeighbours)) { return ElmPRec(D, RNamOutNeighbours);
} else {
ErrorQuit( "the `OutNeighbours` component is not set for this digraph,", 0L, 0L);
}
}
static Obj FuncDIGRAPH_OUT_NEIGHBOURS_FROM_SOURCE_RANGE(Obj self,
Obj N,
Obj src,
Obj ran) {
DIGRAPHS_ASSERT(LEN_LIST(src) == LEN_LIST(ran)); Int n = INT_INTOBJ(N); if (n == 0) { return NEW_PLIST(T_PLIST_EMPTY, 0);
}
Obj ret = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(ret, n);
for (Int i = 1; i <= n; i++) {
Obj next = NEW_PLIST(T_PLIST_EMPTY, 0);
SET_LEN_PLIST(next, 0);
SET_ELM_PLIST(ret, i, next);
CHANGED_BAG(ret);
}
for (Int i = 1; i <= LEN_LIST(src); i++) {
Obj list = ELM_PLIST(ret, INT_INTOBJ(ELM_LIST(src, i)));
ASS_LIST(list, LEN_PLIST(list) + 1, ELM_LIST(ran, i));
CHANGED_BAG(ret);
} return ret;
}
Int DigraphNrEdges(Obj D) { Int nr = 0; if (IsbPRec(D, RNamName("DigraphNrEdges"))) { return INT_INTOBJ(ElmPRec(D, RNamName("DigraphNrEdges")));
} elseif (IsbPRec(D, RNamName("DigraphSource"))) {
nr = LEN_LIST(ElmPRec(D, RNamName("DigraphSource")));
} else { Int n = DigraphNrVertices(D);
Obj list = FuncOutNeighbours(0L, D); for (Int i = 1; i <= n; i++) {
nr += LEN_LIST(ELM_PLIST(list, i));
}
} if (IsAttributeStoringRep(D)) {
AssPRec(D, RNamName("DigraphNrEdges"), INTOBJ_INT(nr));
} return nr;
}
Int DigraphNrAdjacencies(Obj D) { Int nr = 0; if (IsbPRec(D, RNamName("DigraphNrAdjacencies"))) { return INT_INTOBJ(ElmPRec(D, RNamName("DigraphNrAdjacencies")));
} else {
Obj const out = FuncOutNeighbours(0L, D); for (Int v = 1; v <= LEN_LIST(out); ++v) {
Obj const out_v = ELM_LIST(out, v); for (Int w = 1; w <= LEN_LIST(out_v); ++w) { Int u = INT_INTOBJ(ELM_LIST(out_v, w)); if (v <= u
|| CALL_3ARGS(IsDigraphEdge, D, INTOBJ_INT(u), INTOBJ_INT(v))
== False) {
++nr;
}
}
}
} if (IsAttributeStoringRep(D)) {
AssPRec(D, RNamName("DigraphNrAdjacencies"), INTOBJ_INT(nr));
} return nr;
}
Int DigraphNrAdjacenciesWithoutLoops(Obj D) { Int nr = 0; if (IsbPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops"))) { return INT_INTOBJ(ElmPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops")));
} else {
Obj const out = FuncOutNeighbours(0L, D); for (Int v = 1; v <= LEN_LIST(out); ++v) {
Obj const out_v = ELM_LIST(out, v); for (Int w = 1; w <= LEN_LIST(out_v); ++w) { Int u = INT_INTOBJ(ELM_LIST(out_v, w)); if (v < u
|| CALL_3ARGS(IsDigraphEdge, D, INTOBJ_INT(u), INTOBJ_INT(v))
== False) {
++nr;
}
}
}
} if (IsAttributeStoringRep(D)) {
AssPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops"), INTOBJ_INT(nr));
} return nr;
}
/**************************************************************************** ** *F FuncGABOW_SCC ** ** `digraph' should be a list whose entries and the lists of out-neighbours ** of the vertices. So [[2,3],[1],[2]] represents the graph whose edges are ** 1->2, 1->3, 2->1 and 3->2. ** ** returns a newly constructed record with two components 'comps' and 'id' the ** elements of 'comps' are lists representing the strongly connected components ** of the directed graph, and in the component 'id' the following holds: ** id[i]=PositionProperty(comps, x-> i in x); ** i.e. 'id[i]' is the index in 'comps' of the component containing 'i'. ** Neither the components, nor their elements are in any particular order. ** ** The algorithm is that of Gabow, based on the implementation in Sedgwick: ** https://algs4.cs.princeton.edu/42directed/GabowSCC.java.html ** (made non-recursive to avoid problems with stack limits) and ** the implementation of STRONGLY_CONNECTED_COMPONENTS_DIGRAPH in listfunc.c.
*/
PLAIN_LIST(digraph);
n = LEN_PLIST(digraph); if (n == 0) {
out = NEW_PREC(2);
AssPRec(out, RNamName("id"), NEW_PLIST_IMM(T_PLIST_EMPTY, 0));
AssPRec(out, RNamName("comps"), NEW_PLIST_IMM(T_PLIST_EMPTY, 0)); return out;
}
end1 = 0;
stack1 = NEW_PLIST(T_PLIST_CYC, n); // stack1 is a plist so we can use memcopy below
SET_LEN_PLIST(stack1, n);
id = NEW_PLIST_IMM(T_PLIST_CYC, n);
SET_LEN_PLIST(id, n);
// init id for (v = 1; v <= n; v++) {
SET_ELM_PLIST(id, v, INTOBJ_INT(0));
}
count = n;
comps = NEW_PLIST_IMM(T_PLIST_TAB, n);
stack2 = safe_malloc((4 * n + 2) * sizeof(UInt));
frames = stack2 + n + 1;
end2 = 0;
for (v = 1; v <= n; v++) { if (INT_INTOBJ(ELM_PLIST(id, v)) == 0) {
adj = ELM_PLIST(digraph, v);
PLAIN_LIST(adj);
level = 1;
frames[0] = v; // vertex
frames[1] = 1; // index
frames[2] = (UInt) adj;
SET_ELM_PLIST(stack1, ++end1, INTOBJ_INT(v));
stack2[++end2] = end1;
SET_ELM_PLIST(id, v, INTOBJ_INT(end1));
while (1) { if (frames[1] > (UInt) LEN_PLIST((Obj) frames[2])) { if (stack2[end2] == (UInt) INT_INTOBJ(ELM_PLIST(id, frames[0]))) {
end2--;
count++;
nr = 0; do {
nr++;
w = INT_INTOBJ(ELM_PLIST(stack1, end1--));
SET_ELM_PLIST(id, w, INTOBJ_INT(count));
} while (w != frames[0]);
static UInt UF_FIND(UInt* id, UInt i) { while (i != id[i])
i = id[i]; return i;
}
staticvoid UF_COMBINE_CLASSES(UInt* id, UInt i, UInt j) {
i = UF_FIND(id, i);
j = UF_FIND(id, j); if (i < j)
id[j] = i; elseif (j < i)
id[i] = j; // if i = j then there is nothing to combine
}
static Obj FuncDIGRAPH_CONNECTED_COMPONENTS(Obj self, Obj digraph) {
UInt n, *id, *nid, i, e, len, f, nrcomps;
Obj adj, adji, gid, gcomps, comp, out;
out = NEW_PREC(2);
n = DigraphNrVertices(digraph); if (n == 0) {
gid = NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
gcomps = NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
} else {
id = safe_malloc(n * sizeof(UInt)); for (i = 0; i < n; i++) {
id[i] = i;
}
adj = FuncOutNeighbours(self, digraph); for (i = 0; i < n; i++) {
adji = ELM_PLIST(adj, i + 1);
PLAIN_LIST(adji);
len = LEN_PLIST(adji); for (e = 1; e <= len; e++) {
UF_COMBINE_CLASSES(id, i, INT_INTOBJ(ELM_PLIST(adji, e)) - 1);
}
}
// "Normalise" id, giving it sensible labels
nid = safe_malloc(n * sizeof(UInt));
nrcomps = 0; for (i = 0; i < n; i++) {
f = UF_FIND(id, i);
nid[i] = (f == i) ? ++nrcomps : nid[f];
}
free(id);
// Make GAP object from nid
gid = NEW_PLIST_IMM(T_PLIST_CYC, n);
gcomps = NEW_PLIST_IMM(T_PLIST_CYC, nrcomps);
SET_LEN_PLIST(gid, n);
SET_LEN_PLIST(gcomps, nrcomps); for (i = 1; i <= nrcomps; i++) {
SET_ELM_PLIST(gcomps, i, NEW_PLIST_IMM(T_PLIST_CYC, 0));
CHANGED_BAG(gcomps);
} for (i = 1; i <= n; i++) {
SET_ELM_PLIST(gid, i, INTOBJ_INT(nid[i - 1]));
comp = ELM_PLIST(gcomps, nid[i - 1]);
len = LEN_PLIST(comp);
AssPlist(comp, len + 1, INTOBJ_INT(i));
}
free(nid);
}
AssPRec(out, RNamName("id"), gid);
AssPRec(out, RNamName("comps"), gcomps); return out;
}
level = 1;
stack[0] = i;
stack[1] = 1;
prev = 0; while (1) {
j = stack[0];
k = stack[1]; if (ptr[j] == 2) { // we have identified a cycle
stack -= (2 * level) - 2;
free(stack);
free(ptr);
free(depth); return INTOBJ_INT(-2); // We have just travelled around a cycle
}
if (prev > depth[j]) {
depth[j] = prev;
} // Check whether: // 1. We've previously finished with this vertex, OR // 2. Whether we've now investigated all descendant branches
nbs = ELM_PLIST(adj, j); if (ptr[j] == 1 || k > (UInt) LEN_LIST(nbs)) {
ptr[j] = 1;
level--;
prev = depth[j]; if (level == 0) { // finished the search break;
} // Backtrack and choose next available branch
stack -= 2;
ptr[stack[0]] = 0;
prev++;
stack[1]++;
} else { // Otherwise move onto the next available branch
ptr[j] = 2;
level++;
nbs = ELM_PLIST(adj, j);
stack += 2;
stack[0] = INT_INTOBJ(CONST_ADDR_OBJ(nbs)[k]);
stack[1] = 1;
prev = 0;
}
}
x = depth[INT_INTOBJ(start)];
free(ptr);
free(depth);
free(stack); return INTOBJ_INT(x);
}
// Takes in-neighbours (Obj adj) of a topologically sorted non-multi digraph // Returns the out-neighbours of its transitive reduction. // If (Obj loops) == False, loops are removed (transitive reflexive reduction) static Obj FuncDIGRAPH_TRANS_REDUCTION(Obj self, Obj D) {
DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, D) == True); if (!IS_MUTABLE_OBJ(D)) {
ErrorQuit("the argument (a digraph) must be mutable", 0L, 0L);
}
UInt const n = DigraphNrVertices(D);
// Special case for n = 0 if (n == 0) { return D;
}
// Create the GAP out-neighbours structure of the result
Obj ot_list = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(ot_list, n); for (UInt i = 1; i <= n; i++) {
SET_ELM_PLIST(ot_list, i, NEW_PLIST(T_PLIST_EMPTY, 0));
CHANGED_BAG(ot_list);
}
// Create data structures needed for computation
UInt* ptr = safe_calloc(n + 1, sizeof(UInt)); bool* mat = safe_calloc(n * n, sizeof(bool));
UInt* stack = safe_malloc((2 * n + 2) * sizeof(UInt));
// Start a depth-first search from each source of the digraph for (UInt i = 1; i <= n; i++) { if (ptr[i] == 0) { // Remember which vertex was the source
UInt source = i; // not sure if this next line is necessary bool backtracking = false;
UInt level = 1;
stack[0] = i;
stack[1] = 1; while (1) {
UInt j = stack[0];
UInt k = stack[1];
// We have found a loop on vertex j if (ptr[j] == 2) { if (stack[-2] != j) {
ErrorQuit( "the argument (a digraph) must be acyclic except for loops,",
0L,
0L);
}
backtracking = true;
level--;
stack -= 2;
stack[1]++;
ptr[j] = 0; // Store the loop
Obj list = ELM_PLIST(ot_list, j);
ASS_LIST(list, LEN_PLIST(list) + 1, INTOBJ_INT(j));
CHANGED_BAG(ot_list); continue;
}
// Calculate if we need to add an edge from j -> (previous vertex) if (!backtracking && j != source && !mat[(stack[-2] - 1) * n + j - 1]) {
Obj list = ELM_PLIST(ot_list, j);
ASS_LIST(list, LEN_PLIST(list) + 1, INTOBJ_INT(stack[-2]));
CHANGED_BAG(ot_list);
}
Obj nbs = ELM_PLIST(in_list, j);
// Do we need to backtrack? if (ptr[j] == 1 || k > (UInt) LEN_LIST(nbs)) { if (level == 1) break;
for (i = 1; i <= nr; i++) {
nbs = ELM_PLIST(adj, i); if (LEN_LIST(nbs) == 0) { if (ptr[i] == 0) {
count++;
SET_ELM_PLIST(out, count, INTOBJ_INT(i));
}
ptr[i] = 1;
} elseif (ptr[i] == 0) {
level = 1;
stack[0] = i;
stack[1] = 1; while (1) {
j = stack[0];
k = stack[1]; if (ptr[j] == 2) { // SetIsAcyclicDigraph(graph, false);
stack -= 2;
level--; if (stack[0] != j) { // Cycle is not just a loop
free(ptr);
stack -= (2 * level) - 2;
free(stack); return Fail;
}
stack[1]++;
ptr[j] = 0; continue;
}
nbs = ELM_PLIST(adj, j); if (ptr[j] == 1 || k > (UInt) LEN_LIST(nbs)) { if (ptr[j] == 0) { // ADD J TO THE END OF OUR LIST
count++;
SET_ELM_PLIST(out, count, INTOBJ_INT(j));
}
ptr[j] = 1;
level--; if (level == 0) { break;
} // Backtrack and choose next available branch
stack -= 2;
ptr[stack[0]] = 0;
stack[1]++;
} else { // Otherwise move onto the next available branch
ptr[j] = 2;
level++;
nbs = ELM_PLIST(adj, j);
stack += 2;
stack[0] = INT_INTOBJ(ELM_LIST(nbs, k));
stack[1] = 1;
}
}
}
}
free(ptr);
free(stack); return out;
}
// WW. This function performs a depth first search on the digraph defined by // <adj> and returns the adjacency list <out> of a spanning forest. This is a // forest rather than a tree since <adj> is not required to be connected. Each // time a new vertex <j> is discovered (from the previous vertex <i>), the edges // i <-> j are added to <out>.
// TODO(*) use generic DFS, when we have one.
// Assumes that <adj> is the list of adjacencies of a symmetric digraph // Multiple edges and loops are allowed static Obj FuncDIGRAPH_SYMMETRIC_SPANNING_FOREST(Obj self, Obj adj) {
UInt nr, i, j, k, next, len, level;
Obj nbs, out, out_j, new;
UInt *stack, *ptr;
nr = LEN_PLIST(adj);
if (nr == 0) { return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}
// init the adjacencies of the spanning forest
out = NEW_PLIST(T_PLIST_TAB, nr);
SET_LEN_PLIST(out, nr); for (i = 1; i <= nr; i++) {
SET_ELM_PLIST(out, i, NEW_PLIST(T_PLIST_EMPTY, 0));
CHANGED_BAG(out);
}
// init the buffer
ptr = safe_calloc(nr + 1, sizeof(UInt));
stack = safe_malloc((2 * nr + 2) * sizeof(UInt));
for (i = 1; i <= nr; i++) { // perform DFS only on still-undiscovered non-trivial connected components if (!ptr[i] && LEN_LIST(ELM_PLIST(adj, i)) > 0) {
level = 1;
stack[0] = i;
stack[1] = 1;
while (1) {
j = stack[0];
k = stack[1];
nbs = ELM_PLIST(adj, j);
// idea: is <nbs[k]> a new vertex? add edges <j> <-> <nbs[k]> if so
// if we're finished with <j>, or <nbs[k]> doesn't exist, then backtrack if (ptr[j] || k > (UInt) LEN_LIST(nbs)) {
ptr[j] = 1; // vertex <j> and its descendents are now fully explored if (--level == 0) { break; // we've explored the whole connected component
}
stack -= 2;
ptr[stack[0]] = 0;
stack[1]++;
} else {
ptr[j] = 1;
next = INT_INTOBJ(CONST_ADDR_OBJ(nbs)[k]);
level++;
stack += 2;
stack[0] = next;
stack[1] = 1; // if <next> is a brand new vertex, add it to the spanning tree if (ptr[next] == 0) { // create the edge <j> -> <next>
out_j = ELM_PLIST(out, j);
len = LEN_PLIST(out_j);
ASS_LIST(out_j, len + 1, INTOBJ_INT(next)); // create the edge <next> -> <j> // since <next> is new, <out[next]> is still a T_PLIST_EMPTY new = ELM_PLIST(out, next);
ASS_LIST(new, 1, INTOBJ_INT(j));
}
}
}
}
}
free(ptr);
free(stack); return out;
}
static Obj FuncDIGRAPH_SOURCE_RANGE(Obj self, Obj D) {
Obj src, ran, adj, adji; Int i, j, k, m, n, len;
m = DigraphNrEdges(D);
n = DigraphNrVertices(D);
adj = FuncOutNeighbours(self, D);
if (m == 0) {
src = NEW_PLIST_IMM(T_PLIST_EMPTY, m);
ran = NEW_PLIST_IMM(T_PLIST_EMPTY, m);
} else {
src = NEW_PLIST_IMM(T_PLIST_CYC, m);
ran = NEW_PLIST_IMM(T_PLIST_CYC, m);
k = 0; for (i = 1; i <= n; i++) {
adji = ELM_PLIST(adj, i);
len = LEN_LIST(adji); for (j = 1; j <= len; j++) {
k++;
SET_ELM_PLIST(src, k, INTOBJ_INT(i));
SET_ELM_PLIST(ran, k, ELM_LIST(adji, j));
}
}
}
// Assume we are passed a GAP Int nrvertices // Two GAP lists of PosInts (all <= nrvertices) of equal length
// Function to change Out-Neighbours to In-Neighbours, and vice versa static Obj FuncDIGRAPH_IN_OUT_NBS(Obj self, Obj adj) {
Obj inn, innk, adji;
UInt n, i, j, k, len, len2;
n = LEN_PLIST(adj); if (n == 0) { return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}
inn = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(inn, n);
// fill adj with empty plists for (i = 1; i <= n; i++) {
SET_ELM_PLIST(inn, i, NEW_PLIST(T_PLIST_EMPTY, 0));
CHANGED_BAG(inn);
}
for (i = 1; i <= n; i++) {
adji = ELM_PLIST(adj, i);
PLAIN_LIST(adji);
len = LEN_PLIST(adji); for (j = 1; j <= len; j++) {
k = INT_INTOBJ(ELM_PLIST(adji, j));
innk = ELM_PLIST(inn, k);
len2 = LEN_PLIST(innk);
ASS_LIST(innk, len2 + 1, INTOBJ_INT(i));
}
} return inn;
}
Obj FuncADJACENCY_MATRIX(Obj self, Obj digraph) { Int n, i, j, val, len, outj;
Obj adj, mat, adji, next;
n = DigraphNrVertices(digraph); if (n == 0) { return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}
adj = FuncOutNeighbours(self, digraph);
mat = NEW_PLIST_IMM(T_PLIST_TAB, n);
SET_LEN_PLIST(mat, n);
for (i = 1; i <= n; i++) { // Set up the i^th row of the adjacency matrix
next = NEW_PLIST_IMM(T_PLIST_CYC, n);
SET_LEN_PLIST(next, n); for (j = 1; j <= n; j++) {
SET_ELM_PLIST(next, j, INTOBJ_INT(0));
} // Fill in the correct values of the matrix
adji = ELM_PLIST(adj, i);
len = LEN_LIST(adji); for (j = 1; j <= len; j++) {
outj = INT_INTOBJ(ELM_LIST(adji, j));
val = INT_INTOBJ(ELM_PLIST(next, outj)) + 1;
SET_ELM_PLIST(next, outj, INTOBJ_INT(val));
}
SET_ELM_PLIST(mat, i, next);
CHANGED_BAG(mat);
}
SET_FILT_LIST(mat, FN_IS_RECT); return mat;
}
static Obj FuncIS_MULTI_DIGRAPH(Obj self, Obj digraph) {
Obj adj, adji;
UInt n, i, k, j, *seen;
adj = FuncOutNeighbours(self, digraph);
n = DigraphNrVertices(digraph);
seen = safe_calloc(n + 1, sizeof(UInt));
for (i = 1; i <= n; i++) {
adji = ELM_PLIST(adj, i); if ((UInt) LEN_LIST(adji) > n) {
free(seen); returnTrue;
}
PLAIN_LIST(adji); for (j = 1; j <= (UInt) LEN_PLIST(adji); j++) {
k = INT_INTOBJ(ELM_PLIST(adji, j)); if (seen[k] != i) {
seen[k] = i;
} else {
free(seen); returnTrue;
}
}
}
free(seen); returnFalse;
}
/***************** GENERAL FLOYD_WARSHALL ALGORITHM*********************** * This function accepts 5 arguments: * 1. A digraph. * 2. A special function which takes 5 arguments: * - The matrix dist * - 3 integers i, j, k * - An integer n (the number of vertices of digraph) * and modifies the matrix dist according to the values of i, j, k. * 3. Int val1 s.t initially dist[i][j] = val1 if [ i, j ] isn't an edge. * 4. Int val2 s.t initially dist[i][j] = val2 if [ i, j ] is an edge. * 5. bool copy: * - If true, FLOYD_WARSHALL stores the initialised dist, and * compares it with dist after it has gone through the 3 for-loops, * and returns true iff it is unchanged. * - If false, proceeds as usual Floyd-Warshall algorithm and returns * a GAP object matrix as the result. * 6. bool diameter: // TODO wouldn't it be better to just take a * "post-processing" function. JDM * - If true, FLOYD_WARSHALL goes through dist after the 3 for-loops, * returns -1 if dist contains the value -1, else it returns the * maximum value of dist * - If false, proceeds as usual * 7. bool shortest: * - If true, for each vertex i, dist[i][i] is initially set to 0 * - If false, this step is skipped
*/ static Obj FLOYD_WARSHALL(Obj digraph, void (*func)(Int** dist, Int i, Int j, Int k, Int n), Int val1, Int val2, bool copy, bool diameter, bool shortest) { Int n, i, j, k, *dist; Int* adj = NULL;
Obj next, out, outi, val;
n = DigraphNrVertices(digraph);
// Special case for 0-vertex graph if (n == 0) { if (diameter) { return Fail;
} if (copy) { returnTrue;
} return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}
// Initialise the n x n matrix with val1 and val2
dist = safe_malloc(n * n * sizeof(Int)); for (i = 0; i < n * n; i++) {
dist[i] = val1;
}
out = FuncOutNeighbours(0L, digraph); for (i = 1; i <= n; i++) {
outi = ELM_PLIST(out, i);
PLAIN_LIST(outi); for (j = 1; j <= LEN_PLIST(outi); j++) {
k = (i - 1) * n + INT_INTOBJ(ELM_PLIST(outi, j)) - 1;
dist[k] = val2;
}
}
if (shortest) { // This is the special case for DIGRAPH_SHORTEST_DIST for (i = 0; i < n; i++) {
dist[i * n + i] = 0;
}
}
if (copy) { // This is the special case for IS_TRANSITIVE_DIGRAPH
adj = safe_malloc(n * n * sizeof(Int)); for (i = 0; i < n * n; i++) {
adj[i] = dist[i];
}
}
for (k = 0; k < n; k++) { for (i = 0; i < n; i++) { for (j = 0; j < n; j++) {
func(&dist, i, j, k, n);
}
}
}
// the following is a horrible hack if (diameter) { // This is the special case for DIGRAPH_DIAMETER Int maximum = -1; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (dist[i * n + j] > maximum) {
maximum = dist[i * n + j];
} elseif (dist[i * n + j] == -1) {
free(dist);
free(adj); return Fail;
}
}
}
free(dist); return INTOBJ_INT(maximum);
}
if (copy) { // This is the special case for IS_TRANSITIVE_DIGRAPH for (i = 0; i < n * n; i++) { if (adj[i] != dist[i]) {
free(dist);
free(adj); returnFalse;
}
}
free(dist);
free(adj); returnTrue;
}
// Create GAP matrix to return
out = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(out, n);
for (i = 1; i <= n; i++) {
next = NEW_PLIST(T_PLIST_CYC, n);
SET_LEN_PLIST(next, n); for (j = 1; j <= n; j++) {
val = INTOBJ_INT(dist[(i - 1) * n + (j - 1)]); if (val == INTOBJ_INT(-1)) {
val = Fail;
}
SET_ELM_PLIST(next, j, val);
}
SET_ELM_PLIST(out, i, next);
CHANGED_BAG(out);
}
SET_FILT_LIST(out, FN_IS_RECT);
free(dist); return out;
}
staticvoid FW_FUNC_SHORTEST_DIST(Int** dist, Int i, Int j, Int k, Int n) { if ((*dist)[i * n + k] != -1 && (*dist)[k * n + j] != -1) { if ((*dist)[i * n + j] == -1
|| (*dist)[i * n + j] > (*dist)[i * n + k] + (*dist)[k * n + j]) {
(*dist)[i * n + j] = (*dist)[i * n + k] + (*dist)[k * n + j];
}
}
}
staticvoid FW_FUNC_TRANS_CLOSURE(Int** dist, Int i, Int j, Int k, Int n) { if ((*dist)[i * n + k] != 0 && (*dist)[k * n + j] != 0) {
(*dist)[i * n + j] = 1;
}
}
staticvoid
FW_FUNC_REFLEX_TRANS_CLOSURE(Int** dist, Int i, Int j, Int k, Int n) { if ((i == j) || ((*dist)[i * n + k] != 0 && (*dist)[k * n + j] != 0)) {
(*dist)[i * n + j] = 1;
}
}
//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code.
/* Minor modifications to get it to compile in C rather than C++ and
integrate with GAP SL*/
/* Digraphs takes parts of this source from GAP. */
#define BIG_CONSTANT(x) (x##LLU)
staticinline UInt fmix(UInt h) { #ifndef SYS_IS_64_BIT
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16; #else
h ^= h >> 33;
h *= BIG_CONSTANT(0xff51afd7ed558ccd);
h ^= h >> 33;
h *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
h ^= h >> 33; #endif
return h;
}
static Obj FuncDIGRAPH_HASH(Obj self, Obj digraph) {
UInt n, i, h;
Obj out, a; Int nr, j;
h = 0;
n = DigraphNrVertices(digraph);
out = FuncOutNeighbours(self, digraph);
for (i = 1; i <= n; i++) {
a = ELM_PLIST(out, i);
PLAIN_LIST(a);
nr = LEN_PLIST(a); for (j = 1; j <= nr; j++)
h += fmix(INT_INTOBJ(ELM_PLIST(a, j)));
h = fmix(h + 0x9e3779b9);
}
return INTOBJ_INT(h);
}
static Obj FuncDIGRAPH_EQUALS(Obj self, Obj digraph1, Obj digraph2) {
UInt i, n1, n2, m1, m2;
Obj out1, out2, a, b; Int nr, *buf;
// Check NrVertices is equal
n1 = DigraphNrVertices(digraph1);
n2 = DigraphNrVertices(digraph2); if (n1 != n2) { returnFalse;
}
// Compare OutNeighbours of each vertex in turn for (i = 1; i <= n1; i++) {
a = ELM_PLIST(out1, i);
b = ELM_PLIST(out2, i);
PLAIN_LIST(a);
PLAIN_LIST(b);
nr = LEN_PLIST(a); // Check that the OutDegrees match if (nr != LEN_PLIST(b)) {
free(buf); returnFalse;
}
// Compare Sorted(out1[i]) and Sorted(out2[i]) for each vertex i for (i = 1; i <= n1; i++) {
a = ELM_PLIST(out1, i);
b = ELM_PLIST(out2, i);
PLAIN_LIST(a);
PLAIN_LIST(b);
n = DigraphNrVertices(digraph);
num_vc = 0;
num_ec = 0;
// TODO: make a decision about this // mult = (orientation_double == True) ? 2 : 1;
mult = 2;
if (vert_colours != Fail) {
DIGRAPHS_ASSERT(n == (uint64_t) LEN_LIST(vert_colours)); for (i = 1; i <= n; i++) {
num_vc = MAX(num_vc, (uint64_t) INT_INTOBJ(ELM_LIST(vert_colours, i)));
}
}
adj = FuncOutNeighbours(0L, digraph);
if (edge_colours != Fail) {
DIGRAPHS_ASSERT(n == (uint64_t) LEN_LIST(edge_colours)); for (i = 1; i <= n; i++) { Int len = LEN_LIST(ELM_PLIST(edge_colours, i));
DIGRAPHS_ASSERT(LEN_LIST(ELM_PLIST(adj, i)) == len); for (Int l = 1; l <= len; l++) {
uint64_t x = INT_INTOBJ(ELM_LIST(ELM_LIST(edge_colours, i), l));
num_ec = MAX(num_ec, x);
}
}
} elseif (DigraphNrEdges(digraph) > 0) {
num_ec = 1;
}
graph = bliss_digraphs_new(0);
// TODO: make this safe
uint64_t num_layers = 64 - __builtin_clzll(num_ec);
// Take care of the case where there are no edges in the digraph if (DigraphNrEdges(digraph) == 0) {
num_layers = 1;
mult = 1;
}
if (vert_colours == Fail) {
num_vc = 1;
}
// TODO: is duplicating the best idea here? for (i = 1; i <= mult * num_layers; i += mult) { for (j = 1; j <= n; j++) {
colour = (vert_colours != Fail)
? (i - 1) * num_vc + INT_INTOBJ(ELM_LIST(vert_colours, j))
: i - 1;
bliss_digraphs_add_vertex(graph, colour);
} if (mult == 2) { for (j = 1; j <= n; j++) {
colour = (vert_colours != Fail)
? i * num_vc + INT_INTOBJ(ELM_LIST(vert_colours, j))
: i;
bliss_digraphs_add_vertex(graph, colour);
}
}
}
if (mult == 2) { for (i = 0; i < n; i++) {
j = bliss_digraphs_add_vertex(graph, num_vc * num_layers * mult + 2);
bliss_digraphs_add_edge(graph, j, i);
bliss_digraphs_add_edge(graph, j, i + n); for (k = 0; k < num_layers; k++) {
bliss_digraphs_add_edge(graph, j, i + k * mult * n);
bliss_digraphs_add_edge(graph, j, i + (k * mult + 1) * n);
}
}
}
for (i = 1; i < num_layers; i++) { for (j = 1; j <= mult * n; j++) {
bliss_digraphs_add_edge(
graph, (i - 1) * mult * n + (j - 1), i * mult * n + (j - 1));
}
}
for (j = 1; j <= n; j++) {
adjj = ELM_PLIST(adj, j);
nr = LEN_PLIST(adjj); for (k = 1; k <= nr; k++) {
uint64_t w = INT_INTOBJ(ELM_PLIST(adjj, k)); for (i = 0; i < num_layers; i++) {
colour = edge_colours != Fail
? INT_INTOBJ(ELM_LIST(ELM_LIST(edge_colours, j), k))
: 1; if ((1 << i) & colour) {
bliss_digraphs_add_edge(graph,
i * mult * n + (j - 1),
((i + 1) * mult - 1) * n + (w - 1));
}
}
}
} return graph;
}
static BlissGraph* buildBlissMultiDigraphWithColours(Obj digraph, Obj colours) {
UInt n, i, j, k, l, nr;
Obj adji, adj;
BlissGraph* graph;
for (i = 1; i <= n; i++) {
bliss_digraphs_add_vertex(graph, INT_INTOBJ(ELM_LIST(colours, i)));
} for (i = 1; i <= n; i++) {
bliss_digraphs_add_vertex(graph, n + 1);
} for (i = 1; i <= n; i++) {
bliss_digraphs_add_vertex(graph, n + 2);
}
for (i = 1; i <= n; i++) {
bliss_digraphs_add_edge(graph, i - 1, n + i - 1);
bliss_digraphs_add_edge(graph, i - 1, 2 * n + i - 1);
adji = ELM_PLIST(adj, i);
nr = LEN_PLIST(adji); for (j = 1; j <= nr; j++) {
k = bliss_digraphs_add_vertex(graph, n + 3);
l = bliss_digraphs_add_vertex(graph, n + 4);
bliss_digraphs_add_edge(graph, n + i - 1, k);
bliss_digraphs_add_edge(graph, k, l);
bliss_digraphs_add_edge(
graph, l, 2 * n + INT_INTOBJ(ELM_PLIST(adji, j)) - 1);
}
}
return graph;
}
staticvoid digraph_hook_function(void* user_param, unsignedint N, constunsignedint* aut) {
UInt4* ptr;
Obj p, gens;
UInt i, n;
n = INT_INTOBJ(ELM_PLIST(user_param, 2)); // the degree
DIGRAPHS_ASSERT(n <= N);
p = NEW_PERM4(n);
ptr = ADDR_PERM4(p);
// Take a list of C integers, and multiply them together into a GAP int static Obj MultiplyList(int* vals, int length) {
Obj res = INTOBJ_INT(1); for (int i = 0; i < length; ++i) {
res = ProdInt(res, INTOBJ_INT(vals[i]));
} return res;
}
// user_param = [vertex perms, nr vertices, edge perms, nr edges] staticvoid multidigraph_hook_function(void* user_param, unsignedint N, constunsignedint* aut) {
UInt4* ptr;
Obj p, gens;
UInt i, n, m; bool stab;
m = INT_INTOBJ(ELM_PLIST(user_param, 2)); // the nr of vertices
DIGRAPHS_ASSERT(m <= N);
stab = true; for (i = 0; i < m; i++) { if (aut[i] != i) {
stab = false;
}
} if (stab) { // permutation of the edges
n = INT_INTOBJ(ELM_PLIST(user_param, 4)); // the nr of edges
p = NEW_PERM4(n);
ptr = ADDR_PERM4(p);
DIGRAPHS_ASSERT(2 * (n - 1) + m <= N); for (i = 0; i < n; i++) {
ptr[i] = (aut[2 * i + m] - m) / 2;
}
gens = ELM_PLIST(user_param, 3);
} else { // permutation of the vertices
p = NEW_PERM4(m);
ptr = ADDR_PERM4(p);
DIGRAPHS_ASSERT(m <= N); for (i = 0; i < m; i++) {
ptr[i] = aut[i];
}
gens = ELM_PLIST(user_param, 1);
}
AssPlist(gens, LEN_PLIST(gens) + 1, p);
}
// user_param = [vertex perms, nr vertices, edge perms, nr edges] staticvoid multidigraph_colours_hook_function(void* user_param, unsignedint N, constunsignedint* aut) {
UInt4* ptr;
Obj p, gens;
UInt i, n, m; bool stab;
m = INT_INTOBJ(ELM_PLIST(user_param, 2)); // the nr of vertices
DIGRAPHS_ASSERT(m <= N);
stab = true; for (i = 0; i < m; i++) { if (aut[i] != i) {
stab = false;
}
} if (stab) { // permutation of the edges
n = INT_INTOBJ(ELM_PLIST(user_param, 4)); // the nr of edges
p = NEW_PERM4(n);
ptr = ADDR_PERM4(p);
DIGRAPHS_ASSERT(2 * (n - 1) + 3 * m <= N); for (i = 0; i < n; i++) {
ptr[i] = (aut[2 * i + 3 * m] - 3 * m) / 2;
}
gens = ELM_PLIST(user_param, 3);
} else { // permutation of the vertices
p = NEW_PERM4(m);
ptr = ADDR_PERM4(p);
DIGRAPHS_ASSERT(m < N); for (i = 0; i < m; i++) {
ptr[i] = aut[i];
}
gens = ELM_PLIST(user_param, 1);
}
AssPlist(gens, LEN_PLIST(gens) + 1, p);
}
static Obj FuncMULTIDIGRAPH_AUTOMORPHISMS(Obj self, Obj digraph, Obj colours) {
Obj autos, p, q, out;
BlissGraph* graph;
UInt4* ptr; constunsignedint* canon; Int i, m, n;
if (colours == False) {
graph = buildBlissMultiDigraph(digraph);
} else {
graph = buildBlissMultiDigraphWithColours(digraph, colours);
}
autos = NEW_PLIST(T_PLIST, 4);
SET_ELM_PLIST(autos, 1, NEW_PLIST(T_PLIST, 0)); // perms of the vertices
CHANGED_BAG(autos);
SET_ELM_PLIST(autos, 2, INTOBJ_INT(DigraphNrVertices(digraph)));
SET_ELM_PLIST(autos, 3, NEW_PLIST(T_PLIST, 0)); // perms of the edges
CHANGED_BAG(autos);
SET_ELM_PLIST(autos, 4, INTOBJ_INT(DigraphNrEdges(digraph)));
// Get canonical labeling as GAP perms
m = DigraphNrVertices(digraph);
p = NEW_PERM4(m); // perm of vertices
ptr = ADDR_PERM4(p);
for (i = 0; i < m; i++) {
ptr[i] = canon[i];
}
n = DigraphNrEdges(digraph);
q = NEW_PERM4(n); // perm of edges
ptr = ADDR_PERM4(q);
if (colours == False) { for (i = 0; i < n; i++) {
ptr[i] = canon[2 * i + m] - m;
}
} else { for (i = 0; i < n; i++) {
ptr[i] = canon[2 * i + 3 * m] - 3 * m;
}
}
bliss_digraphs_release(graph);
// put the canonical labeling (as a list of two perms) into autos[2]
out = NEW_PLIST(T_PLIST, 2);
SET_ELM_PLIST(out, 1, p);
SET_ELM_PLIST(out, 2, q);
SET_LEN_PLIST(out, 2);
CHANGED_BAG(out);
SET_ELM_PLIST(autos, 2, out);
CHANGED_BAG(autos);
// remove 4th entry of autos (the number of edges) . . .
SET_LEN_PLIST(autos, 3);
m = DigraphNrVertices(digraph);
p = NEW_PERM4(m); // perm of vertices
ptr = ADDR_PERM4(p);
for (i = 0; i < m; i++) {
ptr[i] = canon[i];
}
n = DigraphNrEdges(digraph);
q = NEW_PERM4(n); // perm of edges
ptr = ADDR_PERM4(q);
if (colours == Fail) { for (i = 0; i < n; i++) {
ptr[i] = canon[2 * i + m] - m;
}
} else { for (i = 0; i < n; i++) {
ptr[i] = canon[2 * i + 3 * m] - 3 * m;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.