Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/pkg/digraphs/src/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 27.8.2025 mit Größe 62 kB image not shown  

Quellcode-Bibliothek digraphs.c   Sprache: C

 
/*******************************************************************************
**
*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, falsefalsetrue);
}

static Obj FuncDIGRAPH_DIAMETER(Obj self, Obj digraph) {
  return FLOYD_WARSHALL(
      digraph, FW_FUNC_SHORTEST_DIST, -1, 1, falsetruetrue);
}

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, truefalsefalse);
}

static Obj FuncDIGRAPH_TRANS_CLOSURE(Obj self, Obj digraph) {
  return FLOYD_WARSHALL(
      digraph, FW_FUNC_TRANS_CLOSURE, 0, 1, falsefalsefalse);
}

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, falsefalsefalse);
}

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;
}

92%


¤ 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.0.81Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.