/*******************************************************************************
**
*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
#undef PACKAGE
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_URL
#undef PACKAGE_VERSION
// GAP level things, imported in InitKernel below
Obj IsDigraph;
Obj IsMultiDigraph;
Obj IsDigraphEdge;
Obj DIGRAPHS_ValidateVertexColouring;
Obj Infinity;
Obj IsSymmetricDigraph;
Obj GeneratorsOfGroup;
Obj AutomorphismGroup;
Obj IsAttributeStoringRepObj;
Obj IsPermGroup;
Obj IsDigraphAutomorphism;
Obj LargestMovedPointPerms;
Obj SmallestMovedPointPerm;
Obj IsClique;
Obj IsTrivial;
Obj Orbit;
Obj Stabilizer;
Obj IsSubset;
Obj OnTuples;
Obj Group;
Obj ClosureGroup;
Obj InfoWarning;
static inline bool IsAttributeStoringRep(Obj o) {
return (CALL_1ARGS(IsAttributeStoringRepObj, o) ==
True ?
true :
false);
}
/*************************************************************************/
static Obj FuncDIGRAPH_NR_VERTICES(Obj self, Obj D) {
return INTOBJ_INT(DigraphNrVertices(D));
}
Int DigraphNrVertices(Obj D) {
return LEN_LIST(FuncOutNeighbours(0L, D));
}
static Int 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);
}
else if (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")));
}
else if (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;
}
static Obj FuncDIGRAPH_NREDGES(Obj self, Obj D) {
return INTOBJ_INT(DigraphNrEdges(D));
}
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;
}
static Obj FuncDIGRAPH_NRADJACENCIES(Obj self, Obj D) {
return INTOBJ_INT(DigraphNrAdjacencies(D));
}
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;
}
static Obj FuncDIGRAPH_NRADJACENCIESWITHOUTLOOPS(Obj self, Obj D) {
return INTOBJ_INT(DigraphNrAdjacenciesWithoutLoops(D));
}
/****************************************************************************
**
*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.
*/
static Obj FuncGABOW_SCC(Obj self, Obj digraph) {
UInt end1, end2, count, level, w, v, n, nr, idw, *frames, *stack2;
Obj id, stack1, out, comp, comps, adj;
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]);
comp = NEW_PLIST_IMM(T_PLIST_CYC, nr);
SET_LEN_PLIST(comp, nr);
memcpy(ADDR_OBJ(comp) + 1,
CONST_ADDR_OBJ(stack1) + (end1 + 1),
nr *
sizeof(Obj));
nr = LEN_PLIST(comps) + 1;
SET_ELM_PLIST(comps, nr, comp);
SET_LEN_PLIST(comps, nr);
CHANGED_BAG(comps);
}
level--;
if (level == 0) {
break;
}
frames -= 3;
}
else {
w = INT_INTOBJ(ELM_PLIST((Obj) frames[2], frames[1]++));
idw = INT_INTOBJ(ELM_PLIST(id, w));
if (idw == 0) {
adj = ELM_PLIST(digraph, w);
PLAIN_LIST(adj);
level++;
frames += 3;
frames[0] = w;
// vertex
frames[1] = 1;
// index
frames[2] = (UInt) adj;
SET_ELM_PLIST(stack1, ++end1, INTOBJ_INT(w));
stack2[++end2] = end1;
SET_ELM_PLIST(id, w, INTOBJ_INT(end1));
}
else {
while (stack2[end2] > idw) {
end2--;
// pop from stack2
}
}
}
}
}
}
for (v = 1; v <= n; v++) {
SET_ELM_PLIST(id, v, INTOBJ_INT(INT_INTOBJ(ELM_PLIST(id, v)) - n));
}
out = NEW_PREC(2);
SHRINK_PLIST(comps, LEN_PLIST(comps));
AssPRec(out, RNamName(
"id"), id);
AssPRec(out, RNamName(
"comps"), comps);
free(stack2);
return out;
}
static UInt UF_FIND(UInt* id, UInt i) {
while (i != id[i])
i = id[i];
return i;
}
static void 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;
else if (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;
}
static Obj FuncIS_ACYCLIC_DIGRAPH(Obj self, Obj adj) {
UInt nr, i, j, k, level;
Obj nbs;
UInt *stack, *ptr;
nr = LEN_PLIST(adj);
// init the buf
ptr = safe_calloc(nr + 1,
sizeof(UInt));
stack = safe_malloc((2 * nr + 2) *
sizeof(UInt));
for (i = 1; i <= nr; i++) {
nbs = ELM_PLIST(adj, i);
if (LEN_LIST(nbs) == 0) {
ptr[i] = 1;
}
else if (ptr[i] == 0) {
level = 1;
stack[0] = i;
stack[1] = 1;
while (1) {
j = stack[0];
k = stack[1];
if (ptr[j] == 2) {
free(ptr);
stack -= (2 * level) - 2;
// put the stack back to the start
free(stack);
return False;
// We have just travelled around a cycle
}
// 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--;
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(CONST_ADDR_OBJ(nbs)[k]);
stack[1] = 1;
}
}
}
}
free(ptr);
free(stack);
return True;
}
static Obj FuncDIGRAPH_LONGEST_DIST_VERTEX(Obj self, Obj adj, Obj start) {
UInt nr, i, j, k, level, x, prev;
Obj nbs;
UInt *stack, *ptr, *depth;
nr = LEN_PLIST(adj);
i = INT_INTOBJ(start);
if (i > nr || i < 1) {
ErrorQuit(
"DIGRAPH_LONGEST_DIST_VERTEX: the 2nd "
"arg must be a vertex of the first,",
0L,
0L);
}
nbs = ELM_PLIST(adj, i);
if (LEN_LIST(nbs) == 0) {
return INTOBJ_INT(0);
}
ptr = safe_calloc(nr + 1,
sizeof(UInt));
depth = safe_calloc(nr + 1,
sizeof(UInt));
stack = safe_malloc((2 * nr + 2) *
sizeof(UInt));
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);
}
// Forward decl
static Obj FuncDIGRAPH_IN_OUT_NBS(Obj, Obj);
// 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);
}
Obj
const in_list = FuncDIGRAPH_IN_OUT_NBS(self, FuncOutNeighbours(self, D));
// 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;
backtracking =
true;
level--;
stack -= 2;
ptr[stack[0]] = 0;
stack[1]++;
ptr[j] = 1;
// w is the vertex we are backtracking to (-1)
UInt w = stack[0] - 1;
// Record which vertices we have discovered 'above' w
for (UInt m = 0; m < n; m++) {
mat[w * n + m] = mat[w * n + m] + mat[(j - 1) * n + m];
}
mat[w * n + (j - 1)] =
true;
}
else {
backtracking =
false;
level++;
stack += 2;
stack[0] = INT_INTOBJ(CONST_ADDR_OBJ(nbs)[k]);
stack[1] = 1;
ptr[j] = 2;
}
}
}
}
free(mat);
free(ptr);
free(stack);
AssPRec(D, RNamName(
"OutNeighbours"), ot_list);
return D;
}
// TODO(*) use generic DFS, when we have one.
static Obj FuncDIGRAPH_PATH(Obj self, Obj adj, Obj u, Obj v) {
UInt nr, i, j, k, level, target;
Obj nbs, out, path, edge;
UInt *stack, *ptr;
i = INT_INTOBJ(u);
nbs = ELM_PLIST(adj, i);
if (LEN_LIST(nbs) == 0) {
return Fail;
}
target = INT_INTOBJ(v);
nr = LEN_PLIST(adj);
// init the buf
ptr = safe_calloc(nr + 1,
sizeof(UInt));
stack = safe_malloc((2 * nr + 2) *
sizeof(UInt));
level = 1;
stack[0] = i;
stack[1] = 1;
while (1) {
j = stack[0];
k = stack[1];
// Check whether:
// 1. We've previously visited with this vertex, OR
// 2. Whether we've now investigated all descendant branches
nbs = ELM_PLIST(adj, j);
if (ptr[j] != 0 || k > (UInt) LEN_LIST(nbs)) {
ptr[j] = 1;
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(CONST_ADDR_OBJ(nbs)[k]);
if (stack[0] == target) {
// Create output lists
path = NEW_PLIST_IMM(T_PLIST_CYC, level);
SET_LEN_PLIST(path, level);
SET_ELM_PLIST(path, level--, INTOBJ_INT(stack[0]));
edge = NEW_PLIST_IMM(T_PLIST_CYC, level);
SET_LEN_PLIST(edge, level);
out = NEW_PLIST_IMM(T_PLIST_CYC, 2);
SET_LEN_PLIST(out, 2);
// Fill output lists
while (level > 0) {
stack -= 2;
SET_ELM_PLIST(edge, level, INTOBJ_INT(stack[1]));
SET_ELM_PLIST(path, level--, INTOBJ_INT(stack[0]));
}
SET_ELM_PLIST(out, 1, path);
SET_ELM_PLIST(out, 2, edge);
free(ptr);
free(stack);
return out;
}
stack[1] = 1;
}
}
free(ptr);
free(stack);
return Fail;
}
Obj FuncIS_ANTISYMMETRIC_DIGRAPH(Obj self, Obj adj) {
Int nr, i, j, k, l, level, last1, last2;
Obj nbs;
UInt *stack, *ptr;
nr = LEN_PLIST(adj);
if (nr <= 1) {
return True;
}
// init the buf (is this correct length?)
ptr = safe_calloc(nr + 1,
sizeof(UInt));
stack = safe_malloc((4 * nr + 4) *
sizeof(UInt));
for (i = 1; i <= nr; i++) {
nbs = ELM_PLIST(adj, i);
if (LEN_LIST(nbs) == 0) {
ptr[i] = 1;
}
else if (ptr[i] == 0) {
level = 1;
stack[0] = i;
stack[1] = 1;
stack[2] = 0;
stack[3] = 0;
while (1) {
j = stack[0];
k = stack[1];
last1 = stack[2];
last2 = stack[3];
if (j == last2 && j != last1) {
free(ptr);
stack -= (4 * level) - 4;
// put the stack back to the start
free(stack);
return False;
// Found a non-loop cycle of length 2
}
// 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] == 2) {
PLAIN_LIST(nbs);
for (l = 1; l <= LEN_PLIST(nbs); l++) {
if (last1 != j && INT_INTOBJ(CONST_ADDR_OBJ(nbs)[l]) == last1) {
free(ptr);
stack -= (4 * level) - 4;
// put the stack back to the start
free(stack);
return False;
}
}
}
if (k > LEN_LIST(nbs)) {
ptr[j] = 1;
}
if (ptr[j] >= 1) {
level--;
if (level == 0) {
break;
}
// Backtrack and choose next available branch
stack -= 4;
ptr[stack[0]] = 0;
stack[1]++;
}
else {
// Otherwise move onto the next available branch
ptr[j] = 2;
level++;
nbs = ELM_PLIST(adj, j);
stack += 4;
stack[0] = INT_INTOBJ(ELM_LIST(nbs, k));
stack[1] = 1;
stack[2] = j;
// I am wasting memory here, duplicating info
stack[3] = last1;
}
}
}
}
free(ptr);
free(stack);
return True;
}
static Obj FuncIS_STRONGLY_CONNECTED_DIGRAPH(Obj self, Obj digraph) {
UInt n, nextid, *bag, *ptr1, *ptr2, *fptr, *id, w;
n = LEN_PLIST(digraph);
if (n == 0) {
return True;
}
nextid = 1;
bag = safe_malloc(n * 4 *
sizeof(UInt));
ptr1 = bag;
ptr2 = bag + n;
fptr = bag + n * 2;
id = safe_calloc(n + 1,
sizeof(UInt));
// first vertex v=1
PLAIN_LIST(ELM_PLIST(digraph, 1));
fptr[0] = 1;
// vertex
fptr[1] = 1;
// index
*ptr1 = 1;
*ptr2 = nextid;
id[1] = nextid;
while (1) {
// we always return before level = 0
if (fptr[1] > (UInt) LEN_PLIST(ELM_PLIST(digraph, fptr[0]))) {
if (*ptr2 == id[fptr[0]]) {
do {
n--;
}
while (*(ptr1--) != fptr[0]);
free(bag);
free(id);
return n == 0 ?
True :
False;
}
fptr -= 2;
}
else {
w = INT_INTOBJ(ELM_PLIST(ELM_PLIST(digraph, fptr[0]), fptr[1]++));
if (id[w] == 0) {
PLAIN_LIST(ELM_PLIST(digraph, w));
fptr += 2;
fptr[0] = w;
// vertex
fptr[1] = 1;
// index
nextid++;
*(++ptr1) = w;
*(++ptr2) = nextid;
id[w] = nextid;
}
else {
while ((*ptr2) > id[w]) {
ptr2--;
}
}
}
}
// this should never happen, just to keep the compiler happy
return Fail;
}
static Obj FuncDIGRAPH_TOPO_SORT(Obj self, Obj adj) {
UInt nr, i, j, k, count;
UInt level;
Obj nbs, out;
UInt *stack, *ptr;
nr = LEN_PLIST(adj);
if (nr == 0) {
return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}
out = NEW_PLIST_IMM(T_PLIST_CYC, nr);
SET_LEN_PLIST(out, nr);
if (nr == 1) {
SET_ELM_PLIST(out, 1, INTOBJ_INT(1));
return out;
}
// init the buf
ptr = safe_calloc(nr + 1,
sizeof(UInt));
stack = safe_malloc((2 * nr + 2) *
sizeof(UInt));
count = 0;
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;
}
else if (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));
}
}
}
SET_LEN_PLIST(src, m);
SET_LEN_PLIST(ran, m);
if (IsAttributeStoringRep(D)) {
AssPRec(D, RNamName(
"DigraphSource"), src);
AssPRec(D, RNamName(
"DigraphRange"), ran);
return D;
}
else {
Obj tmp = NEW_PREC(2);
SET_LEN_PREC(tmp, 2);
AssPRec(tmp, RNamName(
"DigraphSource"), src);
AssPRec(tmp, RNamName(
"DigraphRange"), ran);
return tmp;
}
}
// 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);
return True;
}
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);
return True;
}
}
}
free(seen);
return False;
}
/***************** 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) {
return True;
}
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];
}
else if (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);
return False;
}
}
free(dist);
free(adj);
return True;
}
// 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;
}
static void 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];
}
}
}
static Obj FuncDIGRAPH_SHORTEST_DIST(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_SHORTEST_DIST, -1, 1,
false,
false,
true);
}
static Obj FuncDIGRAPH_DIAMETER(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_SHORTEST_DIST, -1, 1,
false,
true,
true);
}
static void 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;
}
}
static Obj FuncIS_TRANSITIVE_DIGRAPH(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1,
true,
false,
false);
}
static Obj FuncDIGRAPH_TRANS_CLOSURE(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1,
false,
false,
false);
}
static void
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;
}
}
static Obj FuncDIGRAPH_REFLEX_TRANS_CLOSURE(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_REFLEX_TRANS_CLOSURE, 0, 1,
false,
false,
false);
}
static Obj FuncRANDOM_DIGRAPH(Obj self, Obj nn, Obj pp) {
UInt n, i, j;
Double p, q;
Int len;
Obj adj, adji;
n = INT_INTOBJ(nn);
p = VAL_MACFLOAT(pp);
adj = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(adj, n);
for (i = 1; i <= n; i++) {
SET_ELM_PLIST(adj, i, NEW_PLIST(T_PLIST_EMPTY, 0));
CHANGED_BAG(adj);
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
q = (
double) rand() / RAND_MAX;
if (q < p) {
adji = ELM_PLIST(adj, i);
len = LEN_PLIST(adji);
ASS_LIST(adji, len + 1, INTOBJ_INT(j));
}
}
}
return adj;
}
static Obj FuncRANDOM_MULTI_DIGRAPH(Obj self, Obj nn, Obj mm) {
UInt n, m, i, j, k;
Int len;
Obj adj, adjj;
n = INT_INTOBJ(nn);
m = INT_INTOBJ(mm);
adj = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(adj, n);
for (i = 1; i <= n; i++) {
SET_ELM_PLIST(adj, i, NEW_PLIST(T_PLIST_EMPTY, 0));
CHANGED_BAG(adj);
}
for (i = 1; i <= m; i++) {
j = (rand() % n) + 1;
k = (rand() % n) + 1;
adjj = ELM_PLIST(adj, j);
len = LEN_PLIST(adjj);
ASS_LIST(adjj, len + 1, INTOBJ_INT(k));
}
return adj;
}
static bool EqJumbledPlists(Obj l, Obj r,
Int nr,
Int* buf) {
bool eq;
Int j, jj;
// Check first whether the lists are identical
eq =
true;
for (j = 1; j <= nr; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j));
if (jj != INT_INTOBJ(ELM_PLIST(r, j))) {
eq =
false;
break;
}
}
// Otherwise check that they have equal content
if (!eq) {
for (j = 1; j <= nr; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j)) - 1;
buf[jj]++;
jj = INT_INTOBJ(ELM_PLIST(r, j)) - 1;
buf[jj]--;
}
for (j = 1; j <= nr; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j)) - 1;
if (buf[jj] != 0) {
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// 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)
static inline 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) {
return False;
}
// Check NrEdges is equal
m1 = DigraphNrEdges(digraph1);
m2 = DigraphNrEdges(digraph2);
if (m1 != m2) {
return False;
}
out1 = FuncOutNeighbours(self, digraph1);
out2 = FuncOutNeighbours(self, digraph2);
buf = safe_calloc(n1,
sizeof(
Int));
// 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);
return False;
}
if (!EqJumbledPlists(a, b, nr, buf)) {
free(buf);
return False;
}
}
free(buf);
return True;
}
static Int LTJumbledPlists(Obj l, Obj r,
Int nr1,
Int nr2,
Int* buf,
Int n) {
bool eq;
Int j, jj, min;
// Check first whether the lists are identical
if (nr1 == nr2) {
eq =
true;
for (j = 1; j <= nr1; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j));
if (jj != INT_INTOBJ(ELM_PLIST(r, j))) {
eq =
false;
break;
}
}
}
else {
eq =
false;
}
// Otherwise compare their content
if (!eq) {
min = nr1 < nr2 ? nr1 : nr2;
for (j = 1; j <= min; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j)) - 1;
buf[jj]++;
jj = INT_INTOBJ(ELM_PLIST(r, j)) - 1;
buf[jj]--;
}
for (j = min + 1; j <= nr1; j++) {
jj = INT_INTOBJ(ELM_PLIST(l, j)) - 1;
buf[jj]++;
}
for (j = min + 1; j <= nr2; j++) {
jj = INT_INTOBJ(ELM_PLIST(r, j)) - 1;
buf[jj]--;
}
for (j = 0; j < n; j++) {
if (buf[j] < 0) {
// Pr("Found difference: range: %d, number: %d\n", j + 1, buf[j]);
return 2;
}
else if (buf[j] > 0) {
// Pr("Found difference: range: %d, number: %d\n", j + 1, buf[j]);
return 1;
}
}
}
return 0;
// Return 0: l = r (as multisets)
// Return 1: l < r
// Return 2: r < l
}
static Obj FuncDIGRAPH_LT(Obj self, Obj digraph1, Obj digraph2) {
UInt i, n1, n2, m1, m2;
Obj out1, out2, a, b;
Int nr1, nr2, *buf, comp, max;
// Compare NrVertices
n1 = DigraphNrVertices(digraph1);
n2 = DigraphNrVertices(digraph2);
if (n1 < n2) {
return True;
}
else if (n2 < n1) {
return False;
}
// Compare NrEdges
m1 = DigraphNrEdges(digraph1);
m2 = DigraphNrEdges(digraph2);
if (m1 < m2) {
return True;
}
else if (m2 < m1) {
return False;
}
out1 = FuncOutNeighbours(self, digraph1);
out2 = FuncOutNeighbours(self, digraph2);
buf = safe_calloc(n1,
sizeof(
Int));
// 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);
nr1 = LEN_PLIST(a);
nr2 = LEN_PLIST(b);
max = nr1 < nr2 ? nr2 : nr1;
// Check whether both vertices have 0 out-degree
if (max != 0) {
if (nr1 == 0) {
free(buf);
return False;
}
else if (nr2 == 0) {
free(buf);
return True;
}
// Both vertices have positive out-degree
// Compare out1[i] and out2[i]
comp = LTJumbledPlists(a, b, nr1, nr2, buf, n1);
if (comp == 1) {
free(buf);
return True;
}
else if (comp == 2) {
free(buf);
return False;
}
// if comp == 0 then the lists are equal, so continue
}
}
free(buf);
return False;
}
// bliss
static BlissGraph* buildBlissMultiDigraph(Obj digraph) {
UInt n, i, j, k, l, nr;
Obj adji, adj;
BlissGraph* graph;
n = DigraphNrVertices(digraph);
graph = bliss_digraphs_new(n);
adj = FuncOutNeighbours(0L, digraph);
for (i = 1; i <= n; i++) {
adji = ELM_PLIST(adj, i);
nr = LEN_PLIST(adji);
for (j = 1; j <= nr; j++) {
k = bliss_digraphs_add_vertex(graph, 1);
l = bliss_digraphs_add_vertex(graph, 2);
bliss_digraphs_add_edge(graph, i - 1, k);
bliss_digraphs_add_edge(graph, k, l);
bliss_digraphs_add_edge(graph, l, INT_INTOBJ(ELM_PLIST(adji, j)) - 1);
}
}
return graph;
}
// TODO: document mult (and everything else)
static BlissGraph*
buildBlissDigraph(Obj digraph, Obj vert_colours, Obj edge_colours) {
uint64_t colour, mult, num_vc, num_ec, n, i, j, k, nr;
Obj adjj, adj;
BlissGraph* graph;
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);
}
}
}
else if (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;
n = DigraphNrVertices(digraph);
DIGRAPHS_ASSERT(n == (UInt) LEN_LIST(colours));
graph = bliss_digraphs_new(0);
adj = FuncOutNeighbours(0L, digraph);
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;
}
static void digraph_hook_function(
void* user_param,
unsigned int N,
const unsigned int* 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);
for (i = 0; i < n; i++) {
ptr[i] = aut[i];
}
gens = ELM_PLIST(user_param, 1);
AssPlist(gens, LEN_PLIST(gens) + 1, 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;
}
static Obj FuncDIGRAPH_AUTOMORPHISMS(Obj self,
Obj digraph,
Obj vert_colours,
Obj edge_colours) {
Obj autos, p, n;
BlissGraph* graph;
UInt4* ptr;
const unsigned int* canon;
Int i;
graph = buildBlissDigraph(digraph, vert_colours, edge_colours);
autos = NEW_PLIST(T_PLIST, 3);
n = INTOBJ_INT(DigraphNrVertices(digraph));
SET_ELM_PLIST(autos, 1, NEW_PLIST(T_PLIST, 0));
// perms of the vertices
CHANGED_BAG(autos);
SET_ELM_PLIST(autos, 2, n);
SET_LEN_PLIST(autos, 2);
BlissStats stats;
canon = bliss_digraphs_find_canonical_labeling(
graph, digraph_hook_function, autos, &stats);
p = NEW_PERM4(INT_INTOBJ(n));
ptr = ADDR_PERM4(p);
for (i = 0; i < INT_INTOBJ(n); i++) {
ptr[i] = canon[i];
}
SET_ELM_PLIST(autos, 2, p);
CHANGED_BAG(autos);
bliss_digraphs_release(graph);
if (LEN_PLIST(ELM_PLIST(autos, 1)) != 0) {
SortDensePlist(ELM_PLIST(autos, 1));
RemoveDupsDensePlist(ELM_PLIST(autos, 1));
}
#ifdef DIGRAPHS_WITH_INCLUDED_BLISS
Obj size = MultiplyList(stats.group_size, stats.group_size_len);
bliss_digraphs_free_blissstats(&stats);
SET_LEN_PLIST(autos, 3);
SET_ELM_PLIST(autos, 3, size);
#endif
return autos;
}
// user_param = [vertex perms, nr vertices, edge perms, nr edges]
static void multidigraph_hook_function(
void* user_param,
unsigned int N,
const unsigned int* 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]
static void multidigraph_colours_hook_function(
void* user_param,
unsigned int N,
const unsigned int* 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;
const unsigned int* 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)));
BlissStats stats;
if (colours ==
False) {
canon = bliss_digraphs_find_canonical_labeling(
graph, multidigraph_hook_function, autos, &stats);
}
else {
canon = bliss_digraphs_find_canonical_labeling(
graph, multidigraph_colours_hook_function, autos, &stats);
}
// 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);
if (LEN_PLIST(ELM_PLIST(autos, 1)) != 0) {
SortDensePlist(ELM_PLIST(autos, 1));
RemoveDupsDensePlist(ELM_PLIST(autos, 1));
}
if (LEN_PLIST(ELM_PLIST(autos, 3)) != 0) {
SortDensePlist(ELM_PLIST(autos, 3));
RemoveDupsDensePlist(ELM_PLIST(autos, 3));
}
#ifdef DIGRAPHS_WITH_INCLUDED_BLISS
Obj size = MultiplyList(stats.group_size, stats.group_size_len);
bliss_digraphs_free_blissstats(&stats);
SET_LEN_PLIST(autos, 4);
SET_ELM_PLIST(autos, 4, size);
#endif
return autos;
}
static Obj FuncDIGRAPH_CANONICAL_LABELLING(Obj self, Obj digraph, Obj colours) {
Obj p;
UInt4* ptr;
BlissGraph* graph;
Int n, i;
const unsigned int* canon;
if (colours == Fail) {
graph = buildBlissDigraph(digraph, NULL, NULL);
}
else {
graph = buildBlissDigraph(digraph, colours, NULL);
}
canon = bliss_digraphs_find_canonical_labeling(graph, 0, 0, 0);
n = DigraphNrVertices(digraph);
p = NEW_PERM4(n);
ptr = ADDR_PERM4(p);
for (i = 0; i < n; i++) {
ptr[i] = canon[i];
}
bliss_digraphs_release(graph);
return p;
}
static Obj
FuncMULTIDIGRAPH_CANONICAL_LABELLING(Obj self, Obj digraph, Obj colours) {
Obj p, q, out;
UInt4* ptr;
BlissGraph* graph;
Int m, n, i;
const unsigned int* canon;
if (colours == Fail) {
graph = buildBlissMultiDigraph(digraph);
}
else {
graph = buildBlissMultiDigraphWithColours(digraph, colours);
}
canon = bliss_digraphs_find_canonical_labeling(graph, 0, 0, 0);
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;
}
}
bliss_digraphs_release(graph);
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);
return out;
}
/*F * * * * * * * * * * * * * initialize package * * * * * * * * * * * * * * */
/******************************************************************************
*V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
*/
static StructGVarFunc GVarFuncs[] = {
GVAR_FUNC(DIGRAPH_NREDGES, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_NRADJACENCIES, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_NRADJACENCIESWITHOUTLOOPS, 1,
"digraph"),
GVAR_FUNC(GABOW_SCC, 1,
"adj"),
GVAR_FUNC(DIGRAPH_CONNECTED_COMPONENTS, 1,
"digraph"),
GVAR_FUNC(IS_ACYCLIC_DIGRAPH, 1,
"adj"),
GVAR_FUNC(DIGRAPH_LONGEST_DIST_VERTEX, 2,
"adj, start"),
GVAR_FUNC(DIGRAPH_TRANS_REDUCTION, 1,
"list"),
GVAR_FUNC(IS_ANTISYMMETRIC_DIGRAPH, 1,
"adj"),
GVAR_FUNC(IS_STRONGLY_CONNECTED_DIGRAPH, 1,
"adj"),
GVAR_FUNC(DIGRAPH_TOPO_SORT, 1,
"adj"),
GVAR_FUNC(DIGRAPH_SYMMETRIC_SPANNING_FOREST, 1,
"adj"),
GVAR_FUNC(DIGRAPH_SOURCE_RANGE, 1,
"digraph"),
GVAR_FUNC(OutNeighbours, 1,
"D"),
GVAR_FUNC(DIGRAPH_OUT_NEIGHBOURS_FROM_SOURCE_RANGE, 3,
"N, source, range"),
GVAR_FUNC(DIGRAPH_NR_VERTICES, 1,
"D"),
GVAR_FUNC(DIGRAPH_IN_OUT_NBS, 1,
"adj"),
GVAR_FUNC(ADJACENCY_MATRIX, 1,
"digraph"),
GVAR_FUNC(IS_MULTI_DIGRAPH, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_SHORTEST_DIST, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_DIAMETER, 1,
"digraph"),
GVAR_FUNC(IS_TRANSITIVE_DIGRAPH, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_TRANS_CLOSURE, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_REFLEX_TRANS_CLOSURE, 1,
"digraph"),
GVAR_FUNC(RANDOM_DIGRAPH, 2,
"nn, limm"),
GVAR_FUNC(RANDOM_MULTI_DIGRAPH, 2,
"nn, mm"),
GVAR_FUNC(DIGRAPH_EQUALS, 2,
"digraph1, digraph2"),
GVAR_FUNC(DIGRAPH_LT, 2,
"digraph1, digraph2"),
GVAR_FUNC(DIGRAPH_HASH, 1,
"digraph"),
GVAR_FUNC(DIGRAPH_PATH, 3,
"digraph, u, v"),
GVAR_FUNC(DIGRAPH_AUTOMORPHISMS, 3,
"digraph, vert_colours, edge_colours"),
GVAR_FUNC(MULTIDIGRAPH_AUTOMORPHISMS, 2,
"digraph, colours"),
GVAR_FUNC(DIGRAPH_CANONICAL_LABELLING, 2,
"digraph, colours"),
GVAR_FUNC(MULTIDIGRAPH_CANONICAL_LABELLING, 2,
"digraph, colours"),
GVAR_FUNC(HomomorphismDigraphsFinder,
-1,
"digraph1, digraph2, hook, user_param, max_results, hint, "
"injective, image, partial_map, colors1, colors2"),
GVAR_FUNC(DigraphsCliquesFinder,
-1,
"digraph, hook, user_param, limit, "
"include, exclude, max, size"),
GVAR_FUNC(IS_PLANAR, 1,
"digraph"),
GVAR_FUNC(PLANAR_EMBEDDING, 1,
"digraph"),
GVAR_FUNC(KURATOWSKI_PLANAR_SUBGRAPH, 1,
"digraph"),
GVAR_FUNC(IS_OUTER_PLANAR, 1,
"digraph"),
GVAR_FUNC(OUTER_PLANAR_EMBEDDING, 1,
"digraph"),
GVAR_FUNC(KURATOWSKI_OUTER_PLANAR_SUBGRAPH, 1,
"digraph"),
GVAR_FUNC(SUBGRAPH_HOMEOMORPHIC_TO_K23, 1,
"digraph"),
GVAR_FUNC(SUBGRAPH_HOMEOMORPHIC_TO_K33, 1,
"digraph"),
GVAR_FUNC(SUBGRAPH_HOMEOMORPHIC_TO_K4, 1,
"digraph"),
GVAR_FUNC(DIGRAPHS_FREE_HOMOS_DATA, 0,
""),
GVAR_FUNC(DIGRAPHS_FREE_CLIQUES_DATA, 0,
""),
{0, 0, 0, 0, 0}
/* Finish with an empty entry */
};
/******************************************************************************
*F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
*/
static Int InitKernel(StructInitInfo* module) {
/* init filters and functions */
InitHdlrFuncsFromTable(GVarFuncs);
ImportGVarFromLibrary(
"IsDigraph", &IsDigraph);
ImportGVarFromLibrary(
"IsMultiDigraph", &IsMultiDigraph);
ImportGVarFromLibrary(
"IsDigraphEdge", &IsDigraphEdge);
ImportGVarFromLibrary(
"DIGRAPHS_ValidateVertexColouring",
&DIGRAPHS_ValidateVertexColouring);
ImportGVarFromLibrary(
"infinity", &Infinity);
ImportGVarFromLibrary(
"IsSymmetricDigraph", &IsSymmetricDigraph);
ImportGVarFromLibrary(
"AutomorphismGroup", &AutomorphismGroup);
ImportGVarFromLibrary(
"GeneratorsOfGroup", &GeneratorsOfGroup);
ImportGVarFromLibrary(
"IsAttributeStoringRep", &IsAttributeStoringRepObj);
ImportGVarFromLibrary(
"IsPermGroup", &IsPermGroup);
ImportGVarFromLibrary(
"IsDigraphAutomorphism", &IsDigraphAutomorphism);
ImportGVarFromLibrary(
"LargestMovedPointPerms", &LargestMovedPointPerms);
ImportGVarFromLibrary(
"SmallestMovedPointPerm", &SmallestMovedPointPerm);
ImportGVarFromLibrary(
"IsClique", &IsClique);
ImportGVarFromLibrary(
"IsTrivial", &IsTrivial);
ImportGVarFromLibrary(
"Orbit", &Orbit);
ImportGVarFromLibrary(
"Stabilizer", &Stabilizer);
ImportGVarFromLibrary(
"IsSubset", &IsSubset);
ImportGVarFromLibrary(
"OnTuples", &OnTuples);
ImportGVarFromLibrary(
"Group", &Group);
ImportGVarFromLibrary(
"ClosureGroup", &ClosureGroup);
ImportGVarFromLibrary(
"InfoWarning", &InfoWarning);
/* return success */
return 0;
}
/******************************************************************************
*F InitLibrary( <module> ) . . . . . . . initialise library data structures
*/
static Int InitLibrary(StructInitInfo* module) {
/* init filters and functions */
InitGVarFuncsFromTable(GVarFuncs);
/* return success */
return 0;
}
/******************************************************************************
*F InitInfopl() . . . . . . . . . . . . . . . . . table of init functions
*/
static StructInitInfo module = {
.type = MODULE_DYNAMIC,
.name =
"digraphs",
.initKernel = InitKernel,
.initLibrary = InitLibrary,
};
extern StructInitInfo* Init__Dynamic(
void);
// prototype to avoid warnings
StructInitInfo* Init__Dynamic(
void) {
return &module;
}