[ccan] [PATCH] permutation: Generate permutations of arrays
David Gibson
david at gibson.dropbear.id.au
Sat Sep 26 22:11:43 AEST 2015
New module.
Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
ccan/permutation/LICENSE | 1 +
ccan/permutation/_info | 60 ++++++++++++++
ccan/permutation/permutation.c | 100 +++++++++++++++++++++++
ccan/permutation/permutation.h | 181 +++++++++++++++++++++++++++++++++++++++++
ccan/permutation/test/api.c | 146 +++++++++++++++++++++++++++++++++
5 files changed, 488 insertions(+)
create mode 120000 ccan/permutation/LICENSE
create mode 100644 ccan/permutation/_info
create mode 100644 ccan/permutation/permutation.c
create mode 100644 ccan/permutation/permutation.h
create mode 100644 ccan/permutation/test/api.c
diff --git a/ccan/permutation/LICENSE b/ccan/permutation/LICENSE
new file mode 120000
index 0000000..dc314ec
--- /dev/null
+++ b/ccan/permutation/LICENSE
@@ -0,0 +1 @@
+../../licenses/LGPL-2.1
\ No newline at end of file
diff --git a/ccan/permutation/_info b/ccan/permutation/_info
new file mode 100644
index 0000000..8b9d0a3
--- /dev/null
+++ b/ccan/permutation/_info
@@ -0,0 +1,60 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * permutation - Generate permutations
+ *
+ * This module allows you to generate all permutations of a given
+ * number of elements. It can also modify a user-supplied array in
+ * place through all those permutations.
+ *
+ * It uses the "plain changes" method, aka the
+ * Steinhaus-Johnson-Trotter algorithm to generate the permtations.
+ * This has some advantages over a naive recursive algorithm:
+ *
+ * - Each permutation is generated from the last by a single swap of
+ * adjacent elements
+ *
+ * - Constructing each permutation in place from the last takes
+ * amortized O(1) time.
+ *
+ * License: LGPL (v2.1 or any later version)
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/permutation/permutation.h>
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * int i;
+ * struct permutation *pi = permutation_new(argc - 1);
+ *
+ * do {
+ * for (i = 1; i <= argc; i++)
+ * printf("%s ", argv[i]);
+ * printf("\n");
+ * } while (permutation_change_array(pi,
+ * &argv[1], sizeof(argv[1])));
+ * exit(0);
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ printf("ccan/mem\n");
+ return 0;
+ }
+
+ if (strcmp(argv[1], "testdepends") == 0) {
+ printf("ccan/array_size\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/permutation/permutation.c b/ccan/permutation/permutation.c
new file mode 100644
index 0000000..19139d5
--- /dev/null
+++ b/ccan/permutation/permutation.c
@@ -0,0 +1,100 @@
+/* GNU LGPL version 2 (or later) - see LICENSE file for details */
+#include "config.h"
+
+#include <assert.h>
+
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/mem/mem.h>
+#include <ccan/permutation/permutation.h>
+
+unsigned long long permutation_count(int n)
+{
+ unsigned long long count = 1;
+ int i;
+
+ for (i = 1; i <=n; i++)
+ count *= i;
+
+ return count;
+}
+
+struct plain_change_state {
+ int8_t dir : 2;
+ uint8_t pos : PERMUTATION_MAX_SHIFT;
+};
+
+static inline struct plain_change_state *get_state(struct permutation *pi)
+{
+ BUILD_ASSERT(sizeof(struct plain_change_state) == 1);
+
+ return (struct plain_change_state *)(pi + 1) + pi->n;
+}
+
+struct permutation *permutation_new(int n)
+{
+ struct permutation *pi;
+ int i;
+ struct plain_change_state *xs;
+
+ assert(n <= PERMUTATION_MAX_ITEMS);
+
+ pi = malloc(sizeof(*pi) + 2 * n);
+ if (!pi)
+ return NULL;
+
+ pi->n = n;
+
+ xs = get_state(pi);
+
+ for (i = 0; i < pi->n; i++) {
+ pi->v[i] = i;
+ xs[i].pos = i;
+ xs[i].dir = (i == 0) ? 0 : -1;
+ }
+
+ return pi;
+}
+
+permutation_swap_t permutation_change(struct permutation *pi)
+{
+ int v, w, i;
+ int a, b, vdir;
+ struct plain_change_state *xs = get_state(pi);
+
+ if (!pi->n)
+ return 0;
+
+ for (v = pi->n - 1; v > 0; v--) {
+ if (xs[v].dir)
+ break;
+ }
+
+ a = xs[v].pos;
+ vdir = xs[v].dir;
+ if (!vdir)
+ return 0;
+
+ b = a + vdir;
+ w = pi->v[b];
+
+ pi->v[a] = w;
+ pi->v[b] = v;
+ xs[v].pos = b;
+ xs[w].pos = a;
+
+ /* If we reach the end, or the next item is larger, set
+ * direction to 0 */
+ if ((b == 0) || (b == (pi->n-1))
+ || (pi->v[b + vdir] > v))
+ xs[v].dir = 0;
+
+ /* Reset direction on all elements greater than the chosen one */
+ for (i = v + 1; i < pi->n; i++) {
+ if (xs[i].pos < b)
+ xs[i].dir = 1;
+ else
+ xs[i].dir = -1;
+ }
+
+ return (b * PERMUTATION_MAX_ITEMS) + a;
+}
diff --git a/ccan/permutation/permutation.h b/ccan/permutation/permutation.h
new file mode 100644
index 0000000..af7bb13
--- /dev/null
+++ b/ccan/permutation/permutation.h
@@ -0,0 +1,181 @@
+/* Licensed under LGPLv2.1+ - see LICENSE file for details */
+#ifndef CCAN_PERMUTATION_H
+#define CCAN_PERMUTATION_H
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/mem/mem.h>
+
+/*
+ * We limit the number of elements to 64, to keep our data structures
+ * neat. That seems low, but even 64! permutations is far too many to
+ * really generate in practice.
+ */
+#define PERMUTATION_MAX_SHIFT 6
+#define PERMUTATION_MAX_ITEMS (1 << PERMUTATION_MAX_SHIFT)
+
+/**
+ * struct permutation - represents a permutation
+ *
+ * The fields here are user visible, but it's allocated internally
+ * with extra state information appended.
+ *
+ * @n: Number of elements in the permutation
+ * @v: values 0..(n-1) arranged in current permutation
+ */
+struct permutation {
+ int n;
+ uint8_t v[];
+ /* Private state data follows */
+};
+
+
+/**
+ * permutation_count - determine number of permutations
+ * @n: Number of elements
+ *
+ * Returns the number of permutations of @n elements.
+ */
+unsigned long long permutation_count(int n);
+
+/**
+ * permutation_swap_t - represents a swap of two items
+ *
+ * Encodes a swap of two elements in an array. Should be treated as
+ * opaque, except that 0 always represents "swap nothing".
+ */
+typedef unsigned permutation_swap_t;
+
+
+/**
+ * permutation_swap_a - first item to swap
+ * permutation_swap_b - second item to swap
+ */
+static inline int permutation_swap_a(permutation_swap_t swap)
+{
+ return swap % PERMUTATION_MAX_ITEMS;
+}
+
+static inline int permutation_swap_b(permutation_swap_t swap)
+{
+ return swap / PERMUTATION_MAX_ITEMS;
+}
+
+/**
+ * permutation_swap - swap two elements in an array
+ * @base: address of array
+ * @size: size of each array element
+ * @swap: which elements to swap
+ *
+ * Swaps the elements at index permutation_swap_a(@swap) and
+ * permutation_swap_b(@swap) in the array at @base of items of size
+ * @size.
+ */
+static inline void permutation_swap_mem(void *base, size_t size,
+ permutation_swap_t swap)
+{
+ char *ap = (char *)base + (permutation_swap_a(swap) * size);
+ char *bp = (char *)base + (permutation_swap_b(swap) * size);
+
+ BUILD_ASSERT(sizeof(permutation_swap_t) * 8
+ >= PERMUTATION_MAX_SHIFT * 2);
+
+ if (!swap)
+ return;
+
+ memswap(ap, bp, size);
+}
+
+/**
+ * PERMUTATION_SWAP - swap two elements in an array
+ * @a_: array
+ * @swap_: which elements to swap
+ *
+ * As permutation_swap(), but must act on a C array declared at the
+ * right size.
+ */
+#define PERMUTATION_SWAP(a_, swap_) \
+ do { \
+ permutation_swap((a_), sizeof((a_)[0]), (swap_)); \
+ } while (0)
+
+
+/**
+ * permutation_new - allocates a new identity permutation
+ * @n: Number of elements
+ *
+ * Allocates and initializes a new permutation of @n elements.
+ * Initially it represents the identity permutation.
+ */
+struct permutation *permutation_new(int n);
+
+/**
+ * PERMUTATION_NEW - allocates a new identity permutation of an array
+ * @array_: Array to permute
+ *
+ * As permutation_new() but take the number of elements from the
+ * declaration of @array_.
+ */
+#define PERMUTATION_NEW(array_) (permutation_new(ARRAY_SIZE(array_)))
+
+/**
+ * permutation_change - Advance to a new permutation
+ * @pi: Current permutation
+ *
+ * Advances @pi to the next permutation by the plain changes method
+ * (Steinhaus-Johnson-Trotter algorithm).
+ *
+ * Returns the elements which were swapped in @pi, or 0 if there are
+ * no more permutations.
+ */
+permutation_swap_t permutation_change(struct permutation *pi);
+
+/**
+ * permutation_change_array - Advance an array to a new permutation
+ * @pi: Current permutation
+ * @base: Address of array
+ * @size: Size of array elements
+ *
+ * Assuing the array at @base is currently in permutation @pi,
+ * advances @pi to the next permutation (as permutation_change()) and
+ * keeps the array in sync.
+ *
+ * Returns true if the permutation was advanced, false if there are no
+ * more permutations.
+ */
+static inline bool permutation_change_array(struct permutation *pi,
+ void *base, size_t size)
+{
+ permutation_swap_t swap = permutation_change(pi);
+
+ permutation_swap_mem(base, size, swap);
+ return (swap != 0);
+}
+
+static inline bool permutation_change_array_check_(struct permutation *pi,
+ void *base, size_t size,
+ int n)
+{
+ assert(n == pi->n);
+ return permutation_change_array(pi, base, size);
+}
+
+/**
+ * PERMUTATION_CHANGE_ARRAY - Advance an array to a new permutation
+ * @pi_: Current permutation
+ * @a_: Array
+ *
+ * As permutation_change_array(), but operate on array @a_, which must
+ * be a C array declared with the same number of elements as @pi_.
+ */
+#define PERMUTATION_CHANGE_ARRAY(pi_, a_) \
+ (permutation_change_array_check_((pi_), (a_), \
+ sizeof((a_)[0]), ARRAY_SIZE(a_)))
+
+#endif /* CCAN_PERMUTATION_H */
diff --git a/ccan/permutation/test/api.c b/ccan/permutation/test/api.c
new file mode 100644
index 0000000..ab54eb8
--- /dev/null
+++ b/ccan/permutation/test/api.c
@@ -0,0 +1,146 @@
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/permutation/permutation.h>
+#include <ccan/tap/tap.h>
+
+#define MAX_ITEMS 10
+
+#define PERMUTE(pi, arr) \
+ do { \
+ ok1(PERMUTATION_CHANGE_ARRAY((pi), (arr))); \
+ } while (0)
+
+#define CHECK_ORDER(a, t, ...) \
+ do { \
+ t cmp[] = { __VA_ARGS__ }; \
+ ok1(memcmp((a), cmp, sizeof(cmp)) == 0); \
+ } while (0)
+
+#define WORDLEN 6
+typedef char word[WORDLEN];
+
+int main(void)
+{
+ struct permutation *pi;
+ int single = 12345;
+ char pair[] = { 'P', 'Q' };
+ uint16_t triple[] = {7, 9, 1000};
+ word four[] = {"ZERO", "ONE", "TWO", "THREE"};
+ int i;
+
+ plan_tests(2 * permutation_count(1) + 1
+ + 2 * permutation_count(2) + 1
+ + 2 * permutation_count(3) + 1
+ + 2 * permutation_count(4) + 1
+ + MAX_ITEMS + 1);
+
+ /* One */
+ pi = permutation_new(1);
+ CHECK_ORDER(&single, int, 12345);
+ ok1(!permutation_change_array(pi, &single, sizeof(single)));
+ CHECK_ORDER(&single, int, 12345);
+ free(pi);
+
+ /* Pair */
+ pi = PERMUTATION_NEW(pair);
+ CHECK_ORDER(pair, char, 'P', 'Q');
+ PERMUTE(pi, pair);
+ CHECK_ORDER(pair, char, 'Q', 'P');
+ ok1(!PERMUTATION_CHANGE_ARRAY(pi, pair));
+ CHECK_ORDER(pair, char, 'Q', 'P');
+ free(pi);
+
+ /* Triple */
+ pi = PERMUTATION_NEW(triple);
+ CHECK_ORDER(triple, uint16_t, 7, 9, 1000);
+ PERMUTE(pi, triple);
+ CHECK_ORDER(triple, uint16_t, 7, 1000, 9);
+ PERMUTE(pi, triple);
+ CHECK_ORDER(triple, uint16_t, 1000, 7, 9);
+ PERMUTE(pi, triple);
+ CHECK_ORDER(triple, uint16_t, 1000, 9, 7);
+ PERMUTE(pi, triple);
+ CHECK_ORDER(triple, uint16_t, 9, 1000, 7);
+ PERMUTE(pi, triple);
+ CHECK_ORDER(triple, uint16_t, 9, 7, 1000);
+ ok1(!PERMUTATION_CHANGE_ARRAY(pi, triple));
+ CHECK_ORDER(triple, uint16_t, 9, 7, 1000);
+ free(pi);
+
+ /* Four */
+ pi = PERMUTATION_NEW(four);
+ CHECK_ORDER(four, word, "ZERO", "ONE", "TWO", "THREE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ZERO", "ONE", "THREE", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ZERO", "THREE", "ONE", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "ZERO", "ONE", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "ZERO", "TWO", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ZERO", "THREE", "TWO", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ZERO", "TWO", "THREE", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ZERO", "TWO", "ONE", "THREE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "ZERO", "ONE", "THREE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "ZERO", "THREE", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "THREE", "ZERO", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "TWO", "ZERO", "ONE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "TWO", "ONE", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "THREE", "ONE", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "ONE", "THREE", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "TWO", "ONE", "ZERO", "THREE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "TWO", "ZERO", "THREE");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "TWO", "THREE", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "THREE", "TWO", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "ONE", "TWO", "ZERO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "THREE", "ONE", "ZERO", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "THREE", "ZERO", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "ZERO", "THREE", "TWO");
+ PERMUTE(pi, four);
+ CHECK_ORDER(four, word, "ONE", "ZERO", "TWO", "THREE");
+ ok1(!PERMUTATION_CHANGE_ARRAY(pi, four));
+ CHECK_ORDER(four, word, "ONE", "ZERO", "TWO", "THREE");
+ free(pi);
+
+ for (i = 0; i <= MAX_ITEMS; i++) {
+ uint64_t nperms = 1;
+
+ diag("Counting permutations of %d\n", i);
+
+ pi = permutation_new(i);
+ while (permutation_change(pi))
+ nperms++;
+
+ ok(nperms == permutation_count(i),
+ "%"PRId64" permutations of %d (%d! == %"PRId64")",
+ nperms, i, i, permutation_count(i));
+ free(pi);
+ }
+
+ /* This exits depending on whether all tests passed */
+
+ return exit_status();
+}
--
2.4.3
More information about the ccan
mailing list