aboutsummaryrefslogtreecommitdiff
path: root/src/depmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/depmap.c')
-rw-r--r--src/depmap.c406
1 files changed, 292 insertions, 114 deletions
diff --git a/src/depmap.c b/src/depmap.c
index fd5521f..a74a7f0 100644
--- a/src/depmap.c
+++ b/src/depmap.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies.
- Copyright (C) 2008-2020 Sergey Poznyakoff
+ Copyright (C) 2008-2023 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,135 +16,179 @@
#include "pies.h"
-#ifndef CHAR_BIT
-# define CHAR_BIT 8
-#endif
-#define BITS_PER_WORD (sizeof(unsigned)*CHAR_BIT)
-
-#define WORDSIZE(n) (((n) + BITS_PER_WORD - 1) / BITS_PER_WORD)
-#define SETBIT(x, i) ((x)[(i)/BITS_PER_WORD] |= (1<<((i) % BITS_PER_WORD)))
-#define CLRBIT(x, i) ((x)[(i)/BITS_PER_WORD] &= ~(1<<((i) % BITS_PER_WORD)))
-#define BITISSET(x, i) (((x)[(i)/BITS_PER_WORD] & (1<<((i) % BITS_PER_WORD))) != 0)
-
-void
-TC (unsigned *R, int n)
-{
- register int rowsize;
- register unsigned mask;
- register unsigned *rowj;
- register unsigned *rp;
- register unsigned *rend;
- register unsigned *ccol;
-
- unsigned *relend;
- unsigned *cword;
- unsigned *rowi;
-
- rowsize = WORDSIZE (n) * sizeof (unsigned);
- relend = (unsigned *) ((char *) R + (n * rowsize));
-
- cword = R;
- mask = 1;
- rowi = R;
- while (rowi < relend)
- {
- ccol = cword;
- rowj = R;
-
- while (rowj < relend)
- {
- if (*ccol & mask)
- {
- rp = rowi;
- rend = (unsigned *) ((char *) rowj + rowsize);
-
- while (rowj < rend)
- *rowj++ |= *rp++;
- }
- else
- {
- rowj = (unsigned *) ((char *) rowj + rowsize);
- }
-
- ccol = (unsigned *) ((char *) ccol + rowsize);
- }
-
- mask <<= 1;
- if (mask == 0)
- {
- mask = 1;
- cword++;
- }
- rowi = (unsigned *) ((char *) rowi + rowsize);
- }
-}
+#define INFINITY INT_MAX
struct pies_depmap
{
- size_t nrows; /* Number of rows (== number of mapped elements) */
- size_t rowlen; /* Length of a row in words */
- unsigned r[1]; /* Data rows */
+ size_t dim; /* Number of vertices */
+ int **dist; /* Distance between two vertices */
+ int **next; /* Next vertex on the shortest path from i to j */
+ int closed; /* True if the transitive closure was computed */
+ int map[1]; /* Storage array for dist and next */
};
-pies_depmap_t
-depmap_alloc (size_t count)
+size_t
+depmap_dim (struct pies_depmap *dmap)
{
- size_t size = (count + BITS_PER_WORD - 1) / BITS_PER_WORD;
- pies_depmap_t dmap = grecs_zalloc (sizeof (*dmap) - 1
- + count * size * sizeof (unsigned));
- dmap->nrows = count;
- dmap->rowlen = size;
- return dmap;
+ return dmap->dim;
}
-/* Return size of a depmap row in bytes */
-static inline size_t
-depmap_row_size (pies_depmap_t dpm)
+pies_depmap_t
+depmap_alloc (size_t count)
{
- return dpm->rowlen * sizeof (unsigned);
-}
+ struct pies_depmap *dmap;
+ size_t size;
+ size_t i, j;
+ int *ip;
+
+ if (count < 0)
+ abort ();
+ /* size = sizeof (*dmap) + 2 * count * count * sizeof (int) */
+ if (count > 0)
+ {
+ if (SIZE_MAX / count < count)
+ grecs_alloc_die ();
+ size = count * count;
+ if (SIZE_MAX / 2 < size)
+ grecs_alloc_die ();
+ size += size;
+ if (SIZE_MAX / sizeof (int) < size)
+ grecs_alloc_die ();
+ size *= sizeof (int);
+ if (SIZE_MAX - size < sizeof (*dmap))
+ grecs_alloc_die ();
+ size += sizeof (*dmap);
+ }
+ else
+ size = sizeof (*dmap);
+
+ dmap = grecs_zalloc (size);
+ dmap->dim = count;
+ dmap->closed = 0;
+ dmap->dist = grecs_calloc (sizeof (dmap->dist[0]), count);
+ dmap->next = grecs_calloc (sizeof (dmap->next[0]), count);
+
+ ip = dmap->map;
+ for (i = 0; i < count; i++)
+ {
+ dmap->dist[i] = ip;
+ for (j = 0; j < count; j++)
+ *ip++ = INFINITY;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ dmap->next[i] = ip;
+ for (j = 0; j < count; j++)
+ *ip++ = -1;
+ }
+
+ return dmap;
+}
pies_depmap_t
-depmap_copy (pies_depmap_t dpm)
+depmap_copy (pies_depmap_t src)
{
- pies_depmap_t copy = depmap_alloc (dpm->nrows);
- memcpy (copy->r, dpm->r, dpm->nrows * depmap_row_size (dpm));
- return copy;
+ pies_depmap_t dst = depmap_alloc (src->dim);
+ memcpy (dst->map, src->map, 2 * src->dim * src->dim * sizeof (src->map[0]));
+ dst->closed = src->closed;
+ return dst;
}
-static unsigned *
-depmap_rowptr (pies_depmap_t dmap, size_t row)
+void
+depmap_free (pies_depmap_t dmap)
{
- return dmap->r + dmap->rowlen * row;
+ if (dmap)
+ {
+ free (dmap->dist);
+ free (dmap->next);
+ free (dmap);
+ }
}
void
-depmap_set (pies_depmap_t dmap, size_t row, size_t col)
+depmap_set (pies_depmap_t dmap, size_t i, size_t j)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- SETBIT (rptr, col);
+ dmap->dist[i][j] = 1;
+ dmap->closed = 0;
}
-void
-depmap_clear (pies_depmap_t dmap, size_t row, size_t col)
+int
+depmap_isset (pies_depmap_t dmap, size_t i, size_t j)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- CLRBIT (rptr, col);
+ return dmap->dist[i][j] != INFINITY;
}
-int
-depmap_isset (pies_depmap_t dmap, size_t row, size_t col)
+/* Remove nth row and column from the map */
+void
+depmap_remove (pies_depmap_t dmap, size_t n)
{
- unsigned *rptr = depmap_rowptr (dmap, row);
- return BITISSET (rptr, col);
+ if (n < dmap->dim - 1)
+ {
+ size_t i;
+
+ /* Remove nth row */
+ memmove (&dmap->dist[n], &dmap->dist[n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->dist[0]));
+ memmove (&dmap->next[n], &dmap->next[n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->next[0]));
+
+ /* Remove nth column */
+ for (i = 0; i < dmap->dim; i++)
+ {
+ memmove (&dmap->dist[i][n], &dmap->dist[i][n+1],
+ (dmap->dim - n - 1) * sizeof (dmap->dist[0][0]));
+ memmove (&dmap->next[i][n], &dmap->next[i][n + 1],
+ (dmap->dim - n - 1) * sizeof (dmap->next[0][0]));
+ }
+ }
+ dmap->dim--;
}
void
depmap_tc (pies_depmap_t dmap)
{
- TC (dmap->r, dmap->nrows);
-}
+ size_t i, j, k;
+
+ if (dmap->closed) return;
+ /*
+ * Initialize next vertex map and reset all distances between
+ * adjacent vertices to 1.
+ */
+ for (i = 0; i < dmap->dim; i++)
+ for (j = 0; j < dmap->dim; j++)
+ {
+ if (depmap_isset (dmap, i, j))
+ {
+ dmap->dist[i][j] = 1;
+ dmap->next[i][j] = j;
+ }
+ else
+ {
+ dmap->next[i][j] = -1;
+ }
+ }
+ /* Modified Floyd-Warshall algorithm */
+ for (k = 0; k < dmap->dim; k++)
+ for (i = 0; i < dmap->dim; i++)
+ for (j = 0; j < dmap->dim; j++)
+ {
+ if (depmap_isset (dmap, i, k) && depmap_isset (dmap, k, j))
+ {
+ int t = dmap->dist[i][k] + dmap->dist[k][j];
+ if (dmap->dist[i][j] > t)
+ {
+ dmap->dist[i][j] = t;
+ dmap->next[i][j] = dmap->next[i][k];
+ }
+ }
+ }
+
+ /* Mark completion */
+ dmap->closed = 1;
+}
+
struct pies_depmap_pos
{
enum pies_depmap_direction dir;
@@ -154,12 +198,12 @@ struct pies_depmap_pos
size_t
depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos)
{
- for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->nrows;
+ for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->dim;
pos->coord[pos->dir]++)
if (depmap_isset (dmap, pos->coord[0], pos->coord[1]))
break;
- return pos->coord[pos->dir] == dmap->nrows ?
+ return pos->coord[pos->dir] == dmap->dim ?
(size_t) -1 : pos->coord[pos->dir];
}
@@ -180,23 +224,157 @@ depmap_end (pies_depmap_pos_t pos)
{
grecs_free (pos);
}
+
+static struct depmap_path *
+depmap_path_alloc (void)
+{
+ struct depmap_path *path = grecs_malloc (sizeof *path);
+ path->len = 0;
+ path->head = path->tail = NULL;
+ path->next = NULL;
+ return path;
+}
-/* Remove nth row and column from the map */
void
-depmap_remove (pies_depmap_t dmap, size_t n)
+depmap_path_free (struct depmap_path *path)
{
- if (n < dmap->nrows - 1)
+ while (path)
{
- size_t i, j;
+ struct depmap_path *next_path = path->next;
+ struct depmap_path_elem *p = path->head;
+ while (p)
+ {
+ struct depmap_path_elem *next = p->next;
+ free (p);
+ p = next;
+ }
+ free (path);
+ path = next_path;
+ }
+}
- /* Remove nth row */
- memmove (depmap_rowptr (dmap, n), depmap_rowptr (dmap, n + 1),
- (dmap->nrows - n - 1) * depmap_row_size (dmap));
- /* Renumber all columns j >= n: j = j - 1 */
- for (i = 0; i < dmap->nrows; i++)
- for (j = n; j < dmap->nrows - 1; j++)
- (depmap_isset (dmap, i, j + 1) ? depmap_set : depmap_clear)
- (dmap, i, j);
+static void
+depmap_path_append (struct depmap_path *path, int idx)
+{
+ struct depmap_path_elem *p = grecs_malloc (sizeof (*p));
+ p->idx = idx;
+ p->next = NULL;
+ if (path->tail)
+ path->tail->next = p;
+ else
+ path->head = p;
+ path->tail = p;
+ path->len++;
+}
+
+/* Returns the path from vertex i to j. */
+static struct depmap_path *
+depmap_path (pies_depmap_t dmap, int i, int j)
+{
+ struct depmap_path *dp;
+ if (dmap->closed && depmap_isset (dmap, i, j))
+ {
+ dp = depmap_path_alloc ();
+ depmap_path_append (dp, i);
+ while (i != j)
+ {
+ i = dmap->next[i][j];
+ depmap_path_append (dp, i);
+ }
+ }
+ else
+ dp = NULL;
+ return dp;
+}
+
+/*
+ * If the vertex i is a part of a cyclic path, return the path adjusted
+ * so that it starts at the vertex with the lowest number among the vertices
+ * traversed by that path.
+ */
+static struct depmap_path *
+depmap_normalized_cyclic_path (pies_depmap_t dmap, int i)
+{
+ struct depmap_path *dp;
+ if (dmap->closed && depmap_isset (dmap, i, i))
+ {
+ struct depmap_path_elem *start, *p;
+
+ dp = depmap_path (dmap, dmap->next[i][i], i);
+ start = dp->head;
+ for (p = start->next; p; p = p->next)
+ {
+ if (p->idx < start->idx)
+ start = p;
+ }
+ if (start != dp->head)
+ {
+ dp->tail->next = dp->head;
+ for (p = dp->head; p->next != start; p = p->next)
+ ;
+ dp->tail = p;
+ dp->tail->next = NULL;
+ dp->head = start;
+ }
+ }
+ else
+ dp = NULL;
+ return dp;
+}
+
+/* Return true (1) if normalized paths A and B are equal. */
+static int
+depmap_path_eq (struct depmap_path *a, struct depmap_path *b)
+{
+ struct depmap_path_elem *ap, *bp;
+ ap = a->head;
+ bp = b->head;
+ while (ap)
+ {
+ if (bp == NULL || ap->idx != bp->idx)
+ return 0;
+ ap = ap->next;
+ bp = bp->next;
+ }
+ return bp == NULL;
+}
+
+static int
+depmap_path_find (struct depmap_path *a, struct depmap_path *b)
+{
+ while (a)
+ {
+ if (depmap_path_eq (a, b))
+ return 1;
+ a = a->next;
+ }
+ return 0;
+}
+
+struct depmap_path *
+depmap_cycle_detect (pies_depmap_t dmap)
+{
+ pies_depmap_t dp = depmap_copy (dmap);
+ size_t i;
+ struct depmap_path *head = NULL, *tail = NULL;
+
+ depmap_tc (dp);
+ for (i = 0; i < dmap->dim; i++)
+ {
+ if (depmap_isset (dp, i, i))
+ {
+ struct depmap_path *path = depmap_normalized_cyclic_path (dp, i);
+ if (!head)
+ head = tail = path;
+ else if (depmap_path_find (head, path))
+ depmap_path_free (path);
+ else
+ {
+ tail->next = path;
+ tail = path;
+ }
+ }
}
- dmap->nrows--;
+ depmap_free (dp);
+ return head;
}

Return to:

Send suggestions and report system problems to the System administrator.