/* * Copyright (c) 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* A test for for functions and macros declared in heap.h. */ #include #undef NDEBUG #include "heap.h" #include #include #include #include #include "command-line.h" #include "ovstest.h" #include "random.h" #include "util.h" /* Sample heap element. */ struct element { uint32_t full_pri; struct heap_node heap_node; }; static struct element * element_from_heap_node(const struct heap_node *node) { return CONTAINER_OF(node, struct element, heap_node); } static int compare_uint32s(const void *a_, const void *b_) { const uint32_t *a = a_; const uint32_t *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'heap' is internally consistent and contains all 'n' of the * 'priorities'. */ static void check_heap(const struct heap *heap, const uint32_t priorities[], size_t n) { uint32_t *priorities_copy; uint32_t *elements_copy; struct element *element; size_t i; assert(heap_count(heap) == n); assert(heap_is_empty(heap) == !n); if (n > 0) { assert(heap_max(heap) == heap->array[1]); } /* Check indexes. */ for (i = 1; i <= n; i++) { assert(heap->array[i]->idx == i); } /* Check that priority values are internally consistent. */ for (i = 1; i <= n; i++) { element = element_from_heap_node(heap->array[i]); assert(element->heap_node.priority == (element->full_pri >> 16)); } /* Check the heap property. */ for (i = 1; i <= n; i++) { size_t parent = heap_parent__(i); size_t left = heap_left__(i); size_t right = heap_right__(i); if (parent >= 1) { assert(heap->array[parent]->priority >= heap->array[i]->priority); } if (left <= n) { assert(heap->array[left]->priority <= heap->array[i]->priority); } if (right <= n) { assert(heap->array[right]->priority <= heap->array[i]->priority); } } /* Check that HEAP_FOR_EACH iterates all the nodes in order. */ i = 0; HEAP_FOR_EACH (element, heap_node, heap) { assert(i < n); assert(&element->heap_node == heap->array[i + 1]); i++; } assert(i == n); priorities_copy = xmemdup(priorities, n * sizeof *priorities); elements_copy = xmalloc(n * sizeof *priorities); i = 0; HEAP_FOR_EACH (element, heap_node, heap) { elements_copy[i++] = element->heap_node.priority; } qsort(priorities_copy, n, sizeof *priorities_copy, compare_uint32s); qsort(elements_copy, n, sizeof *elements_copy, compare_uint32s); for (i = 0; i < n; i++) { assert((priorities_copy[i] >> 16) == elements_copy[i]); } free(priorities_copy); free(elements_copy); } static void shuffle(uint32_t *p, size_t n) { for (; n > 1; n--, p++) { uint32_t *q = &p[random_range(n)]; uint32_t tmp = *p; *p = *q; *q = tmp; } } /* Prints the values in 'heap', plus 'name' as a title. */ static void OVS_UNUSED print_heap(const char *name, struct heap *heap) { struct element *e; printf("%s:", name); HEAP_FOR_EACH (e, heap_node, heap) { printf(" %"PRIu32":%"PRIu32, e->full_pri >> 16, e->full_pri & 0xffff); } printf("\n"); } static int factorial(int n_items) { int n, i; n = 1; for (i = 2; i <= n_items; i++) { n *= i; } return n; } static void swap(uint32_t *a, uint32_t *b) { uint32_t tmp = *a; *a = *b; *b = tmp; } static void reverse(uint32_t *a, int n) { int i; for (i = 0; i < n / 2; i++) { int j = n - (i + 1); swap(&a[i], &a[j]); } } static bool next_permutation(uint32_t *a, int n) { int k; for (k = n - 2; k >= 0; k--) { if ((a[k] >> 16) < (a[k + 1] >> 16)) { int l; for (l = n - 1; ; l--) { if ((a[l] >> 16) > (a[k] >> 16)) { swap(&a[k], &a[l]); reverse(a + (k + 1), n - (k + 1)); return true; } } } } return false; } static void test_insert_delete__(struct element *elements, const uint32_t *insert, const uint32_t *delete, size_t n) { struct heap heap; size_t i; heap_init(&heap); check_heap(&heap, NULL, 0); for (i = 0; i < n; i++) { uint32_t priority = insert[i]; elements[i].full_pri = priority; heap_insert(&heap, &elements[i].heap_node, priority >> 16); check_heap(&heap, insert, i + 1); } for (i = 0; i < n; i++) { struct element *element; HEAP_FOR_EACH (element, heap_node, &heap) { if (element->full_pri == delete[i]) { goto found; } } OVS_NOT_REACHED(); found: heap_remove(&heap, &element->heap_node); check_heap(&heap, delete + i + 1, n - (i + 1)); } heap_destroy(&heap); } static void test_insert_delete_raw__(struct element *elements, const uint32_t *insert, unsigned int insert_pattern, const uint32_t *delete, unsigned int delete_pattern, size_t n) { struct heap heap; size_t i; heap_init(&heap); check_heap(&heap, NULL, 0); for (i = 0; i < n; i++) { uint32_t priority = insert[i]; elements[i].full_pri = priority; heap_raw_insert(&heap, &elements[i].heap_node, priority >> 16); if (insert_pattern & (1u << i)) { heap_rebuild(&heap); check_heap(&heap, insert, i + 1); } } for (i = 0; i < n; i++) { struct element *element; HEAP_FOR_EACH (element, heap_node, &heap) { if (element->full_pri == delete[i]) { goto found; } } OVS_NOT_REACHED(); found: heap_raw_remove(&heap, &element->heap_node); if (delete_pattern & (1u << i)) { heap_rebuild(&heap); check_heap(&heap, delete + i + 1, n - (i + 1)); } } heap_destroy(&heap); } static void test_heap_insert_delete_same_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete__(elements, insert, insert, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_reverse_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; uint32_t delete[N_ELEMS]; n_permutations++; for (i = 0; i < N_ELEMS; i++) { delete[N_ELEMS - i - 1] = insert[i]; } test_insert_delete__(elements, insert, delete, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_every_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 5 }; uint32_t insert[N_ELEMS]; int outer_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } outer_permutations = 0; do { struct element elements[N_ELEMS]; uint32_t delete[N_ELEMS]; int inner_permutations; outer_permutations++; for (i = 0; i < N_ELEMS; i++) { delete[i] = i << 16; } inner_permutations = 0; do { inner_permutations++; test_insert_delete__(elements, insert, delete, N_ELEMS); } while (next_permutation(delete, N_ELEMS)); assert(inner_permutations == factorial(N_ELEMS)); } while (next_permutation(insert, N_ELEMS)); assert(outer_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_same_order_with_dups(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; unsigned int pattern; size_t i; for (pattern = 0; pattern < (1u << N_ELEMS); pattern += 2) { int n_permutations, expected_permutations; uint32_t insert[N_ELEMS]; int j; j = 0; for (i = 0; i < N_ELEMS; i++) { if (i && !(pattern & (1u << i))) { j++; } insert[i] = (j << 16) | i; } expected_permutations = factorial(N_ELEMS); for (i = 0; i < N_ELEMS; ) { j = i + 1; if (pattern & (1u << i)) { for (; j < N_ELEMS; j++) { if (!(pattern & (1u << j))) { break; } } expected_permutations /= factorial(j - i + 1); } i = j; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete__(elements, insert, insert, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == expected_permutations); } } static void test_heap_raw_insert(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete_raw__(elements, insert, 1u << (N_ELEMS - 1), insert, UINT_MAX, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_raw_delete(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 16 }; uint32_t insert[N_ELEMS]; uint32_t delete[N_ELEMS]; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; delete[i] = i << 16; } for (i = 0; i < 1000; i++) { struct element elements[N_ELEMS]; shuffle(insert, N_ELEMS); shuffle(delete, N_ELEMS); test_insert_delete_raw__(elements, insert, 0, delete, (1u << (N_ELEMS - 1)) | (1u << (N_ELEMS / 2)), N_ELEMS); } } static const struct ovs_cmdl_command commands[] = { { "insert-delete-same-order", NULL, 0, 0, test_heap_insert_delete_same_order, OVS_RO }, { "insert-delete-reverse-order", NULL, 0, 0, test_heap_insert_delete_reverse_order, OVS_RO }, { "insert-delete-every-order", NULL, 0, 0, test_heap_insert_delete_every_order, OVS_RO }, { "insert-delete-same-order-with-dups", NULL, 0, 0, test_heap_insert_delete_same_order_with_dups, OVS_RO }, { "raw-insert", NULL, 0, 0, test_heap_raw_insert, OVS_RO }, { "raw-delete", NULL, 0, 0, test_heap_raw_delete, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static void test_heap_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-heap", test_heap_main);