pax_global_header00006660000000000000000000000064126657157250014532gustar00rootroot0000000000000052 comment=560ed785a0687b35301a8c23b8bb9fc06b69d827 BitArray-2.0/000077500000000000000000000000001266571572500131105ustar00rootroot00000000000000BitArray-2.0/.gitignore000066400000000000000000000002341266571572500150770ustar00rootroot00000000000000dev/bartest dev/bit_array_generate dev/bit_array_test dev/bitlock_test dev/bitlock_try_test dev/core examples/example_c examples/example_cpp *.dump *.a *.o BitArray-2.0/.travis.yml000066400000000000000000000007521266571572500152250ustar00rootroot00000000000000# Control file for continuous integration testing at http://travis-ci.org/ language: c compiler: - clang - gcc os: - linux - osx sudo: false install: make all script: make test # Gitter notifications notifications: webhooks: urls: - https://webhooks.gitter.im/e/6945dcbee7845a3c8966 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: false # default: false BitArray-2.0/AUTHORS000066400000000000000000000001201266571572500141510ustar00rootroot00000000000000Isaac Turner Diego Caro Sergey Filippov Chris J. Kiick BitArray-2.0/LICENSE000066400000000000000000000144731266571572500141260ustar00rootroot00000000000000Statement of Purpose -------------------- The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. ----------------------- 1. *Copyright and Related Rights.* A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: - the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; - moral rights retained by the original author(s) and/or performer(s); - publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; - rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; - rights protecting the extraction, dissemination, use and reuse of data in a Work; - database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and - other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. *Waiver.* To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. *Public License Fallback.* Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. *Limitations and Disclaimers.* - No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. - Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. - Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. - Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. BitArray-2.0/Makefile000066400000000000000000000015731266571572500145560ustar00rootroot00000000000000CC ?= gcc PLATFORM := $(shell uname) COMPILER := $(shell ($(CC) -v 2>&1) | tr A-Z a-z ) ifdef DEBUG OPT = -O0 -DDEBUG=1 --debug -g -ggdb else ifneq (,$(findstring gcc,$(COMPILER))) OPT = -O4 TGTFLAGS = -fwhole-program else OPT = -O3 endif endif CFLAGS = -Wall -Wextra -Wc++-compat -I. $(OPT) OBJFLAGS = -fPIC all: libbitarr.a dev examples bit_array.o: bit_array.c bit_array.h bit_macros.h libbitarr.a: bit_array.o ar -csru libbitarr.a bit_array.o %.o: %.c %.h $(CC) $(CFLAGS) $(OBJFLAGS) -c $< -o $@ dev: libbitarr.a bit_macros.h cd dev && $(MAKE) examples: libbitarr.a cd examples && $(MAKE) test: libbitarr.a cd dev && $(MAKE) test cd examples && $(MAKE) test clean: rm -rf libbitarr.a *.o *.dSYM *.greg cd dev && $(MAKE) clean cd examples && $(MAKE) clean # Comment this line out to keep .o files .INTERMEDIATE: $(OBJS) .PHONY: all clean test examples dev BitArray-2.0/README.md000066400000000000000000000556761266571572500144120ustar00rootroot00000000000000**C code for bit arrays** ========================= https://github.com/noporpoise/BitArray/ License: Public Domain, no warranty Isaac Turner [![Build Status](https://travis-ci.org/noporpoise/BitArray.png?branch=master)](https://travis-ci.org/noporpoise/BitArray) About ===== Bit arrays are arrays of bits (values zero or one). This is a convenient and efficient implementation for C/C++. Arrays can be enlarged or shrunk as needed. Bit arrays are initialised to zero when created or extended. All operations have their bounds checked - an "Out of bounds" error is printed if you try to access a bit with index >= length. Arrays of length 0 are permitted. Indices must be >= 0. Please get in touch if you have suggestions / requests / bugs. Adapted from: http://stackoverflow.com/a/2633584/431087 Build ===== To build the library: make To build and run the test code: make test Using bit_array in your code ============================ You are welcome to bundle bit_array with your own code. Add to the top of your code: #include "bit_array.h" Add to your compiler arguments: BIT_ARR_PATH=path/to/bit_array/ gcc ... -I$(BIT_ARR_PATH) -L$(BIT_ARR_PATH) -lbitarr Shorter function names are provided in `bar.h`, which can be included instead of `bit_array.h`: #include "bar.h" Thread safety ------------- You cannot safely access the same BitArray in multiple threads at once. Use a lock to protect BitArray objects. The same methods can be safely called in separate threads as long as they are not accessing the same BitArray struct. Basics ------ Constructor - create a new bit array of length nbits BIT_ARRAY* bit_array_create(bit_index_t nbits) Destructor - free the memory used for a bit array void bit_array_free(BIT_ARRAY* bitarray) Alternatively, allocate / free using an existing struct BIT_ARRAY* bit_array_alloc(BIT_ARRAY* bitarr, bit_index_t nbits) void bit_array_dealloc(BIT_ARRAY* bitarr) Get length of bit array bit_index_t bit_array_length(const BIT_ARRAY* bit_arr) Change the size of a bit array. Enlarging an array will add zeros to the end of it. Returns 1 on success, 0 on failure (e.g. not enough memory) char bit_array_resize(BIT_ARRAY* bitarr, bit_index_t new_num_of_bits) Set/Get bits ------------ Get the value of a bit (returns 0 or 1) char bit_array_get_bit(const BIT_ARRAY* bitarr, bit_index_t b) Set a bit (to 1) at position `b` void bit_array_set_bit(BIT_ARRAY* bitarr, bit_index_t b) Clear a bit (to 0) at position `b` void bit_array_clear_bit(BIT_ARRAY* bitarr, bit_index_t b) Toggle a bit. If bit is 0 change to 1; if bit is 1 change to 0. Also known as a complement function. void bit_array_toggle_bit(BIT_ARRAY* bitarr, bit_index_t b) Assign a value to a bit. If `c != 0` then set bit; otherwise clear bit. void bit_array_assign_bit(BIT_ARRAY* bitarr, bit_index_t b, char c) Fast MACROs ----------- You can also use the following which are implemented as MACROs without bounds checking: bit_array_get(BIT_ARRAY *arr, bit_index_t i) bit_array_set(BIT_ARRAY *arr, bit_index_t i) bit_array_clear(BIT_ARRAY *arr, bit_index_t i) bit_array_toggle(BIT_ARRAY *arr, bit_index_t i) bit_array_assign(BIT_ARRAY *arr, bit_index_t i, char c) Get a word_t with the bottom `nbits` set to 1, the rest to 0: word_t BIT_MASK(int nbits) Combine two words with a mask `((a & abits) | (b & ~abits))`: word_t BIT_MASK_MERGE(word_t a, word_t b, int abits) Set, clear and toggle several bits ---------------------------------- Note: variable args are of type unsigned int Set multiple bits at once. void bit_array_set_bits(BIT_ARRAY* bitarr, size_t n, ...) // e.g. set bits 1,20,31: bit_array_set_bits(bitarr, 3, 1,20,31); Clear multiple bits at once. void bit_array_clear_bits(BIT_ARRAY* bitarr, size_t n, ...) // e.g. clear bits 1,20,31: bit_array_clear_bits(bitarr, 3, 1,20,31); Toggle multiple bits at once void bit_array_toggle_bits(BIT_ARRAY* bitarr, size_t n, ...) // e.g. toggle bits 1,20,31: bit_array_toggle_bits(bitarr, 3, 1,20,31); Set, clear and toggle a region ------------------------------ Clear all the bits in the region `start` to `start+length-1` inclusive void bit_array_clear_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length) Set all the bits in the region `start` to `start+length-1` inclusive void bit_array_set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length) Toggle all the bits in the region `start` to `start+length-1` inclusive void bit_array_toggle_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length) Set, clear and toggle all bits ------------------------------ Set all bits in this array to 0 void bit_array_clear_all(BIT_ARRAY* bitarr) Set all bits in this array to 1 void bit_array_set_all(BIT_ARRAY* bitarr) Set all 1 bits to 0, and all 0 bits to 1 (i.e. flip all the bits) void bit_array_toggle_all(BIT_ARRAY* bitarr) Get / set a word ---------------- Get a word of a given size. First bit is in the least significant bit position. Index `start` must be within the range of the bit array (0 <= x < length) uint64_t bit_array_get_word64(const BIT_ARRAY* bitarr, bit_index_t start) uint32_t bit_array_get_word32(const BIT_ARRAY* bitarr, bit_index_t start) uint16_t bit_array_get_word16(const BIT_ARRAY* bitarr, bit_index_t start) uint8_t bit_array_get_word8 (const BIT_ARRAY* bitarr, bit_index_t start) uint64_t bit_array_get_wordn (const BIT_ARRAY* bitarr, bit_index_t start, int n) Set 64 bits at once from a particular start position void bit_array_set_word64(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word) void bit_array_set_word32(BIT_ARRAY* bitarr, bit_index_t start, uint32_t word) void bit_array_set_word16(BIT_ARRAY* bitarr, bit_index_t start, uint16_t word) void bit_array_set_word8 (BIT_ARRAY* bitarr, bit_index_t start, uint8_t word) void bit_array_set_wordn (BIT_ARRAY* bitarr, bit_index_t start, uint64_t word, int n) Count bits set -------------- Get the number of bits set (hamming weight) bit_index_t bit_array_num_bits_set(const BIT_ARRAY* bitarr) Get the number of bits set in on array and not the other. This is equivalent to hamming weight of the XOR of the two arrays. e.g. 10101 vs 00111 => hamming distance 2 (XOR is 10010) bit_index_t bit_array_hamming_distance(const BIT_ARRAY* arr1, const BIT_ARRAY* arr2) Get the number of bits not set (`length - hamming weight`) bit_index_t bit_array_num_bits_cleared(const BIT_ARRAY* bitarr) Find the index of the first bit that is set. Returns 1 if a bit is set, otherwise 0. Index of first set bit is stored in the integer pointed to by `result`. If no bits are set, value at `result` is not changed and zero is returned. char bit_array_find_first_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result) Find the index of the first bit that is clear. Returns 1 if a bit is clear, otherwise 0. Index of first clear bit is stored in the integer pointed to by `result`. If no bits are clear, zero is returned. char bit_array_find_first_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result) Find the index of the last bit that is set. Returns 1 if a bit is set, otherwise 0. Index of last set bit is stored in the integer pointed to by `result`. If no bits are set, value at `result` is not changed and zero is returned. char bit_array_find_last_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result) Find the index of the last bit that is NOT set. Returns 1 if a bit is zero, otherwise 0. Index of last zero bit is stored in the integer pointed to by `result`. If no bits are zero, value at `result` is not changed and zero is returned. char bit_array_find_last_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result) Find the index of the next bit that is set, at or after `offset`. Returns 1 if a bit is set, otherwise 0. Index of next set bit is stored in the integer pointed to by `result`. If no next bit is set, value at `result` is not changed and 0 is returned. char bit_array_find_next_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) Find the index of the next bit that is clear, at or after `offset`. Returns 1 if a bit is clear, otherwise 0. Index of next clear bit is stored in the integer pointed to by `result`. If no next bit is clear, 0 is returned. char bit_array_find_next_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) Find the index of the previous bit that is set, before `offset`. Note: 'before' does not include `offset`. Returns 1 if a bit is set, otherwise 0 Index of previous set bit is stored in the integer pointed to by `result` If no previous bit is set, value at `result` is not changed char bit_array_find_prev_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) Find the index of the previous bit that is NOT set, before `offset`. Note: 'before' does not include `offset`. Returns 1 if a bit is clear, otherwise 0 Index of previous zero bit is stored in the integer pointed to by `result` If no previous bit is zero, value at `result` is not changed char bit_array_find_prev_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) Parity / Permutation -------------------- Get parity: returns 1 if odd number of bits set, 0 if even. char bit_array_parity(const BIT_ARRAY* bitarr) Get the next permutation of an array with a fixed size and given number of bits set. Also known as next lexicographic permutation. Given a bit array find the next lexicographic orginisation of the bits Number of possible combinations given by `size choose bits_set` where `bits_set` is the result of `bit_array_num_bits_set(bitarr)`. Example: 00011 -> 00101 -> 00110 -> 01001 -> 01010 -> 01100 -> 10001 -> 10010 -> 10100 -> 11000 -> 00011 (back to start) void bit_array_next_permutation(BIT_ARRAY* bitarr) Sorting ------- Put all the 0s before all the 1s void bit_array_sort_bits(BIT_ARRAY* bitarr) Put all the 1s before all the 0s void bit_array_sort_bits_rev(BIT_ARRAY* bitarr) String and printing functions ----------------------------- To convert to/from string representations of an array, '1' and '0' are used by default as on and off. Create a bit array from a string of '0's and '1's e.g. "01001010110". void bit_array_from_str(BIT_ARRAY* bitarr, const char* bitstr) Construct a BIT_ARRAY from a substring with given on and off characters. `left_to_right` determines the order in which bits are printed. Terminates string with '\0'. void bit_array_from_substr(BIT_ARRAY* bitarr, bit_index_t offset, const char* str, size_t len, const char *on, const char *off, char left_to_right) To string method. Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in length. Terminates string with '\0'. char* bit_array_to_str(const BIT_ARRAY* bitarr, char* str) To construct a string in reverse (highest bit on the left, lowest on the right) bit_array_to_str_rev(const BIT_ARRAY* bitarr, char* str) Get a string representations for a given region, using given on/off characters. `left_to_right` determines the order in which bits are printed. Note: does not null-terminate. void bit_array_to_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char on, char off, char left_to_right) Print this array to a file stream. Prints '0's and '1'. Doesn't print newline. void bit_array_print(const BIT_ARRAY* bitarr, FILE* fout) Print a string representations for a given region, using given on/off characters. `left_to_right` determines the order in which bits are printed. void bit_array_print_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char on, char off, char left_to_right) Decimal ------- Get bit array as decimal str e.g. 0b1101 -> "13". `len` is the length of str char array. `bit_array_to_decimal()` write at most `len-1` chars to `str`. Returns the number of characters that would have been written to str -- return is the same as strlen(str) upon success. size_t bit_array_to_decimal(const BIT_ARRAY *bitarr, char *str, size_t len) Example usage: char str[10]; size_t len = bit_array_to_decimal(arr, str, 10); if(len > 9) { // str wasn't big enough } Get bit array from decimal str (e.g. "13" -> 0b1101). Returns number of characters used size_t bit_array_from_decimal(BIT_ARRAY *bitarr, const char* decimal) Example usage: char *str = "1234"; BIT_ARRAY *bitarr = bit_array_create(0); size_t len = bit_array_from_decimal(bitarr, str); if(len < strlen(str)) { // Parsing ended prematurely (non-numeric characters encountered) } Hexidecimal ----------- Loads array from hex string Returns the number of bits loaded (will be chars rounded up to multiple of 8) (0 on failure) bit_index_t bit_array_from_hex(BIT_ARRAY* bitarr, bit_index_t offset, const char* str, size_t len) Returns number of characters written size_t bit_array_to_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char uppercase) Print bit array as hex size_t bit_array_print_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char uppercase) Clone/copy ---------- Copy a BIT_ARRAY struct and the data it holds - returns pointer to new object BIT_ARRAY* bit_array_clone(const BIT_ARRAY* bitarr) Copy bits from one array to another. Destination and source can be the same bit_array and src/dst regions can overlap void bit_array_copy(BIT_ARRAY* dst, bit_index_t dstindx, const BIT_ARRAY* src, bit_index_t srcindx, bit_index_t length) Logic operators and shifts -------------------------- Destination and source bit arrays must be of the same length, however they may point to the same object void bit_array_and(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2) void bit_array_or(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2) void bit_array_xor(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2) void bit_array_not(BIT_ARRAY* dest, const BIT_ARRAY* src) Shift array left/right with a given `fill` (0 or 1) void bit_array_shift_right(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) void bit_array_shift_left(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) To shift and add digits instead of losing data, use the extend left shift function: void bit_array_shift_left_extend(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) Circular or cycle shifts. Bits wrap around once shifted off the end void bit_array_cycle_right(BIT_ARRAY* bitarr, bit_index_t dist) void bit_array_cycle_left(BIT_ARRAY* bitarr, bit_index_t dist) Interleave bits --------------- Copy bits from two arrays into another, alternating between taking a bit from each. In other words, two arrays a,b,c,d and 1,2,3,4 -> a,1,b,2,c,3,d,4. Examples: * 0011 0000 -> 00001010 * 1111 0000 -> 10101010 * 0101 1010 -> 01100110 `dst` cannot point to the same bit array as `src1` or `src2`. However `src1` and `src2` may point to the same bit array. void bit_array_interleave(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) Reverse ------- Reverse the whole array or part of it. void bit_array_reverse(BIT_ARRAY* bitarr) void bit_array_reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length) Comparing --------- Comparison functions return: * > 0 iff bitarr1 > bitarr2 * 0 iff bitarr1 == bitarr2 * < 0 iff bitarr1 < bitarr2 Compare two bit arrays by value stored, with index 0 being the Least Significant Bit (LSB). Arrays do not have to be the same length. Example: ..0101 (5) > ...0011 (3) [index 0 is LSB at right hand side]. int bit_array_cmp(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2) Compare two bit arrays by value stored, with index 0 being the Most Significant Bit (MSB). Sorts on length if all zeros: (0,0) < (0,0,0) Arrays do not have to be the same length. Example: 10.. > 01.. [index 0 is MSB at left hand side] int bit_array_cmp_big_endian(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2) Compare `bitarr` with `(bitarr2 << pos)`. Does not use array length, only value stored. int bit_array_cmp_words(const BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *bitarr2) Compare value stored against an unsigned long (treats `bitarr` as large unsigned integer type): int bit_array_compare_num(BIT_ARRAY* bitarr, unsigned long value) Arithmetic ---------- Bit arrays can be interpretted as arbitrarily large unsigned integers. To do this the bit at index 0 is treated as the least significant bit. BitArrays provide functions for arithmetic between a BitArray & a long, and between BitArrays. Get the value of this number in an unsigned long. Returns 1 on sucess, 0 if value in array is too big. char bit_array_as_num(BIT_ARRAY* bitarr, unsigned long* result) (Note: see also `bit_array_compare_num(BIT_ARRAY*, unsigned long)`) Add to an array. `bitarr` will be extended if needed. void bit_array_add_uint64(BIT_ARRAY* bitarr, unsigned long value) Add `add` to `bitarr` at `pos` -- same as: bitarr + (add << pos) where pos can be bigger than the length of the array (bitarr will be resized) void bit_array_add_word(BIT_ARRAY *bitarr, bit_index_t pos, uint64_t add) Add `add << pos` to `bitarr` void bit_array_add_words(BIT_ARRAY *bitarr, bit_index_t pos, BIT_ARRAY *add) Subtract from an array. If `value` is greater than `bitarr`, `bitarr` is not changed and `0` is returned. Returns `1` on success, `0` if `value > bitarr` char bit_array_sub_uint64(BIT_ARRAY* bitarr, unsigned long value) Minus `minus << pos` from `bitarr` char bit_array_sub_words(BIT_ARRAY* bitarr, bit_index_t pos, BIT_ARRAY* minus) Multiply by some value void bit_array_mul_uint64(BIT_ARRAY *bitarr, uint64_t multiplier) Add two bit arrays together and store the result. `src1` and `src2` do not have to be the same length. `src1`, `src2` and `dst` can all be the same or different `BIT_ARRAY`s. If `dst` is shorter than either of `src1` or `src2`, it is enlarged to be as long as the longest. void bit_array_add(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) Subtract on BIT_ARRAY from another. `src1`, `src2` and `dst` can all be the same or different `BIT_ARRAY`s. If dst is shorter than src1, it will be extended to be as long as `src1`. `src1` must be greater than or equal to `src2` (`src1 >= src2`). void bit_array_subtract(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) dst = src1 * src2 Pointers cannot all point to the same BIT_ARRAY void bit_array_multiply(BIT_ARRAY *dst, BIT_ARRAY *src1, BIT_ARRAY *src2) Divide a BitArray by a BitArray; returns: * `quotient = dividend / divisor` * `dividend = dividend % divisor` Dividend is used to return the remainder. void bit_array_divide(BIT_ARRAY *dividend, BIT_ARRAY *quotient, BIT_ARRAY *divisor) Read/Write bit_array to a file ------------------------------ File format is [8 bytes: for number of elements in array][data]. Number of bytes of data is: `(int)((num_of_bits + 7) / 8)` -- i.e. `roundup(num_of_bits/8)` Saves bit array to a file. Returns the number of bytes written bit_index_t bit_array_save(const BIT_ARRAY* bitarr, FILE* f) Reads bit array from a file. `bitarr` is resized and filled with data from the file. Returns 1 on success, 0 on failure. char bit_array_load(BIT_ARRAY* bitarr, FILE* f) Hash Value ---------- Get a hash value for this array. Pass `seed` as `0` on first call, pass previous hash value if rehashing due to a collision. Uses Bob Jenkins hash lookup3 function (http://burtleburtle.net/bob/hash/index.html) uint64_t bit_array_hash(const BIT_ARRAY* bitarr, uint64_t seed) Randomness ---------- Set bits randomly with probability prob (where `0 <= prob <= 1`) void bit_array_random(BIT_ARRAY* bitarr, float prob) Shuffle the bits in an array randomly void bit_array_shuffle(BIT_ARRAY* bitarr) // e.g. If you want exactly 9 random bits set in an array, use: bit_array_set_region(arr, 0, 9); // set the first 9 bits bit_array_shuffle(arr); // shuffle the array Useful functions ---------------- The file `bit_macros.h` contains many useful macros for bit arrays. Simple bit array functions can be implemented with this file alone. Generalised 'binary to string' function. Adds bits to the string in order of lsb to msb e.g. 0b11010 (26 in decimal) would come out as "01011" char* bit_array_word2str(const void *ptr, size_t num_of_bits, char *str); // Same as above but in reverse char* bit_array_word2str_rev(const void *ptr, size_t num_of_bits, char *str); For those who hate all that typing: the file "bar.h" contains macros to supply short "bar*" names for the most used bit array operations. This is meant to be similar to the "str*" function names for string manipulation. Constants --------- `BIT_INDEX_MIN` and `BIT_INDEX_MAX` define the min and max values of datatype `bit_index_t`. These are defined as `0` and `2^63 - 1`. Contributing ============ Please feel free to submit issues and pull requests. I appreciate bug reports. Methods are named: * `_name()` indicates only used internally * `bit_array_name()` exported as is Testing on different platforms is especially appreciated. I only have access to Mac OS X and Linux. License ======= This software is in the *Public Domain*. That means you can do whatever you like with it. That includes being used in proprietary products without attribution or restrictions. There are no warranties and there may be bugs. Formally we are using CC0 - a Creative Commons license to place this work in the public domain. A copy of CC0 is in the LICENSE file. "CC0 is a public domain dedication from Creative Commons. A work released under CC0 is dedicated to the public domain to the fullest extent permitted by law. If that is not possible for any reason, CC0 also provides a lax, permissive license as a fallback. Both public domain works and the lax license provided by CC0 are compatible with the GNU GPL." - http://www.gnu.org/licenses/license-list.html#CC0 Development =========== To do: * search function: `int bit_array_search(const BIT_ARRAY *arr, const BIT_ARRAY *query);` * windows support * 32 bit support * faster multiply / divide? (i.e. Karatsuba) BitArray-2.0/bar.h000066400000000000000000000105521266571572500140300ustar00rootroot00000000000000/* bar.h project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sept 2014 */ // shorten the names of some of the bit_array functions to be more // like the str* function names. The prefix "bar" is used to represent // bit_array and is analogous to "str". #ifndef BAR_HEADER_SEEN #define BAR_HEADER_SEEN #include "bit_array.h" #define bar BIT_ARRAY #define barcreate bit_array_create #define bardestroy bit_array_free #define baralloc bit_array_alloc #define barfree bit_array_dealloc #define barlen bit_array_length #define barsize bit_array_resize #define barcap bit_array_ensure_size // These five are MACROs #define barget bit_array_get #define barset bit_array_set #define barclr bit_array_clear #define barflip bit_array_toggle #define barmake bit_array_assign /* Functions instead of macros bars* => s for safe */ #define barsget bit_array_get_bit #define barsset bit_array_set_bit #define barsclr bit_array_clear_bit #define barsflip bit_array_toggle_bit #define barsmake bit_array_assign_bit /* "resize" functions barr*: automatically enlarge array if needed */ #define barrget bit_array_rget #define barrset bit_array_rset #define barrclr bit_array_rclear #define barrflip bit_array_rtoggle #define barrmake bit_array_rassign #define barsetn bit_array_set_bits #define barclrn bit_array_clear_bits #define barflipn bit_array_toggle_bits #define barsetr bit_array_set_region #define barclrr bit_array_clear_region #define barflipr bit_array_toggle_region #define barfill bit_array_set_all #define barzero bit_array_clear_all #define bartogl bit_array_toggle_all /* gw "get word" */ #define bargw64 bit_array_get_word64 #define bargw32 bit_array_get_word32 #define bargw16 bit_array_get_word16 #define bargw8 bit_array_get_word8 #define bargwn bit_array_get_wordn /* sw "set word" */ #define barsw64 bit_array_set_word64 #define barsw32 bit_array_set_word32 #define barsw16 bit_array_set_word16 #define barswn bit_array_set_wordn #define barncpy bit_array_copy #define barcpy bit_array_copy_all #define bardup bit_array_clone #define barpopc bit_array_num_bits_set #define barzeros bit_array_num_bits_cleared #define bardist bit_array_hamming_distance #define barparity bit_array_parity #define barfns bit_array_find_next_set_bit #define barfps bit_array_find_prev_set_bit #define barffs bit_array_find_first_set_bit #define barfls bit_array_find_last_set_bit #define barfnz bit_array_find_next_clear_bit #define barfpz bit_array_find_prev_clear_bit #define barffz bit_array_find_first_clear_bit #define barflz bit_array_find_last_clear_bit #define barsort bit_array_sort_bits #define barsortr bit_array_sort_bits_rev #define barand bit_array_and #define baror bit_array_or #define barxor bit_array_xor #define barnot bit_array_not #define barcmp bit_array_cmp #define barcmpbe bit_array_cmp_big_endian #define barcmpw bit_array_cmp_words #define barcmp64 bit_array_cmp_uint64 #define barshr bit_array_shift_right #define barshl bit_array_shift_left #define bareshl bit_array_shift_left_extend #define barcycr bit_array_cycle_right #define barcycl bit_array_cycle_left #define barmix bit_array_interleave #define barrev bit_array_reverse #define barrevr bit_array_reverse_region #define bar2num bit_array_as_num /* Add/sub/mult/div a bit array with: */ /* _i unsigned integer, _si shifted integer, _sb shifted bitarray */ #define baraddi bit_array_add_uint64 #define baraddsi bit_array_add_word #define baraddsb bit_array_add_words #define barsubi bit_array_sub_uint64 #define barsubsi bit_array_sub_word #define barsubsb bit_array_sub_words #define barmuli bit_array_mul_uint64 #define bardivi bit_array_div_uint64 /* arguments are both bit arrays */ #define baradd bit_array_add #define barsub bit_array_subtract #define barmul bit_array_multiply #define bardiv bit_array_divide #define barsave bit_array_save #define barload bit_array_load #define barhash bit_array_hash #define barrand bit_array_random #define barshfl bit_array_shuffle #define barperm bit_array_next_permutation #endif /* BAR_HEADER_SEEN */ BitArray-2.0/bit_array.c000066400000000000000000002424371266571572500152440ustar00rootroot00000000000000/* bit_array.c project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Aug 2014 */ // 64 bit words // Array length can be zero // Unused top bits must be zero #include #include #include #include // ULONG_MAX #include #include // needed for abort() #include // memset() #include #include // needed for seeding rand() #include // need for getpid() for seeding rand number #include // need for tolower() #include // perror() #include // for seeding random // Windows includes #if defined(_WIN32) #include #endif #include "bit_array.h" #include "bit_macros.h" // // Tables of constants // // byte reverse look up table static const word_t reverse_table[256] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, }; // Morton table for interleaving bytes static const word_t morton_table0[256] = { 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555, }; // Morton table for interleaving bytes, shifted left 1 bit static const word_t morton_table1[256] = { 0x0000, 0x0002, 0x0008, 0x000A, 0x0020, 0x0022, 0x0028, 0x002A, 0x0080, 0x0082, 0x0088, 0x008A, 0x00A0, 0x00A2, 0x00A8, 0x00AA, 0x0200, 0x0202, 0x0208, 0x020A, 0x0220, 0x0222, 0x0228, 0x022A, 0x0280, 0x0282, 0x0288, 0x028A, 0x02A0, 0x02A2, 0x02A8, 0x02AA, 0x0800, 0x0802, 0x0808, 0x080A, 0x0820, 0x0822, 0x0828, 0x082A, 0x0880, 0x0882, 0x0888, 0x088A, 0x08A0, 0x08A2, 0x08A8, 0x08AA, 0x0A00, 0x0A02, 0x0A08, 0x0A0A, 0x0A20, 0x0A22, 0x0A28, 0x0A2A, 0x0A80, 0x0A82, 0x0A88, 0x0A8A, 0x0AA0, 0x0AA2, 0x0AA8, 0x0AAA, 0x2000, 0x2002, 0x2008, 0x200A, 0x2020, 0x2022, 0x2028, 0x202A, 0x2080, 0x2082, 0x2088, 0x208A, 0x20A0, 0x20A2, 0x20A8, 0x20AA, 0x2200, 0x2202, 0x2208, 0x220A, 0x2220, 0x2222, 0x2228, 0x222A, 0x2280, 0x2282, 0x2288, 0x228A, 0x22A0, 0x22A2, 0x22A8, 0x22AA, 0x2800, 0x2802, 0x2808, 0x280A, 0x2820, 0x2822, 0x2828, 0x282A, 0x2880, 0x2882, 0x2888, 0x288A, 0x28A0, 0x28A2, 0x28A8, 0x28AA, 0x2A00, 0x2A02, 0x2A08, 0x2A0A, 0x2A20, 0x2A22, 0x2A28, 0x2A2A, 0x2A80, 0x2A82, 0x2A88, 0x2A8A, 0x2AA0, 0x2AA2, 0x2AA8, 0x2AAA, 0x8000, 0x8002, 0x8008, 0x800A, 0x8020, 0x8022, 0x8028, 0x802A, 0x8080, 0x8082, 0x8088, 0x808A, 0x80A0, 0x80A2, 0x80A8, 0x80AA, 0x8200, 0x8202, 0x8208, 0x820A, 0x8220, 0x8222, 0x8228, 0x822A, 0x8280, 0x8282, 0x8288, 0x828A, 0x82A0, 0x82A2, 0x82A8, 0x82AA, 0x8800, 0x8802, 0x8808, 0x880A, 0x8820, 0x8822, 0x8828, 0x882A, 0x8880, 0x8882, 0x8888, 0x888A, 0x88A0, 0x88A2, 0x88A8, 0x88AA, 0x8A00, 0x8A02, 0x8A08, 0x8A0A, 0x8A20, 0x8A22, 0x8A28, 0x8A2A, 0x8A80, 0x8A82, 0x8A88, 0x8A8A, 0x8AA0, 0x8AA2, 0x8AA8, 0x8AAA, 0xA000, 0xA002, 0xA008, 0xA00A, 0xA020, 0xA022, 0xA028, 0xA02A, 0xA080, 0xA082, 0xA088, 0xA08A, 0xA0A0, 0xA0A2, 0xA0A8, 0xA0AA, 0xA200, 0xA202, 0xA208, 0xA20A, 0xA220, 0xA222, 0xA228, 0xA22A, 0xA280, 0xA282, 0xA288, 0xA28A, 0xA2A0, 0xA2A2, 0xA2A8, 0xA2AA, 0xA800, 0xA802, 0xA808, 0xA80A, 0xA820, 0xA822, 0xA828, 0xA82A, 0xA880, 0xA882, 0xA888, 0xA88A, 0xA8A0, 0xA8A2, 0xA8A8, 0xA8AA, 0xAA00, 0xAA02, 0xAA08, 0xAA0A, 0xAA20, 0xAA22, 0xAA28, 0xAA2A, 0xAA80, 0xAA82, 0xAA88, 0xAA8A, 0xAAA0, 0xAAA2, 0xAAA8, 0xAAAA, }; // // Macros // // WORD_SIZE is the number of bits per word // sizeof gives size in bytes (8 bits per byte) #define WORD_SIZE 64 // #define WORD_SIZE (sizeof(word_t) * 8) // POPCOUNT is number of bits set #if defined(_WIN32) // See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel static word_t __inline windows_popcount(word_t w) { w = w - ((w >> 1) & (word_t)~(word_t)0/3); w = (w & (word_t)~(word_t)0/15*3) + ((w >> 2) & (word_t)~(word_t)0/15*3); w = (w + (w >> 4)) & (word_t)~(word_t)0/255*15; c = (word_t)(w * ((word_t)~(word_t)0/255)) >> (sizeof(word_t) - 1) * 8; } static word_t __inline windows_parity(word_t w) { w ^= w >> 1; w ^= w >> 2; w = (w & 0x1111111111111111UL) * 0x1111111111111111UL; return (w >> 60) & 1; } #define POPCOUNT(x) windows_popcountl(x) #define PARITY(x) windows_parity(x) #else #define POPCOUNT(x) (unsigned)__builtin_popcountll(x) #define PARITY(x) (unsigned)__builtin_parityll(x) #endif #define MIN(a, b) (((a) <= (b)) ? (a) : (b)) #define MAX(a, b) (((a) >= (b)) ? (a) : (b)) // Make this a power of two #define INIT_CAPACITY_WORDS 2 // word of all 1s #define WORD_MAX (~(word_t)0) #define SET_REGION(arr,start,len) _set_region((arr),(start),(len),FILL_REGION) #define CLEAR_REGION(arr,start,len) _set_region((arr),(start),(len),ZERO_REGION) #define TOGGLE_REGION(arr,start,len) _set_region((arr),(start),(len),SWAP_REGION) // Have we initialised with srand() ? static char rand_initiated = 0; static void _seed_rand() { if(!rand_initiated) { // Initialise random number generator struct timeval time; gettimeofday(&time, NULL); srand((((time.tv_sec ^ getpid()) * 1000001) + time.tv_usec)); rand_initiated = 1; } } // // Common internal functions // #define bits_in_top_word(nbits) ((nbits) ? bitset64_idx((nbits) - 1) + 1 : 0) // Mostly used for debugging static inline void _print_word(word_t word, FILE* out) { word_offset_t i; for(i = 0; i < WORD_SIZE; i++) { fprintf(out, "%c", ((word >> i) & (word_t)0x1) == 0 ? '0' : '1'); } } // prints right to left static inline char* _word_to_str(word_t word, char str[WORD_SIZE+1]) __attribute__((unused)); static inline char* _word_to_str(word_t word, char str[WORD_SIZE+1]) { word_offset_t i; for(i = 0; i < WORD_SIZE; i++) { str[WORD_SIZE-i-1] = ((word >> i) & (word_t)0x1) == 0 ? '0' : '1'; } str[WORD_SIZE] = '\0'; return str; } // Used in debugging #ifdef DEBUG #define DEBUG_PRINT(msg,...) printf("[%s:%i] "msg, __FILE__, __LINE__, ##__VA_ARGS__); #define DEBUG_VALIDATE(a) validate_bitarr((a), __FILE__, __LINE__) #else #define DEBUG_PRINT(msg,...) #define DEBUG_VALIDATE(a) #endif void validate_bitarr(BIT_ARRAY *arr, const char *file, int lineno) { // Check top word is masked word_addr_t tw = arr->num_of_words == 0 ? 0 : arr->num_of_words - 1; bit_index_t top_bits = bits_in_top_word(arr->num_of_bits); int err = 0; if(arr->words[tw] > bitmask64(top_bits)) { _print_word(arr->words[tw], stderr); fprintf(stderr, "\n[%s:%i] Expected %i bits in top word[%i]\n", file, lineno, (int)top_bits, (int)tw); err = 1; } // Check num of words is correct word_addr_t num_words = roundup_bits2words64(arr->num_of_bits); if(num_words != arr->num_of_words) { fprintf(stderr, "\n[%s:%i] num of words wrong " "[bits: %i, word: %i, actual words: %i]\n", file, lineno, (int)arr->num_of_bits, (int)num_words, (int)arr->num_of_words); err = 1; } if(err) abort(); } // Reverse a word static inline word_t _reverse_word(word_t word) { word_t reverse = (reverse_table[(word) & 0xff] << 56) | (reverse_table[(word >> 8) & 0xff] << 48) | (reverse_table[(word >> 16) & 0xff] << 40) | (reverse_table[(word >> 24) & 0xff] << 32) | (reverse_table[(word >> 32) & 0xff] << 24) | (reverse_table[(word >> 40) & 0xff] << 16) | (reverse_table[(word >> 48) & 0xff] << 8) | (reverse_table[(word >> 56) & 0xff]); return reverse; } static inline void _mask_top_word(BIT_ARRAY* bitarr) { // Mask top word word_addr_t num_of_words = MAX(1, bitarr->num_of_words); word_offset_t bits_active = bits_in_top_word(bitarr->num_of_bits); bitarr->words[num_of_words-1] &= bitmask64(bits_active); } // // Get and set words (internal use only -- no bounds checking) // static inline word_t _get_word(const BIT_ARRAY* bitarr, bit_index_t start) { word_addr_t word_index = bitset64_wrd(start); word_offset_t word_offset = bitset64_idx(start); word_t result = bitarr->words[word_index] >> word_offset; word_offset_t bits_taken = WORD_SIZE - word_offset; // word_offset is now the number of bits we need from the next word // Check the next word has at least some bits if(word_offset > 0 && start + bits_taken < bitarr->num_of_bits) { result |= bitarr->words[word_index+1] << (WORD_SIZE - word_offset); } return result; } // Set 64 bits from a particular start position // Doesn't extend bit array static inline void _set_word(BIT_ARRAY* bitarr, bit_index_t start, word_t word) { word_addr_t word_index = bitset64_wrd(start); word_offset_t word_offset = bitset64_idx(start); if(word_offset == 0) { bitarr->words[word_index] = word; } else { bitarr->words[word_index] = (word << word_offset) | (bitarr->words[word_index] & bitmask64(word_offset)); if(word_index+1 < bitarr->num_of_words) { bitarr->words[word_index+1] = (word >> (WORD_SIZE - word_offset)) | (bitarr->words[word_index+1] & (WORD_MAX << word_offset)); } } // Mask top word _mask_top_word(bitarr); DEBUG_VALIDATE(bitarr); } static inline void _set_byte(BIT_ARRAY *bitarr, bit_index_t start, uint8_t byte) { word_t w = _get_word(bitarr, start); _set_word(bitarr, start, (w & ~(word_t)0xff) | byte); } // 4 bits static inline void _set_nibble(BIT_ARRAY *bitarr, bit_index_t start, uint8_t nibble) { word_t w = _get_word(bitarr, start); _set_word(bitarr, start, (w & ~(word_t)0xf) | nibble); } // Wrap around static inline word_t _get_word_cyclic(const BIT_ARRAY* bitarr, bit_index_t start) { word_t word = _get_word(bitarr, start); bit_index_t bits_taken = bitarr->num_of_bits - start; if(bits_taken < WORD_SIZE) { word |= (bitarr->words[0] << bits_taken); if(bitarr->num_of_bits < (bit_index_t)WORD_SIZE) { // Mask word to prevent repetition of the same bits word = word & bitmask64(bitarr->num_of_bits); } } return word; } // Wrap around static inline void _set_word_cyclic(BIT_ARRAY* bitarr, bit_index_t start, word_t word) { _set_word(bitarr, start, word); bit_index_t bits_set = bitarr->num_of_bits - start; if(bits_set < WORD_SIZE && start > 0) { word >>= bits_set; // Prevent overwriting the bits we've just set // by setting 'start' as the upper bound for the number of bits to write word_offset_t bits_remaining = MIN(WORD_SIZE - bits_set, start); word_t mask = bitmask64(bits_remaining); bitarr->words[0] = bitmask_merge(word, bitarr->words[0], mask); } } // // Fill a region (internal use only) // // FillAction is fill with 0 or 1 or toggle typedef enum {ZERO_REGION, FILL_REGION, SWAP_REGION} FillAction; static inline void _set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FillAction action) { if(length == 0) return; word_addr_t first_word = bitset64_wrd(start); word_addr_t last_word = bitset64_wrd(start+length-1); word_offset_t foffset = bitset64_idx(start); word_offset_t loffset = bitset64_idx(start+length-1); if(first_word == last_word) { word_t mask = bitmask64(length) << foffset; switch(action) { case ZERO_REGION: bitarr->words[first_word] &= ~mask; break; case FILL_REGION: bitarr->words[first_word] |= mask; break; case SWAP_REGION: bitarr->words[first_word] ^= mask; break; } } else { // Set first word switch(action) { case ZERO_REGION: bitarr->words[first_word] &= bitmask64(foffset); break; case FILL_REGION: bitarr->words[first_word] |= ~bitmask64(foffset); break; case SWAP_REGION: bitarr->words[first_word] ^= ~bitmask64(foffset); break; } word_addr_t i; // Set whole words switch(action) { case ZERO_REGION: for(i = first_word + 1; i < last_word; i++) bitarr->words[i] = (word_t)0; break; case FILL_REGION: for(i = first_word + 1; i < last_word; i++) bitarr->words[i] = WORD_MAX; break; case SWAP_REGION: for(i = first_word + 1; i < last_word; i++) bitarr->words[i] ^= WORD_MAX; break; } // Set last word switch(action) { case ZERO_REGION: bitarr->words[last_word] &= ~bitmask64(loffset+1); break; case FILL_REGION: bitarr->words[last_word] |= bitmask64(loffset+1); break; case SWAP_REGION: bitarr->words[last_word] ^= bitmask64(loffset+1); break; } } } // // Constructor // // If cannot allocate memory, set errno to ENOMEM, return NULL BIT_ARRAY* bit_array_alloc(BIT_ARRAY* bitarr, bit_index_t nbits) { bitarr->num_of_bits = nbits; bitarr->num_of_words = roundup_bits2words64(nbits); bitarr->capacity_in_words = MAX(8, roundup2pow(bitarr->num_of_words)); bitarr->words = (word_t*)calloc(bitarr->capacity_in_words, sizeof(word_t)); if(bitarr->words == NULL) { errno = ENOMEM; return NULL; } return bitarr; } void bit_array_dealloc(BIT_ARRAY* bitarr) { free(bitarr->words); memset(bitarr, 0, sizeof(BIT_ARRAY)); } // If cannot allocate memory, set errno to ENOMEM, return NULL BIT_ARRAY* bit_array_create(bit_index_t nbits) { BIT_ARRAY* bitarr = (BIT_ARRAY*)malloc(sizeof(BIT_ARRAY)); // error if could not allocate enough memory if(bitarr == NULL || bit_array_alloc(bitarr, nbits) == NULL) { if(bitarr != NULL) free(bitarr); errno = ENOMEM; return NULL; } DEBUG_PRINT("Creating BIT_ARRAY (bits: %lu; allocated words: %lu; " "using words: %lu; WORD_SIZE: %i)\n", (unsigned long)nbits, (unsigned long)bitarr->capacity_in_words, (unsigned long)roundup_bits2words64(nbits), (int)WORD_SIZE); DEBUG_VALIDATE(bitarr); return bitarr; } // // Destructor // void bit_array_free(BIT_ARRAY* bitarr) { if(bitarr->words != NULL) free(bitarr->words); free(bitarr); } bit_index_t bit_array_length(const BIT_ARRAY* bit_arr) { return bit_arr->num_of_bits; } // Change the size of a bit array. Enlarging an array will add zeros // to the end of it. Returns 1 on success, 0 on failure (e.g. not enough memory) char bit_array_resize(BIT_ARRAY* bitarr, bit_index_t new_num_of_bits) { word_addr_t old_num_of_words = bitarr->num_of_words; word_addr_t new_num_of_words = roundup_bits2words64(new_num_of_bits); bitarr->num_of_bits = new_num_of_bits; bitarr->num_of_words = new_num_of_words; DEBUG_PRINT("Resize: old_num_of_words: %i; new_num_of_words: %i capacity: %i\n", (int)old_num_of_words, (int)new_num_of_words, (int)bitarr->capacity_in_words); if(new_num_of_words > bitarr->capacity_in_words) { // Need to change the amount of memory used word_addr_t old_capacity_in_words = bitarr->capacity_in_words; size_t old_capacity_in_bytes = old_capacity_in_words * sizeof(word_t); bitarr->capacity_in_words = roundup2pow(new_num_of_words); bitarr->capacity_in_words = MAX(8, bitarr->capacity_in_words); size_t new_capacity_in_bytes = bitarr->capacity_in_words * sizeof(word_t); bitarr->words = (word_t*)realloc(bitarr->words, new_capacity_in_bytes); if(bitarr->words == NULL) { // error - could not allocate enough memory perror("resize realloc"); errno = ENOMEM; return 0; } // Need to zero new memory size_t num_bytes_to_zero = new_capacity_in_bytes - old_capacity_in_bytes; memset(bitarr->words + old_capacity_in_words, 0, num_bytes_to_zero); DEBUG_PRINT("zeroing from word %i for %i bytes\n", (int)old_capacity_in_words, (int)num_bytes_to_zero); } else if(new_num_of_words < old_num_of_words) { // Shrunk -- need to zero old memory size_t num_bytes_to_zero = (old_num_of_words - new_num_of_words)*sizeof(word_t); memset(bitarr->words + new_num_of_words, 0, num_bytes_to_zero); } // Mask top word _mask_top_word(bitarr); DEBUG_VALIDATE(bitarr); return 1; } void bit_array_resize_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits) { bit_index_t old_num_of_bits = bitarr->num_of_bits; if(!bit_array_resize(bitarr, num_of_bits)) { fprintf(stderr, "Ran out of memory resizing [%lu -> %lu]", (unsigned long)old_num_of_bits, (unsigned long)num_of_bits); abort(); } } // If bitarr length < num_bits, resizes to num_bits char bit_array_ensure_size(BIT_ARRAY* bitarr, bit_index_t ensure_num_of_bits) { if(bitarr->num_of_bits < ensure_num_of_bits) { return bit_array_resize(bitarr, ensure_num_of_bits); } return 1; } void bit_array_ensure_size_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits) { if(num_of_bits > bitarr->num_of_bits) { bit_array_resize_critical(bitarr, num_of_bits); } } static inline void _bit_array_ensure_nwords(BIT_ARRAY* bitarr, word_addr_t nwords, const char *file, int lineno, const char *func) { size_t newmem, oldmem; if(bitarr->capacity_in_words < nwords) { oldmem = bitarr->capacity_in_words * sizeof(word_t); bitarr->capacity_in_words = roundup2pow(nwords); newmem = bitarr->capacity_in_words * sizeof(word_t); bitarr->words = (word_t*)realloc(bitarr->words, newmem); if(bitarr->words == NULL) { fprintf(stderr, "[%s:%i:%s()] Ran out of memory resizing [%zu -> %zu]", file, lineno, func, oldmem, newmem); abort(); } DEBUG_PRINT("Ensure nwords realloc %zu -> %zu\n", oldmem, newmem); } } // // Get, set, clear, assign and toggle individual bits // // Get the value of a bit (returns 0 or 1) char bit_array_get_bit(const BIT_ARRAY* bitarr, bit_index_t b) { assert(b < bitarr->num_of_bits); return bit_array_get(bitarr, b); } // set a bit (to 1) at position b void bit_array_set_bit(BIT_ARRAY* bitarr, bit_index_t b) { assert(b < bitarr->num_of_bits); bit_array_set(bitarr,b); DEBUG_VALIDATE(bitarr); } // clear a bit (to 0) at position b void bit_array_clear_bit(BIT_ARRAY* bitarr, bit_index_t b) { assert(b < bitarr->num_of_bits); bit_array_clear(bitarr, b); DEBUG_VALIDATE(bitarr); } // If bit is 0 -> 1, if bit is 1 -> 0. AKA 'flip' void bit_array_toggle_bit(BIT_ARRAY* bitarr, bit_index_t b) { assert(b < bitarr->num_of_bits); bit_array_toggle(bitarr, b); DEBUG_VALIDATE(bitarr); } // If char c != 0, set bit; otherwise clear bit void bit_array_assign_bit(BIT_ARRAY* bitarr, bit_index_t b, char c) { assert(b < bitarr->num_of_bits); bit_array_assign(bitarr, b, c ? 1 : 0); DEBUG_VALIDATE(bitarr); } // // Get, set etc with resize // // Get the value of a bit (returns 0 or 1) char bit_array_rget(BIT_ARRAY* bitarr, bit_index_t b) { bit_array_ensure_size_critical(bitarr, b+1); return bit_array_get(bitarr, b); } // set a bit (to 1) at position b void bit_array_rset(BIT_ARRAY* bitarr, bit_index_t b) { bit_array_ensure_size_critical(bitarr, b+1); bit_array_set(bitarr,b); DEBUG_VALIDATE(bitarr); } // clear a bit (to 0) at position b void bit_array_rclear(BIT_ARRAY* bitarr, bit_index_t b) { bit_array_ensure_size_critical(bitarr, b+1); bit_array_clear(bitarr, b); DEBUG_VALIDATE(bitarr); } // If bit is 0 -> 1, if bit is 1 -> 0. AKA 'flip' void bit_array_rtoggle(BIT_ARRAY* bitarr, bit_index_t b) { bit_array_ensure_size_critical(bitarr, b+1); bit_array_toggle(bitarr, b); DEBUG_VALIDATE(bitarr); } // If char c != 0, set bit; otherwise clear bit void bit_array_rassign(BIT_ARRAY* bitarr, bit_index_t b, char c) { bit_array_ensure_size_critical(bitarr, b+1); bit_array_assign(bitarr, b, c ? 1 : 0); DEBUG_VALIDATE(bitarr); } // // Set, clear and toggle several bits at once // // Set multiple bits at once. // e.g. set bits 1, 20 & 31: bit_array_set_bits(bitarr, 3, 1,20,31); void bit_array_set_bits(BIT_ARRAY* bitarr, size_t n, ...) { size_t i; va_list argptr; va_start(argptr, n); for(i = 0; i < n; i++) { unsigned int bit_index = va_arg(argptr, unsigned int); bit_array_set_bit(bitarr, bit_index); } va_end(argptr); DEBUG_VALIDATE(bitarr); } // Clear multiple bits at once. // e.g. clear bits 1, 20 & 31: bit_array_clear_bits(bitarr, 3, 1,20,31); void bit_array_clear_bits(BIT_ARRAY* bitarr, size_t n, ...) { size_t i; va_list argptr; va_start(argptr, n); for(i = 0; i < n; i++) { unsigned int bit_index = va_arg(argptr, unsigned int); bit_array_clear_bit(bitarr, bit_index); } va_end(argptr); DEBUG_VALIDATE(bitarr); } // Toggle multiple bits at once // e.g. toggle bits 1, 20 & 31: bit_array_toggle_bits(bitarr, 3, 1,20,31); void bit_array_toggle_bits(BIT_ARRAY* bitarr, size_t n, ...) { size_t i; va_list argptr; va_start(argptr, n); for(i = 0; i < n; i++) { unsigned int bit_index = va_arg(argptr, unsigned int); bit_array_toggle_bit(bitarr, bit_index); } va_end(argptr); DEBUG_VALIDATE(bitarr); } // // Set, clear and toggle all bits in a region // // Set all the bits in a region void bit_array_set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len) { assert(start + len <= bitarr->num_of_bits); SET_REGION(bitarr, start, len); DEBUG_VALIDATE(bitarr); } // Clear all the bits in a region void bit_array_clear_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len) { assert(start + len <= bitarr->num_of_bits); CLEAR_REGION(bitarr, start, len); DEBUG_VALIDATE(bitarr); } // Toggle all the bits in a region void bit_array_toggle_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len) { assert(start + len <= bitarr->num_of_bits); TOGGLE_REGION(bitarr, start, len); DEBUG_VALIDATE(bitarr); } // // Set, clear and toggle all bits at once // // set all elements of data to one void bit_array_set_all(BIT_ARRAY* bitarr) { bit_index_t num_of_bytes = bitarr->num_of_words * sizeof(word_t); memset(bitarr->words, 0xFF, num_of_bytes); _mask_top_word(bitarr); DEBUG_VALIDATE(bitarr); } // set all elements of data to zero void bit_array_clear_all(BIT_ARRAY* bitarr) { memset(bitarr->words, 0, bitarr->num_of_words * sizeof(word_t)); DEBUG_VALIDATE(bitarr); } // Set all 1 bits to 0, and all 0 bits to 1. AKA flip void bit_array_toggle_all(BIT_ARRAY* bitarr) { word_addr_t i; for(i = 0; i < bitarr->num_of_words; i++) { bitarr->words[i] ^= WORD_MAX; } _mask_top_word(bitarr); DEBUG_VALIDATE(bitarr); } // // Get a word at a time // uint64_t bit_array_get_word64(const BIT_ARRAY* bitarr, bit_index_t start) { assert(start < bitarr->num_of_bits); return (uint64_t)_get_word(bitarr, start); } uint32_t bit_array_get_word32(const BIT_ARRAY* bitarr, bit_index_t start) { assert(start < bitarr->num_of_bits); return (uint32_t)_get_word(bitarr, start); } uint16_t bit_array_get_word16(const BIT_ARRAY* bitarr, bit_index_t start) { assert(start < bitarr->num_of_bits); return (uint16_t)_get_word(bitarr, start); } uint8_t bit_array_get_word8(const BIT_ARRAY* bitarr, bit_index_t start) { assert(start < bitarr->num_of_bits); return (uint8_t)_get_word(bitarr, start); } uint64_t bit_array_get_wordn(const BIT_ARRAY* bitarr, bit_index_t start, int n) { assert(start < bitarr->num_of_bits); assert(n <= 64); return (uint64_t)(_get_word(bitarr, start) & bitmask64(n)); } // // Set a word at a time // // Doesn't extend bit array. However it is safe to TRY to set bits beyond the // end of the array, as long as: `start` is < `bit_array_length(arr)` // void bit_array_set_word64(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word) { assert(start < bitarr->num_of_bits); _set_word(bitarr, start, (word_t)word); } void bit_array_set_word32(BIT_ARRAY* bitarr, bit_index_t start, uint32_t word) { assert(start < bitarr->num_of_bits); word_t w = _get_word(bitarr, start); _set_word(bitarr, start, bitmask_merge(w, word, 0xffffffff00000000UL)); } void bit_array_set_word16(BIT_ARRAY* bitarr, bit_index_t start, uint16_t word) { assert(start < bitarr->num_of_bits); word_t w = _get_word(bitarr, start); _set_word(bitarr, start, bitmask_merge(w, word, 0xffffffffffff0000UL)); } void bit_array_set_word8(BIT_ARRAY* bitarr, bit_index_t start, uint8_t byte) { assert(start < bitarr->num_of_bits); _set_byte(bitarr, start, byte); } void bit_array_set_wordn(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word, int n) { assert(start < bitarr->num_of_bits); assert(n <= 64); word_t w = _get_word(bitarr, start), m = bitmask64(n); _set_word(bitarr, start, bitmask_merge(word,w,m)); } // // Number of bits set // // Get the number of bits set (hamming weight) bit_index_t bit_array_num_bits_set(const BIT_ARRAY* bitarr) { word_addr_t i; bit_index_t num_of_bits_set = 0; for(i = 0; i < bitarr->num_of_words; i++) { if(bitarr->words[i] > 0) { num_of_bits_set += POPCOUNT(bitarr->words[i]); } } return num_of_bits_set; } // Get the number of bits not set (1 - hamming weight) bit_index_t bit_array_num_bits_cleared(const BIT_ARRAY* bitarr) { return bitarr->num_of_bits - bit_array_num_bits_set(bitarr); } // Get the number of bits set in on array and not the other. This is equivalent // to hamming weight of the XOR when the two arrays are the same length. // e.g. 10101 vs 00111 => hamming distance 2 (XOR is 10010) bit_index_t bit_array_hamming_distance(const BIT_ARRAY* arr1, const BIT_ARRAY* arr2) { word_addr_t min_words = MIN(arr1->num_of_words, arr2->num_of_words); word_addr_t max_words = MAX(arr1->num_of_words, arr2->num_of_words); bit_index_t hamming_distance = 0; word_addr_t i; for(i = 0; i < min_words; i++) { hamming_distance += POPCOUNT(arr1->words[i] ^ arr2->words[i]); } if(min_words != max_words) { const BIT_ARRAY* long_arr = (arr1->num_of_words > arr2->num_of_words ? arr1 : arr2); for(i = min_words; i < max_words; i++) { hamming_distance += POPCOUNT(long_arr->words[i]); } } return hamming_distance; } // Parity - returns 1 if odd number of bits set, 0 if even char bit_array_parity(const BIT_ARRAY* bitarr) { word_addr_t w; unsigned int parity = 0; for(w = 0; w < bitarr->num_of_words; w++) { parity ^= PARITY(bitarr->words[w]); } return (char)parity; } // // Find indices of set/clear bits // // Find the index of the next bit that is set/clear, at or after `offset` // Returns 1 if such a bit is found, otherwise 0 // Index is stored in the integer pointed to by `result` // If no such bit is found, value at `result` is not changed #define _next_bit_func_def(FUNC,GET) \ char FUNC(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) \ { \ assert(offset < bitarr->num_of_bits); \ if(bitarr->num_of_bits == 0 || offset >= bitarr->num_of_bits) { return 0; } \ \ /* Find first word that is greater than zero */ \ word_addr_t i = bitset64_wrd(offset); \ word_t w = GET(bitarr->words[i]) & ~bitmask64(bitset64_idx(offset)); \ \ while(1) { \ if(w > 0) { \ bit_index_t pos = i * WORD_SIZE + trailing_zeros(w); \ if(pos < bitarr->num_of_bits) { *result = pos; return 1; } \ else { return 0; } \ } \ i++; \ if(i >= bitarr->num_of_words) break; \ w = GET(bitarr->words[i]); \ } \ \ return 0; \ } // Find the index of the previous bit that is set/clear, before `offset`. // Returns 1 if such a bit is found, otherwise 0 // Index is stored in the integer pointed to by `result` // If no such bit is found, value at `result` is not changed #define _prev_bit_func_def(FUNC,GET) \ char FUNC(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) \ { \ assert(offset <= bitarr->num_of_bits); \ if(bitarr->num_of_bits == 0 || offset == 0) { return 0; } \ \ /* Find prev word that is greater than zero */ \ word_addr_t i = bitset64_wrd(offset-1); \ word_t w = GET(bitarr->words[i]) & bitmask64(bitset64_idx(offset-1)+1); \ \ if(w > 0) { *result = (i+1) * WORD_SIZE - leading_zeros(w) - 1; return 1; } \ \ /* i is unsigned so have to use break when i == 0 */ \ for(--i; i != BIT_INDEX_MAX; i--) { \ w = GET(bitarr->words[i]); \ if(w > 0) { \ *result = (i+1) * WORD_SIZE - leading_zeros(w) - 1; \ return 1; \ } \ } \ \ return 0; \ } #define GET_WORD(x) (x) #define NEG_WORD(x) (~(x)) _next_bit_func_def(bit_array_find_next_set_bit, GET_WORD); _next_bit_func_def(bit_array_find_next_clear_bit,NEG_WORD); _prev_bit_func_def(bit_array_find_prev_set_bit, GET_WORD); _prev_bit_func_def(bit_array_find_prev_clear_bit,NEG_WORD); // Find the index of the first bit that is set. // Returns 1 if a bit is set, otherwise 0 // Index of first set bit is stored in the integer pointed to by result // If no bits are set, value at `result` is not changed char bit_array_find_first_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result) { return bit_array_find_next_set_bit(bitarr, 0, result); } // same same char bit_array_find_first_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result) { return bit_array_find_next_clear_bit(bitarr, 0, result); } // Find the index of the last bit that is set. // Returns 1 if a bit is set, otherwise 0 // Index of last set bit is stored in the integer pointed to by `result` // If no bits are set, value at `result` is not changed char bit_array_find_last_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result) { return bit_array_find_prev_set_bit(bitarr, bitarr->num_of_bits, result); } // same same char bit_array_find_last_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result) { return bit_array_find_prev_clear_bit(bitarr, bitarr->num_of_bits, result); } // // "Sorting" bits // // Put all the 0s before all the 1s void bit_array_sort_bits(BIT_ARRAY* bitarr) { bit_index_t num_of_bits_set = bit_array_num_bits_set(bitarr); bit_index_t num_of_bits_cleared = bitarr->num_of_bits - num_of_bits_set; bit_array_set_all(bitarr); CLEAR_REGION(bitarr, 0, num_of_bits_cleared); DEBUG_VALIDATE(bitarr); } // Put all the 1s before all the 0s void bit_array_sort_bits_rev(BIT_ARRAY* bitarr) { bit_index_t num_of_bits_set = bit_array_num_bits_set(bitarr); bit_array_clear_all(bitarr); SET_REGION(bitarr, 0, num_of_bits_set); DEBUG_VALIDATE(bitarr); } // // Strings and printing // // Construct a BIT_ARRAY from a substring with given on and off characters. void bit_array_from_substr(BIT_ARRAY* bitarr, bit_index_t offset, const char *str, size_t len, const char *on, const char *off, char left_to_right) { bit_array_ensure_size(bitarr, offset + len); bit_array_clear_region(bitarr, offset, len); // BitArray region is now all 0s -- just set the 1s size_t i; bit_index_t j; for(i = 0; i < len; i++) { if(strchr(on, str[i]) != NULL) { j = offset + (left_to_right ? i : len - i - 1); bit_array_set(bitarr, j); } else { assert(strchr(off, str[i]) != NULL); } } DEBUG_VALIDATE(bitarr); } // From string method void bit_array_from_str(BIT_ARRAY* bitarr, const char* str) { bit_array_from_substr(bitarr, 0, str, strlen(str), "1", "0", 1); } // Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in length // Terminates string with '\0' char* bit_array_to_str(const BIT_ARRAY* bitarr, char* str) { bit_index_t i; for(i = 0; i < bitarr->num_of_bits; i++) { str[i] = bit_array_get(bitarr, i) ? '1' : '0'; } str[bitarr->num_of_bits] = '\0'; return str; } char* bit_array_to_str_rev(const BIT_ARRAY* bitarr, char* str) { bit_index_t i; for(i = 0; i < bitarr->num_of_bits; i++) { str[i] = bit_array_get(bitarr, bitarr->num_of_bits-i-1) ? '1' : '0'; } str[bitarr->num_of_bits] = '\0'; return str; } // Get a string representations for a given region, using given on/off characters. // Note: does not null-terminate void bit_array_to_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char on, char off, char left_to_right) { assert(start + length <= bitarr->num_of_bits); bit_index_t i, j; bit_index_t end = start + length - 1; for(i = 0; i < length; i++) { j = (left_to_right ? start + i : end - i); str[i] = bit_array_get(bitarr, j) ? on : off; } // str[length] = '\0'; } // Print this array to a file stream. Prints '0's and '1'. Doesn't print newline. void bit_array_print(const BIT_ARRAY* bitarr, FILE* fout) { bit_index_t i; for(i = 0; i < bitarr->num_of_bits; i++) { fprintf(fout, "%c", bit_array_get(bitarr, i) ? '1' : '0'); } } // Print a string representations for a given region, using given on/off characters. void bit_array_print_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char on, char off, char left_to_right) { assert(start + length <= bitarr->num_of_bits); bit_index_t i, j; bit_index_t end = start + length - 1; for(i = 0; i < length; i++) { j = (left_to_right ? start + i : end - i); fprintf(fout, "%c", bit_array_get(bitarr, j) ? on : off); } } // // Decimal // // Get bit array as decimal str (e.g. 0b1101 -> "13") // len is the length of str char array -- will write at most len-1 chars // returns the number of characters needed // return is the same as strlen(str) size_t bit_array_to_decimal(const BIT_ARRAY *bitarr, char *str, size_t len) { size_t i = 0; if(bit_array_cmp_uint64(bitarr, 0) == 0) { if(len >= 2) { *str = '0'; *(str+1) = '\0'; } return 1; } BIT_ARRAY *tmp = bit_array_clone(bitarr); uint64_t rem; str[len-1] = '\0'; while(bit_array_cmp_uint64(tmp, 0) != 0) { bit_array_div_uint64(tmp, 10, &rem); if(i < len-1) { str[len-2-i] = '0' + rem; } i++; } if(i < len-1) { // Moves null-terminator as well memmove(str, str+len-i-1, i+1); } bit_array_free(tmp); return i; } // Get bit array from decimal str (e.g. "13" -> 0b1101) // Returns number of characters used size_t bit_array_from_decimal(BIT_ARRAY *bitarr, const char* decimal) { bit_array_clear_all(bitarr); size_t i = 0; if(decimal[0] == '\0' || decimal[0] < '0' || decimal[0] > '9') { return 0; } bit_array_add_uint64(bitarr, decimal[i] - '0'); i++; while(decimal[i] != '\0' && decimal[i] >= '0' && decimal[i] <= '9') { bit_array_mul_uint64(bitarr, 10); bit_array_add_uint64(bitarr, decimal[i] - '0'); i++; } return i; } // // Hexidecimal // char bit_array_hex_to_nibble(char c, uint8_t *b) { c = tolower(c); if(c >= '0' && c <= '9') { *b = c - '0'; return 1; } else if(c >= 'a' && c <= 'f') { *b = 0xa + (c - 'a'); return 1; } else { return 0; } } char bit_array_nibble_to_hex(uint8_t b, char uppercase) { if(b <= 9) { return '0' + b; } else { return (uppercase ? 'A' : 'a') + (b - 0xa); } } // Loads array from hex string // Returns the number of bits loaded (will be chars rounded up to multiple of 4) // (0 on failure) bit_index_t bit_array_from_hex(BIT_ARRAY* bitarr, bit_index_t offset, const char* str, size_t len) { if(str[0] == '0' && tolower(str[1]) == 'x') { str += 2; len -= 2; } size_t i; for(i = 0; i < len; i++, offset += 4) { uint8_t b; if(bit_array_hex_to_nibble(str[i], &b)) { bit_array_ensure_size(bitarr, offset + 4); _set_nibble(bitarr, offset, b); } else { break; } } return 4 * i; } // Returns number of characters written size_t bit_array_to_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char uppercase) { assert(start + length <= bitarr->num_of_bits); size_t k = 0; bit_index_t offset, end = start + length; for(offset = start; offset + WORD_SIZE <= end; offset += WORD_SIZE) { word_t w = _get_word(bitarr, offset); word_offset_t j; for(j = 0; j < 64; j += 4) { str[k++] = bit_array_nibble_to_hex((w>>j) & 0xf, uppercase); } } if(offset < end) { // Remaining full nibbles (4 bits) word_t w = _get_word(bitarr, offset); for(; offset + 4 <= end; offset += 4) { str[k++] = bit_array_nibble_to_hex(w & 0xf, uppercase); w >>= 4; } if(offset < end) { // Remaining bits str[k++] = bit_array_nibble_to_hex(w & bitmask64(end - offset), uppercase); } } str[k] = '\0'; // Return number of characters written return k; } // Print bit array as hex size_t bit_array_print_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char uppercase) { assert(start + length <= bitarr->num_of_bits); size_t k = 0; bit_index_t offset, end = start + length; for(offset = start; offset + WORD_SIZE <= end; offset += WORD_SIZE) { word_t w = _get_word(bitarr, offset); word_offset_t j; for(j = 0; j < 64; j += 4) { fprintf(fout, "%c", bit_array_nibble_to_hex((w>>j) & 0xf, uppercase)); k++; } } if(offset < end) { // Remaining full nibbles (4 bits) word_t w = _get_word(bitarr, offset); for(; offset + 4 <= end; offset += 4) { fprintf(fout, "%c", bit_array_nibble_to_hex(w & 0xf, uppercase)); w >>= 4; k++; } if(offset < end) { // Remaining bits char hex = bit_array_nibble_to_hex(w & bitmask64(end - offset), uppercase); fprintf(fout, "%c", hex); k++; } } return k; } // // Clone and copy // // Returns NULL if cannot malloc BIT_ARRAY* bit_array_clone(const BIT_ARRAY* bitarr) { BIT_ARRAY* cpy = bit_array_create(bitarr->num_of_bits); if(cpy == NULL) { return NULL; } // Copy across bits memcpy(cpy->words, bitarr->words, bitarr->num_of_words * sizeof(word_t)); DEBUG_VALIDATE(cpy); return cpy; } // destination and source may be the same bit_array // and src/dst regions may overlap static void _array_copy(BIT_ARRAY* dst, bit_index_t dstindx, const BIT_ARRAY* src, bit_index_t srcindx, bit_index_t length) { DEBUG_PRINT("bit_array_copy(dst: %zu, src: %zu, length: %zu)\n", (size_t)dstindx, (size_t)srcindx, (size_t)length); // Num of full words to copy word_addr_t num_of_full_words = length / WORD_SIZE; word_addr_t i; word_offset_t bits_in_last_word = bits_in_top_word(length); if(dst == src && srcindx > dstindx) { // Work left to right DEBUG_PRINT("work left to right\n"); for(i = 0; i < num_of_full_words; i++) { word_t word = _get_word(src, srcindx+i*WORD_SIZE); _set_word(dst, dstindx+i*WORD_SIZE, word); } if(bits_in_last_word > 0) { word_t src_word = _get_word(src, srcindx+i*WORD_SIZE); word_t dst_word = _get_word(dst, dstindx+i*WORD_SIZE); word_t mask = bitmask64(bits_in_last_word); word_t word = bitmask_merge(src_word, dst_word, mask); _set_word(dst, dstindx+num_of_full_words*WORD_SIZE, word); } } else { // Work right to left DEBUG_PRINT("work right to left\n"); for(i = 0; i < num_of_full_words; i++) { word_t word = _get_word(src, srcindx+length-(i+1)*WORD_SIZE); _set_word(dst, dstindx+length-(i+1)*WORD_SIZE, word); } DEBUG_PRINT("Copy %i,%i to %i\n", (int)srcindx, (int)bits_in_last_word, (int)dstindx); if(bits_in_last_word > 0) { word_t src_word = _get_word(src, srcindx); word_t dst_word = _get_word(dst, dstindx); word_t mask = bitmask64(bits_in_last_word); word_t word = bitmask_merge(src_word, dst_word, mask); _set_word(dst, dstindx, word); } } _mask_top_word(dst); } // destination and source may be the same bit_array // and src/dst regions may overlap void bit_array_copy(BIT_ARRAY* dst, bit_index_t dstindx, const BIT_ARRAY* src, bit_index_t srcindx, bit_index_t length) { assert(srcindx + length <= src->num_of_bits); assert(dstindx <= dst->num_of_bits); _array_copy(dst, dstindx, src, srcindx, length); DEBUG_VALIDATE(dst); } // Clone `src` into `dst`. Resizes `dst`. void bit_array_copy_all(BIT_ARRAY* dst, const BIT_ARRAY* src) { bit_array_resize_critical(dst, src->num_of_bits); memmove(dst->words, src->words, src->num_of_words * sizeof(word_t)); DEBUG_VALIDATE(dst); } // // Logic operators // // Destination can be the same as one or both of the sources void bit_array_and(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { // Ensure dst array is big enough word_addr_t max_bits = MAX(src1->num_of_bits, src2->num_of_bits); bit_array_ensure_size_critical(dst, max_bits); word_addr_t min_words = MIN(src1->num_of_words, src2->num_of_words); word_addr_t i; for(i = 0; i < min_words; i++) { dst->words[i] = src1->words[i] & src2->words[i]; } // Set remaining bits to zero for(i = min_words; i < dst->num_of_words; i++) { dst->words[i] = (word_t)0; } DEBUG_VALIDATE(dst); } // Destination can be the same as one or both of the sources static void _logical_or_xor(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2, char use_xor) { // Ensure dst array is big enough bit_array_ensure_size_critical(dst, MAX(src1->num_of_bits, src2->num_of_bits)); word_addr_t min_words = MIN(src1->num_of_words, src2->num_of_words); word_addr_t max_words = MAX(src1->num_of_words, src2->num_of_words); word_addr_t i; if(use_xor) { for(i = 0; i < min_words; i++) dst->words[i] = src1->words[i] ^ src2->words[i]; } else { for(i = 0; i < min_words; i++) dst->words[i] = src1->words[i] | src2->words[i]; } // Copy remaining bits from longer src array if(min_words != max_words) { const BIT_ARRAY* longer = src1->num_of_words > src2->num_of_words ? src1 : src2; for(i = min_words; i < max_words; i++) { dst->words[i] = longer->words[i]; } } // Set remaining bits to zero size_t size = (dst->num_of_words - max_words) * sizeof(word_t); memset(dst->words + max_words, 0, size); DEBUG_VALIDATE(dst); } void bit_array_or(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { _logical_or_xor(dst, src1, src2, 0); } // Destination can be the same as one or both of the sources void bit_array_xor(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { _logical_or_xor(dst, src1, src2, 1); } // If dst is longer than src, top bits are set to 1 void bit_array_not(BIT_ARRAY* dst, const BIT_ARRAY* src) { bit_array_ensure_size_critical(dst, src->num_of_bits); word_addr_t i; for(i = 0; i < src->num_of_words; i++) { dst->words[i] = ~(src->words[i]); } // Set remaining words to 1s for(i = src->num_of_words; i < dst->num_of_words; i++) { dst->words[i] = WORD_MAX; } _mask_top_word(dst); DEBUG_VALIDATE(dst); } // // Comparisons // // Compare two bit arrays by value stored, with index 0 being the Least // Significant Bit (LSB). Arrays do not have to be the same length. // Example: ..0101 (5) > ...0011 (3) [index 0 is LSB at right hand side] // Sorts on length if all zeros: (0,0) < (0,0,0) // returns: // >0 iff bitarr1 > bitarr2 // 0 iff bitarr1 == bitarr2 // <0 iff bitarr1 < bitarr2 int bit_array_cmp(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2) { word_addr_t i; word_t word1, word2; word_addr_t min_words = bitarr1->num_of_words; // i is unsigned so break when i == 0 if(bitarr1->num_of_words > bitarr2->num_of_words) { min_words = bitarr2->num_of_words; for(i = bitarr1->num_of_words-1; ; i--) { if(bitarr1->words[i]) return 1; if(i == bitarr2->num_of_words) break; } } else if(bitarr1->num_of_words < bitarr2->num_of_words) { for(i = bitarr2->num_of_words-1; ; i--) { if(bitarr2->words[i]) return 1; if(i == bitarr1->num_of_words) break; } } if(min_words == 0) return 0; for(i = min_words-1; ; i--) { word1 = bitarr1->words[i]; word2 = bitarr2->words[i]; if(word1 != word2) return (word1 > word2 ? 1 : -1); if(i == 0) break; } if(bitarr1->num_of_bits == bitarr2->num_of_bits) return 0; return bitarr1->num_of_bits > bitarr2->num_of_bits ? 1 : -1; } // Compare two bit arrays by value stored, with index 0 being the Most // Significant Bit (MSB). Arrays do not have to be the same length. // Example: 10.. > 01.. [index 0 is MSB at left hand side] // Sorts on length if all zeros: (0,0) < (0,0,0) // returns: // >0 iff bitarr1 > bitarr2 // 0 iff bitarr1 == bitarr2 // <0 iff bitarr1 < bitarr2 int bit_array_cmp_big_endian(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2) { word_addr_t min_words = MAX(bitarr1->num_of_words, bitarr2->num_of_words); word_addr_t i; word_t word1, word2; for(i = 0; i < min_words; i++) { word1 = _reverse_word(bitarr1->words[i]); word2 = _reverse_word(bitarr2->words[i]); if(word1 != word2) return (word1 > word2 ? 1 : -1); } // Check remaining words. Only one of these loops will execute for(; i < bitarr1->num_of_words; i++) if(bitarr1->words[i]) return 1; for(; i < bitarr2->num_of_words; i++) if(bitarr2->words[i]) return -1; if(bitarr1->num_of_bits == bitarr2->num_of_bits) return 0; return bitarr1->num_of_bits > bitarr2->num_of_bits ? 1 : -1; } // compare bitarr with (bitarr2 << pos) // bit_array_cmp(bitarr1, bitarr2<0 iff bitarr1 > bitarr2 // 0 iff bitarr1 == bitarr2 // <0 iff bitarr1 < bitarr2 int bit_array_cmp_words(const BIT_ARRAY *arr1, bit_index_t pos, const BIT_ARRAY *arr2) { if(arr1->num_of_bits == 0 && arr2->num_of_bits == 0) { return 0; } bit_index_t top_bit1 = 0, top_bit2 = 0; char arr1_zero = !bit_array_find_last_set_bit(arr1, &top_bit1); char arr2_zero = !bit_array_find_last_set_bit(arr2, &top_bit2); if(arr1_zero && arr2_zero) return 0; if(arr1_zero) return -1; if(arr2_zero) return 1; bit_index_t top_bit2_offset = top_bit2 + pos; if(top_bit1 != top_bit2_offset) { return top_bit1 > top_bit2_offset ? 1 : -1; } word_addr_t i; word_t word1, word2; for(i = top_bit2 / WORD_SIZE; i > 0; i--) { word1 = _get_word(arr1, pos + i * WORD_SIZE); word2 = arr2->words[i]; if(word1 > word2) return 1; if(word1 < word2) return -1; } word1 = _get_word(arr1, pos); word2 = arr2->words[0]; if(word1 > word2) return 1; if(word1 < word2) return -1; // return 1 if arr1[0..pos] != 0, 0 otherwise // Whole words word_addr_t num_words = pos / WORD_SIZE; for(i = 0; i < num_words; i++) { if(arr1->words[i] > 0) { return 1; } } word_offset_t bits_remaining = pos - num_words * WORD_SIZE; if(arr1->words[num_words] & bitmask64(bits_remaining)) { return 1; } return 0; } // // Reverse -- coords may wrap around // // No bounds checking // length cannot be zero static void _reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length) { bit_index_t left = start; bit_index_t right = (start + length - WORD_SIZE) % bitarr->num_of_bits; while(length >= 2 * WORD_SIZE) { // Swap entire words word_t left_word = _get_word_cyclic(bitarr, left); word_t right_word = _get_word_cyclic(bitarr, right); // reverse words individually left_word = _reverse_word(left_word); right_word = _reverse_word(right_word); // Swap _set_word_cyclic(bitarr, left, right_word); _set_word_cyclic(bitarr, right, left_word); // Update left = (left + WORD_SIZE) % bitarr->num_of_bits; right = (right < WORD_SIZE ? right + bitarr->num_of_bits : right) - WORD_SIZE; length -= 2 * WORD_SIZE; } word_t word, rev; if(length == 0) { return; } else if(length > WORD_SIZE) { // Words overlap word_t left_word = _get_word_cyclic(bitarr, left); word_t right_word = _get_word_cyclic(bitarr, right); rev = _reverse_word(left_word); right_word = _reverse_word(right_word); // fill left 64 bits with right word rev _set_word_cyclic(bitarr, left, right_word); // Now do remaining bits (length is between 1 and 64 bits) left += WORD_SIZE; length -= WORD_SIZE; word = _get_word_cyclic(bitarr, left); } else { word = _get_word_cyclic(bitarr, left); rev = _reverse_word(word); } rev >>= WORD_SIZE - length; word_t mask = bitmask64(length); word = bitmask_merge(rev, word, mask); _set_word_cyclic(bitarr, left, word); } void bit_array_reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len) { assert(start + len <= bitarr->num_of_bits); if(len > 0) _reverse_region(bitarr, start, len); DEBUG_VALIDATE(bitarr); } void bit_array_reverse(BIT_ARRAY* bitarr) { if(bitarr->num_of_bits > 0) _reverse_region(bitarr, 0, bitarr->num_of_bits); DEBUG_VALIDATE(bitarr); } // // Shift left / right // // Shift towards MSB / higher index void bit_array_shift_left(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) { if(shift_dist >= bitarr->num_of_bits) { fill ? bit_array_set_all(bitarr) : bit_array_clear_all(bitarr); return; } else if(shift_dist == 0) { return; } FillAction action = fill ? FILL_REGION : ZERO_REGION; bit_index_t cpy_length = bitarr->num_of_bits - shift_dist; _array_copy(bitarr, shift_dist, bitarr, 0, cpy_length); _set_region(bitarr, 0, shift_dist, action); } // shift left extend - don't truncate bits when shifting UP, instead // make room for them. void bit_array_shift_left_extend(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) { bit_index_t newlen = bitarr->num_of_bits + shift_dist; bit_index_t cpy_length = bitarr->num_of_bits; if(shift_dist == 0) { return; } bit_array_resize_critical(bitarr, newlen); FillAction action = fill ? FILL_REGION : ZERO_REGION; _array_copy(bitarr, shift_dist, bitarr, 0, cpy_length); _set_region(bitarr, 0, shift_dist, action); } // Shift towards LSB / lower index void bit_array_shift_right(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill) { if(shift_dist >= bitarr->num_of_bits) { fill ? bit_array_set_all(bitarr) : bit_array_clear_all(bitarr); return; } else if(shift_dist == 0) { return; } FillAction action = fill ? FILL_REGION : ZERO_REGION; bit_index_t cpy_length = bitarr->num_of_bits - shift_dist; bit_array_copy(bitarr, 0, bitarr, shift_dist, cpy_length); _set_region(bitarr, cpy_length, shift_dist, action); } // // Cycle // // Cycle towards index 0 void bit_array_cycle_right(BIT_ARRAY* bitarr, bit_index_t cycle_dist) { if(bitarr->num_of_bits == 0) { return; } cycle_dist = cycle_dist % bitarr->num_of_bits; if(cycle_dist == 0) { return; } bit_index_t len1 = cycle_dist; bit_index_t len2 = bitarr->num_of_bits - cycle_dist; _reverse_region(bitarr, 0, len1); _reverse_region(bitarr, len1, len2); bit_array_reverse(bitarr); } // Cycle away from index 0 void bit_array_cycle_left(BIT_ARRAY* bitarr, bit_index_t cycle_dist) { if(bitarr->num_of_bits == 0) { return; } cycle_dist = cycle_dist % bitarr->num_of_bits; if(cycle_dist == 0) { return; } bit_index_t len1 = bitarr->num_of_bits - cycle_dist; bit_index_t len2 = cycle_dist; _reverse_region(bitarr, 0, len1); _reverse_region(bitarr, len1, len2); bit_array_reverse(bitarr); } // // Next permutation // static word_t _next_permutation(word_t v) { assert(v); // From http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation word_t t = v | (v - 1); // t gets v's least significant 0 bits set to 1 // Next set to 1 the most significant bit to change, // set to 0 the least significant ones, and add the necessary 1 bits. return (t+1) | (((~t & (t+1)) - 1) >> (trailing_zeros(v) + 1)); } // Get the next permutation of an array with a fixed size and given number of // bits set. Also known as next lexicographic permutation. // Given a bit array find the next lexicographic orginisation of the bits // Number of possible combinations given by (size choose bits_set) i.e. nCk // 00011 -> 00101 -> 00110 -> 01001 -> 01010 -> // 01100 -> 10001 -> 10010 -> 10100 -> 11000 -> 00011 (back to start) void bit_array_next_permutation(BIT_ARRAY* bitarr) { if(bitarr->num_of_bits == 0) { return; } word_addr_t w; char carry = 0; word_offset_t top_bits = bitset64_idx(bitarr->num_of_bits); for(w = 0; w < bitarr->num_of_words; w++) { word_t mask = (w < bitarr->num_of_words - 1 || top_bits == 0) ? WORD_MAX : bitmask64(top_bits); if(bitarr->words[w] > 0 && (bitarr->words[w] | (bitarr->words[w]-1)) == mask) { // Bits in this word cannot be moved forward carry = 1; } else if(carry) { // 0111 -> 1000, 1000 -> 1001 word_t tmp = bitarr->words[w] + 1; // Count bits previously set bit_index_t bits_previously_set = POPCOUNT(bitarr->words[w]); // set new word bitarr->words[w] = tmp; // note: w is unsigned // Zero words while counting bits set while(w > 0) { bits_previously_set += POPCOUNT(bitarr->words[w-1]); bitarr->words[w-1] = 0; w--; } // Set bits at the beginning SET_REGION(bitarr, 0, bits_previously_set - POPCOUNT(tmp)); carry = 0; break; } else if(bitarr->words[w] > 0) { bitarr->words[w] = _next_permutation(bitarr->words[w]); break; } } if(carry) { // Loop around bit_index_t num_bits_set = bit_array_num_bits_set(bitarr); bit_array_clear_all(bitarr); SET_REGION(bitarr, 0, num_bits_set); } DEBUG_VALIDATE(bitarr); } // // Interleave // // dst cannot point to the same bit array as src1 or src2 // src1, src2 may point to the same bit array // abcd 1234 -> a1b2c3d4 // 0011 0000 -> 00001010 // 1111 0000 -> 10101010 // 0101 1010 -> 01100110 void bit_array_interleave(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { // dst cannot be either src1 or src2 assert(dst != src1 && dst != src2); // Behaviour undefined when src1 length != src2 length", assert(src1->num_of_bits == src2->num_of_bits); // Need at least src1->num_of_words + src2->num_of_words size_t nwords = MIN(src1->num_of_words + src2->num_of_words, 2); _bit_array_ensure_nwords(dst, nwords, __FILE__, __LINE__, __func__); dst->num_of_bits = src1->num_of_bits + src2->num_of_bits; dst->num_of_words = roundup_bits2words64(dst->num_of_bits); word_addr_t i, j; for(i = 0, j = 0; i < src1->num_of_words; i++) { word_t a = src1->words[i]; word_t b = src2->words[i]; dst->words[j++] = morton_table0[(a ) & 0xff] | morton_table1[(b ) & 0xff] | (morton_table0[(a >> 8) & 0xff] << 16) | (morton_table1[(b >> 8) & 0xff] << 16) | (morton_table0[(a >> 16) & 0xff] << 32) | (morton_table1[(b >> 16) & 0xff] << 32) | (morton_table0[(a >> 24) & 0xff] << 48) | (morton_table1[(b >> 24) & 0xff] << 48); dst->words[j++] = morton_table0[(a >> 32) & 0xff] | morton_table1[(b >> 32) & 0xff] | (morton_table0[(a >> 40) & 0xff] << 16) | (morton_table1[(b >> 40) & 0xff] << 16) | (morton_table0[(a >> 48) & 0xff] << 32) | (morton_table1[(b >> 48) & 0xff] << 32) | (morton_table0[(a >> 56) ] << 48) | (morton_table1[(b >> 56) ] << 48); } DEBUG_VALIDATE(dst); } // // Random // // Set bits randomly with probability prob : 0 <= prob <= 1 void bit_array_random(BIT_ARRAY* bitarr, float prob) { assert(prob >= 0 && prob <= 1); if(bitarr->num_of_bits == 0) { return; } else if(prob == 1) { bit_array_set_all(bitarr); return; } // rand() generates number between 0 and RAND_MAX inclusive // therefore we want to check if rand() <= p long p = RAND_MAX * prob; _seed_rand(); word_addr_t w; word_offset_t o; // Initialise to zero memset(bitarr->words, 0, bitarr->num_of_words * sizeof(word_t)); for(w = 0; w < bitarr->num_of_words - 1; w++) { for(o = 0; o < WORD_SIZE; o++) { if(rand() <= p) { bitarr->words[w] |= ((word_t)0x1 << o); } } } // Top word word_offset_t bits_in_last_word = bits_in_top_word(bitarr->num_of_bits); w = bitarr->num_of_words - 1; for(o = 0; o < bits_in_last_word; o++) { if(rand() <= p) { bitarr->words[w] |= ((word_t)0x1 << o); } } DEBUG_VALIDATE(bitarr); } // Shuffle the bits in an array randomly void bit_array_shuffle(BIT_ARRAY* bitarr) { if(bitarr->num_of_bits == 0) return; _seed_rand(); bit_index_t i, j; for(i = bitarr->num_of_bits - 1; i > 0; i--) { j = (bit_index_t)rand() % i; // Swap i and j char x = (bitarr->words[bitset64_wrd(i)] >> bitset64_idx(i)) & 0x1; char y = (bitarr->words[bitset64_wrd(j)] >> bitset64_idx(j)) & 0x1; if(!y) bitarr->words[bitset64_wrd(i)] &= ~((word_t)0x1 << bitset64_idx(i)); else bitarr->words[bitset64_wrd(i)] |= (word_t)0x1 << bitset64_idx(i); if(!x) bitarr->words[bitset64_wrd(j)] &= ~((word_t)0x1 << bitset64_idx(j)); else bitarr->words[bitset64_wrd(j)] |= (word_t)0x1 << bitset64_idx(j); } DEBUG_VALIDATE(bitarr); } // // Arithmetic // // Returns 1 on sucess, 0 if value in array is too big char bit_array_as_num(const BIT_ARRAY* bitarr, uint64_t* result) { if(bitarr->num_of_bits == 0) { *result = 0; return 1; } word_addr_t w; for(w = bitarr->num_of_words-1; w > 0; w--) { if(bitarr->words[w] > 0) { return 0; } } *result = bitarr->words[0]; return 1; } // 1 iff bitarr > value // 0 iff bitarr == value // -1 iff bitarr < value int bit_array_cmp_uint64(const BIT_ARRAY* bitarr, uint64_t value) { uint64_t arr_num = 0; // If cannot put bitarr in uint64, it is > value if(!bit_array_as_num(bitarr, &arr_num)) return 1; if(arr_num > value) return 1; else if(arr_num < value) return -1; else return 0; } // If value is zero, no change is made void bit_array_add_uint64(BIT_ARRAY* bitarr, uint64_t value) { if(value == 0) { return; } else if(bitarr->num_of_bits == 0) { bit_array_resize_critical(bitarr, WORD_SIZE - leading_zeros(value)); bitarr->words[0] = (word_t)value; return; } char carry = 0; word_addr_t i; for(i = 0; i < bitarr->num_of_words; i++) { if(WORD_MAX - bitarr->words[i] < value) { carry = 1; bitarr->words[i] += value; } else { // Carry is absorbed bitarr->words[i] += value; carry = 0; break; } } if(carry) { // Bit array full, need another bit after all words filled bit_array_resize_critical(bitarr, bitarr->num_of_words * WORD_SIZE + 1); // Set top word to 1 bitarr->words[bitarr->num_of_words-1] = 1; } else { word_t final_word = bitarr->words[bitarr->num_of_words-1]; word_offset_t expected_bits = bits_in_top_word(bitarr->num_of_bits); word_offset_t actual_bits = WORD_SIZE - leading_zeros(final_word); if(actual_bits > expected_bits) { // num_of_bits has increased -- num_of_words has not bitarr->num_of_bits += (actual_bits - expected_bits); } } } // If value is greater than bitarr, bitarr is not changed and 0 is returned // Returns 1 on success, 0 if value > bitarr char bit_array_sub_uint64(BIT_ARRAY* bitarr, uint64_t value) { if(value == 0) { return 1; } else if(bitarr->words[0] >= value) { bitarr->words[0] -= value; return 1; } value -= bitarr->words[0]; word_addr_t i; for(i = 1; i < bitarr->num_of_words; i++) { if(bitarr->words[i] > 0) { // deduct one bitarr->words[i]--; for(; i > 0; i--) { bitarr->words[i] = WORD_MAX; } // -1 since we've already deducted 1 bitarr->words[0] = WORD_MAX - value - 1; return 1; } } // subtract value is greater than array return 0; } // // Arithmetic between bit arrays // // src1, src2 and dst can all be the same BIT_ARRAY static void _arithmetic(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2, char subtract) { word_addr_t max_words = MAX(src1->num_of_words, src2->num_of_words); // Adding: dst_words >= max(src1 words, src2 words) // Subtracting: dst_words is >= src1->num_of_words char carry = subtract ? 1 : 0; word_addr_t i; word_t word1, word2; for(i = 0; i < max_words; i++) { word1 = (i < src1->num_of_words ? src1->words[i] : 0); word2 = (i < src2->num_of_words ? src2->words[i] : 0); if(subtract) word2 = ~word2; dst->words[i] = word1 + word2 + carry; // Update carry carry = WORD_MAX - word1 < word2 || WORD_MAX - word1 - word2 < (word_t)carry; } if(subtract) { carry = 0; } else { // Check last word word_offset_t bits_on_last_word = bits_in_top_word(dst->num_of_bits); if(bits_on_last_word < WORD_SIZE) { word_t mask = bitmask64(bits_on_last_word); if(dst->words[max_words-1] > mask) { // Array has overflowed, increase size dst->num_of_bits++; } } else if(carry) { // Carry onto a new word if(dst->num_of_words == max_words) { // Need to resize for the carry bit bit_array_resize_critical(dst, dst->num_of_bits+1); } dst->words[max_words] = (word_t)1; } } // Zero the rest of dst array for(i = max_words+carry; i < dst->num_of_words; i++) { dst->words[i] = (word_t)0; } DEBUG_VALIDATE(dst); } // src1, src2 and dst can all be the same BIT_ARRAY // If dst is shorter than either of src1, src2, it is enlarged void bit_array_add(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { bit_array_ensure_size_critical(dst, MAX(src1->num_of_bits, src2->num_of_bits)); _arithmetic(dst, src1, src2, 0); } // dst = src1 - src2 // src1, src2 and dst can all be the same BIT_ARRAY // If dst is shorter than src1, it will be extended to be as long as src1 // src1 must be greater than or equal to src2 (src1 >= src2) void bit_array_subtract(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2) { // subtraction by method of complements: // a - b = a + ~b + 1 = src1 + ~src2 +1 assert(bit_array_cmp(src1, src2) >= 0); // Require src1 >= src2 bit_array_ensure_size_critical(dst, src1->num_of_bits); _arithmetic(dst, src1, src2, 1); } // Add `add` to `bitarr` at `pos` // Bounds checking not needed as out of bounds is valid void bit_array_add_word(BIT_ARRAY *bitarr, bit_index_t pos, uint64_t add) { DEBUG_VALIDATE(bitarr); if(add == 0) { return; } else if(pos >= bitarr->num_of_bits) { // Resize and add! bit_index_t num_bits_required = pos + (WORD_SIZE - leading_zeros(add)); bit_array_resize_critical(bitarr, num_bits_required); _set_word(bitarr, pos, (word_t)add); return; } /* char str[1000]; printf(" add_word: %s\n", bit_array_to_str_rev(bitarr, str)); printf(" word: %s [pos: %i]\n", _word_to_str(add, str), (int)pos); */ word_t w = _get_word(bitarr, pos); word_t sum = w + add; char carry = WORD_MAX - w < add; // Ensure array is big enough bit_index_t num_bits_required = pos + (carry ? WORD_SIZE + 1 : (WORD_SIZE - leading_zeros(sum))); bit_array_ensure_size(bitarr, num_bits_required); _set_word(bitarr, pos, sum); pos += WORD_SIZE; if(carry) { word_offset_t offset = pos % WORD_SIZE; word_addr_t addr = bitset64_wrd(pos); add = (word_t)0x1 << offset; carry = (WORD_MAX - bitarr->words[addr] < add); sum = bitarr->words[addr] + add; num_bits_required = addr * WORD_SIZE + (carry ? WORD_SIZE + 1 : (WORD_SIZE - leading_zeros(sum))); bit_array_ensure_size(bitarr, num_bits_required); bitarr->words[addr++] = sum; if(carry) { while(addr < bitarr->num_of_words && bitarr->words[addr] == WORD_MAX) { bitarr->words[addr++] = 0; } if(addr == bitarr->num_of_words) { bit_array_resize_critical(bitarr, addr * WORD_SIZE + 1); } else if(addr == bitarr->num_of_words-1 && bitarr->words[addr] == bitmask64(bits_in_top_word(bitarr->num_of_bits))) { bit_array_resize_critical(bitarr, bitarr->num_of_bits + 1); } bitarr->words[addr]++; } } DEBUG_VALIDATE(bitarr); } // Add `add` to `bitarr` at `pos` // Bounds checking not needed as out of bounds is valid void bit_array_add_words(BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *add) { assert(bitarr != add); // bitarr and add cannot point to the same bit array bit_index_t add_top_bit_set; if(!bit_array_find_last_set_bit(add, &add_top_bit_set)) { // No bits set in add return; } else if(pos >= bitarr->num_of_bits) { // Just resize and copy! bit_index_t num_bits_required = pos + add_top_bit_set + 1; bit_array_resize_critical(bitarr, num_bits_required); _array_copy(bitarr, pos, add, 0, add->num_of_bits); return; } else if(pos == 0) { bit_array_add(bitarr, bitarr, add); return; } /* char str[1000]; printf(" add_words1: %s\n", bit_array_to_str_rev(bitarr, str)); printf(" add_words2: %s\n", bit_array_to_str_rev(add, str)); printf(" [pos: %i]\n", (int)pos); */ bit_index_t num_bits_required = pos + add_top_bit_set + 1; bit_array_ensure_size(bitarr, num_bits_required); word_addr_t first_word = bitset64_wrd(pos); word_offset_t first_offset = bitset64_idx(pos); word_t w = add->words[0] << first_offset; unsigned char carry = (WORD_MAX - bitarr->words[first_word] < w); bitarr->words[first_word] += w; word_addr_t i = first_word + 1; bit_index_t offset = WORD_SIZE - first_offset; for(; carry || offset <= add_top_bit_set; i++, offset += WORD_SIZE) { w = offset < add->num_of_bits ? _get_word(add, offset) : (word_t)0; if(i >= bitarr->num_of_words) { // Extend by a word bit_array_resize_critical(bitarr, (bit_index_t)(i+1)*WORD_SIZE+1); } word_t prev = bitarr->words[i]; bitarr->words[i] += w + carry; carry = (WORD_MAX - prev < w || (carry && prev + w == WORD_MAX)) ? 1 : 0; } word_offset_t top_bits = WORD_SIZE - leading_zeros(bitarr->words[bitarr->num_of_words-1]); bit_index_t min_bits = (bitarr->num_of_words-1)*WORD_SIZE + top_bits; if(bitarr->num_of_bits < min_bits) { // Extend within the last word bitarr->num_of_bits = min_bits; } DEBUG_VALIDATE(bitarr); } char bit_array_sub_word(BIT_ARRAY* bitarr, bit_index_t pos, word_t minus) { DEBUG_VALIDATE(bitarr); if(minus == 0) { return 1; } word_t w = _get_word(bitarr, pos); if(w >= minus) { _set_word(bitarr, pos, w - minus); DEBUG_VALIDATE(bitarr); return 1; } minus -= w; bit_index_t offset; for(offset = pos + WORD_SIZE; offset < bitarr->num_of_bits; offset += WORD_SIZE) { w = _get_word(bitarr, offset); if(w > 0) { // deduct one _set_word(bitarr, offset, w - 1); SET_REGION(bitarr, pos, offset-pos); // -1 since we've already deducted 1 minus--; _set_word(bitarr, pos, WORD_MAX - minus); DEBUG_VALIDATE(bitarr); return 1; } } DEBUG_VALIDATE(bitarr); return 0; } char bit_array_sub_words(BIT_ARRAY* bitarr, bit_index_t pos, BIT_ARRAY* minus) { assert(bitarr != minus); // bitarr and minus cannot point to the same bit array int cmp = bit_array_cmp_words(bitarr, pos, minus); if(cmp == 0) { bit_array_clear_all(bitarr); return 1; } else if(cmp < 0) { return 0; } bit_index_t bitarr_length = bitarr->num_of_bits; bit_index_t bitarr_top_bit_set; bit_array_find_last_set_bit(bitarr, &bitarr_top_bit_set); // subtraction by method of complements: // a - b = a + ~b + 1 = src1 + ~src2 +1 bit_array_not(minus, minus); bit_array_add_words(bitarr, pos, minus); bit_array_add_word(bitarr, pos, (word_t)1); bit_array_sub_word(bitarr, pos+minus->num_of_bits, 1); bit_array_resize(bitarr, bitarr_length); bit_array_not(minus, minus); DEBUG_VALIDATE(bitarr); return 1; } void bit_array_mul_uint64(BIT_ARRAY *bitarr, uint64_t multiplier) { if(bitarr->num_of_bits == 0 || multiplier == 1) { return; } else if(multiplier == 0) { bit_array_clear_all(bitarr); return; } bit_index_t i; for(i = bitarr->num_of_bits; i > 0; i--) { if(bit_array_get(bitarr, i-1)) { bit_array_clear(bitarr, i-1); bit_array_add_word(bitarr, i-1, multiplier); } } DEBUG_VALIDATE(bitarr); } void bit_array_multiply(BIT_ARRAY *dst, BIT_ARRAY *src1, BIT_ARRAY *src2) { if(src1->num_of_bits == 0 || src2->num_of_bits == 0) { bit_array_clear_all(dst); return; } // Cannot pass the same array as dst, src1 AND src2 assert(dst != src1 || dst != src2); // Dev: multiplier == 1? BIT_ARRAY *read_arr, *add_arr; if(src1 == dst) { read_arr = src1; add_arr = src2; } else { read_arr = src2; add_arr = src1; } if(dst != src1 && dst != src2) { bit_array_clear_all(dst); } bit_index_t i; for(i = read_arr->num_of_bits; i > 0; i--) { if(bit_array_get(read_arr, i-1)) { bit_array_clear(dst, i-1); bit_array_add_words(dst, i-1, add_arr); } } DEBUG_VALIDATE(dst); } // bitarr = round_down(bitarr / divisor) // rem = bitarr % divisor void bit_array_div_uint64(BIT_ARRAY *bitarr, uint64_t divisor, uint64_t *rem) { assert(divisor != 0); // cannot divide by zero bit_index_t div_top_bit = 63 - leading_zeros(divisor); bit_index_t bitarr_top_bit; if(!bit_array_find_last_set_bit(bitarr, &bitarr_top_bit)) { *rem = 0; return; } if(bitarr_top_bit < div_top_bit) { *rem = bitarr->words[0]; bit_array_clear_all(bitarr); return; } // When div is shifted by offset, their top set bits are aligned bit_index_t offset = bitarr_top_bit - div_top_bit; uint64_t tmp = _get_word(bitarr, offset); _set_word(bitarr, offset, (word_t)0); // Carry if 1 if the top bit was set before left shift char carry = 0; // offset unsigned so break when offset == 0 while(1) { if(carry) { // (carry:tmp) - divisor = (WORD_MAX+1+tmp)-divisor tmp = WORD_MAX - divisor + tmp + 1; bit_array_set(bitarr, offset); } else if(tmp >= divisor) { tmp -= divisor; bit_array_set(bitarr, offset); } else { bit_array_clear(bitarr, offset); } if(offset == 0) break; offset--; // Is the top bit set (that we're about to shift off)? carry = tmp & 0x8000000000000000; tmp <<= 1; tmp |= bit_array_get(bitarr, offset); } *rem = tmp; } // Results in: // quotient = dividend / divisor // dividend = dividend % divisor // (dividend is used to return the remainder) void bit_array_divide(BIT_ARRAY *dividend, BIT_ARRAY *quotient, BIT_ARRAY *divisor) { assert(bit_array_cmp_uint64(divisor, 0) != 0); // Cannot divide by zero bit_array_clear_all(quotient); int cmp = bit_array_cmp(dividend, divisor); if(cmp == 0) { bit_array_ensure_size(quotient, 1); bit_array_set(quotient, 0); bit_array_clear_all(dividend); return; } else if(cmp < 0) { // dividend is < divisor, quotient is zero -- done return; } // now we know: dividend > divisor, quotient is zero'd, // dividend != 0, divisor != 0 bit_index_t dividend_top_bit = 0, div_top_bit = 0; bit_array_find_last_set_bit(dividend, ÷nd_top_bit); bit_array_find_last_set_bit(divisor, &div_top_bit); // When divisor is shifted by offset, their top set bits are aligned bit_index_t offset = dividend_top_bit - div_top_bit; // offset unsigned so break when offset == 0 for(; ; offset--) { if(bit_array_cmp_words(dividend, offset, divisor) >= 0) { bit_array_sub_words(dividend, offset, divisor); bit_array_ensure_size(quotient, offset+1); bit_array_set(quotient, offset); } if(offset == 0) break; } } // // Read/Write from files // // file format is [8 bytes: for number of elements in array][data] // data is written in little endian order (least sig byte first) // // Saves bit array to a file. Returns the number of bytes written // number of bytes returned should be 8+(bitarr->num_of_bits+7)/8 bit_index_t bit_array_save(const BIT_ARRAY* bitarr, FILE* f) { bit_index_t num_of_bytes = roundup_bits2bytes(bitarr->num_of_bits); bit_index_t bytes_written = 0; const int endian = 1; if(*(uint8_t*)&endian == 1) { // Little endian machine // Write 8 bytes to store the number of bits in the array bytes_written += fwrite(&bitarr->num_of_bits, 1, 8, f); // Write the array bytes_written += fwrite(bitarr->words, 1, num_of_bytes, f); } else { // Big endian machine uint64_t i, w, whole_words = num_of_bytes/sizeof(word_t); uint64_t rem_bytes = num_of_bytes - whole_words*sizeof(word_t); uint64_t n_bits = byteswap64(bitarr->num_of_bits); // Write 8 bytes to store the number of bits in the array bytes_written += fwrite(&n_bits, 1, 8, f); // Write the array for(i = 0; i < whole_words; i++) { w = byteswap64(bitarr->words[i]); bytes_written += fwrite(&w, 1, 8, f); } if(rem_bytes > 0) { w = byteswap64(bitarr->words[whole_words]); bytes_written += fwrite(&w, 1, rem_bytes, f); } } return bytes_written; } // Load a uint64 from little endian format. // Works for both big and little endian architectures static inline uint64_t le64_to_cpu(const uint8_t *x) { return (((uint64_t)(x[0])) | ((uint64_t)(x[1]) << 8) | ((uint64_t)(x[2]) << 16) | ((uint64_t)(x[3]) << 24) | ((uint64_t)(x[4]) << 32) | ((uint64_t)(x[5]) << 40) | ((uint64_t)(x[6]) << 48) | ((uint64_t)(x[7]) << 56)); } // Reads bit array from a file. bitarr is resized and filled. // Returns 1 on success, 0 on failure char bit_array_load(BIT_ARRAY* bitarr, FILE* f) { // Read in number of bits, return 0 if we can't read in bit_index_t num_bits; if(fread(&num_bits, 1, 8, f) != 8) return 0; num_bits = le64_to_cpu((uint8_t*)&num_bits); // Resize bit_array_resize_critical(bitarr, num_bits); // Have to calculate how many bytes are needed for the file // (Note: this may be different from num_of_words * sizeof(word_t)) bit_index_t num_of_bytes = roundup_bits2bytes(bitarr->num_of_bits); if(fread(bitarr->words, 1, num_of_bytes, f) != num_of_bytes) return 0; // Fix endianness word_addr_t i; for(i = 0; i < bitarr->num_of_words; i++) bitarr->words[i] = le64_to_cpu((uint8_t*)&bitarr->words[i]); // Mask top word _mask_top_word(bitarr); DEBUG_VALIDATE(bitarr); return 1; } // // Hash function // /* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) /* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } /* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. -------------------------------------------------------------------- hashword2() -- same as hashword(), but take two seeds and return two 32-bit values. pc and pb must both be nonnull, and *pc and *pb must both be initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be the same as the return value from hashword(). -------------------------------------------------------------------- */ static void hashword2 ( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t *pc, /* IN: seed OUT: primary hash value */ uint32_t *pb) /* IN: more seed OUT: secondary hash value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; c += *pb; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ *pc=c; *pb=b; } // Pass seed as 0 on first call, pass previous hash value if rehashing due // to a collision // Using bob jenkins hash lookup3 uint64_t bit_array_hash(const BIT_ARRAY* bitarr, uint64_t seed) { uint32_t seed32[2]; memcpy(seed32, &seed, sizeof(uint32_t)*2); // Round up length to number 32bit words hashword2((uint32_t*)bitarr->words, (bitarr->num_of_bits + 31) / 32, &seed32[0], &seed32[1]); // XOR with array length. This ensures arrays with different length but same // contents have different hash values seed ^= bitarr->num_of_bits; return seed; } // // Generally useful functions // // Generalised 'binary to string' function // Adds bits to the string in order of lsb to msb // e.g. 0b11010 (26 in decimal) would come out as "01011" char* bit_array_word2str(const void *ptr, size_t num_of_bits, char *str) { const uint8_t* d = (const uint8_t*)ptr; size_t i; for(i = 0; i < num_of_bits; i++) { uint8_t bit = (d[i/8] >> (i % 8)) & 0x1; str[i] = bit ? '1' : '0'; } str[num_of_bits] = '\0'; return str; } char* bit_array_word2str_rev(const void *ptr, size_t num_of_bits, char *str) { const uint8_t* d = (const uint8_t*)ptr; size_t i; for(i = 0; i < num_of_bits; i++) { uint8_t bit = (d[i/8] >> (i % 8)) & 0x1; str[num_of_bits-1-i] = bit ? '1' : '0'; } str[num_of_bits] = '\0'; return str; } BitArray-2.0/bit_array.h000066400000000000000000000461271266571572500152470ustar00rootroot00000000000000/* bit_array.h project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sep 2014 */ #ifndef BIT_ARRAY_HEADER_SEEN #define BIT_ARRAY_HEADER_SEEN #include #include #include "bit_macros.h" typedef struct BIT_ARRAY BIT_ARRAY; // 64 bit words typedef uint64_t word_t, word_addr_t, bit_index_t; typedef uint8_t word_offset_t; // Offset within a 64 bit word #define BIT_INDEX_MIN 0 #define BIT_INDEX_MAX (~(bit_index_t)0) #ifdef __cplusplus extern "C" { #endif // // Structs // struct BIT_ARRAY { word_t* words; bit_index_t num_of_bits; // Number of words used -- this is just round_up(num_of_bits / 64) // if num_of_bits == 0, this is 0 word_addr_t num_of_words; // For more efficient allocation we use realloc only to double size -- // not for adding every word. Initial size is INIT_CAPACITY_WORDS. word_addr_t capacity_in_words; }; // // Basics: Constructor, destructor, get length, resize // // Constructor - create a new bit array of length nbits BIT_ARRAY* bit_array_create(bit_index_t nbits); // Destructor - free the memory used for a bit array void bit_array_free(BIT_ARRAY* bitarray); // Allocate using existing struct BIT_ARRAY* bit_array_alloc(BIT_ARRAY* bitarr, bit_index_t nbits); void bit_array_dealloc(BIT_ARRAY* bitarr); // Get length of bit array bit_index_t bit_array_length(const BIT_ARRAY* bit_arr); // Change the size of a bit array. Enlarging an array will add zeros // to the end of it. Returns 1 on success, 0 on failure (e.g. not enough memory) char bit_array_resize(BIT_ARRAY* bitarr, bit_index_t new_num_of_bits); // If bitarr length < num_bits, resizes to num_bits char bit_array_ensure_size(BIT_ARRAY* bitarr, bit_index_t ensure_num_of_bits); // Same as above but exit with an error message if out of memory void bit_array_resize_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits); void bit_array_ensure_size_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits); // // Macros // // // Get, set, clear, assign and toggle individual bits // Macros for fast access -- beware: no bounds checking // #define bit_array_get(arr,i) bitset_get((arr)->words, i) #define bit_array_set(arr,i) bitset_set((arr)->words, i) #define bit_array_clear(arr,i) bitset_del((arr)->words, i) #define bit_array_toggle(arr,i) bitset_tgl((arr)->words, i) // c must be 0 or 1 #define bit_array_assign(arr,i,c) bitset_cpy((arr)->words,i,c) // // Get, set, clear, assign and toggle individual bits // "Safe": use assert() to check bounds // // Get the value of a bit (returns 0 or 1) char bit_array_get_bit(const BIT_ARRAY* bitarr, bit_index_t b); void bit_array_set_bit(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_clear_bit(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_toggle_bit(BIT_ARRAY* bitarr, bit_index_t b); // If char c != 0, set bit; otherwise clear bit void bit_array_assign_bit(BIT_ARRAY* bitarr, bit_index_t b, char c); // // "Resizing": enlarge array if needed // char bit_array_rget(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_rset(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_rclear(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_rtoggle(BIT_ARRAY* bitarr, bit_index_t b); void bit_array_rassign(BIT_ARRAY* bitarr, bit_index_t b, char c); // // Set, clear and toggle several bits at once // // Set multiple bits at once. // e.g. set bits 1, 20 & 31: bit_array_set_bits(bitarr, 3, 1,20,31); // Note: variable args are of type unsigned int void bit_array_set_bits(BIT_ARRAY* bitarr, size_t n, ...); // Clear multiple bits at once. // e.g. clear bits 1, 20 & 31: bit_array_clear_bits(bitarr, 3, 1,20,31); // Note: variable args are of type unsigned int void bit_array_clear_bits(BIT_ARRAY* bitarr, size_t n, ...); // Toggle multiple bits at once // e.g. toggle bits 1, 20 & 31: bit_array_toggle_bits(bitarr, 3, 1,20,31); // Note: variable args are of type unsigned int void bit_array_toggle_bits(BIT_ARRAY* bitarr, size_t n, ...); // // Set, clear and toggle all bits in a region // // Set all the bits in a region void bit_array_set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len); // Clear all the bits in a region void bit_array_clear_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len); // Toggle all the bits in a region void bit_array_toggle_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len); // // Set, clear and toggle all bits at once // // Set all bits in this array to 1 void bit_array_set_all(BIT_ARRAY* bitarr); // Set all bits in this array to 0 void bit_array_clear_all(BIT_ARRAY* bitarr); // Set all 1 bits to 0, and all 0 bits to 1 void bit_array_toggle_all(BIT_ARRAY* bitarr); // // Get / set a word of a given size // // First bit is in the least significant bit position // start index must be within the range of the bit array (0 <= x < length) uint64_t bit_array_get_word64(const BIT_ARRAY* bitarr, bit_index_t start); uint32_t bit_array_get_word32(const BIT_ARRAY* bitarr, bit_index_t start); uint16_t bit_array_get_word16(const BIT_ARRAY* bitarr, bit_index_t start); uint8_t bit_array_get_word8(const BIT_ARRAY* bitarr, bit_index_t start); uint64_t bit_array_get_wordn(const BIT_ARRAY* bitarr, bit_index_t start, int n); // Set 64 bits at once from a particular start position // Doesn't extend bit array. However it is safe to TRY to set bits beyond the // end of the array, as long as: `start` is < `bit_array_length(arr)` void bit_array_set_word64(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word); void bit_array_set_word32(BIT_ARRAY* bitarr, bit_index_t start, uint32_t word); void bit_array_set_word16(BIT_ARRAY* bitarr, bit_index_t start, uint16_t word); void bit_array_set_word8(BIT_ARRAY* bitarr, bit_index_t start, uint8_t byte); void bit_array_set_wordn(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word, int n); // // Number of bits set // // Get the number of bits set (hamming weight) bit_index_t bit_array_num_bits_set(const BIT_ARRAY* bitarr); // Get the number of bits not set (length - hamming weight) bit_index_t bit_array_num_bits_cleared(const BIT_ARRAY* bitarr); // Get the number of bits set in on array and not the other. This is equivalent // to hamming weight of the XOR when the two arrays are the same length. // e.g. 10101 vs 00111 => hamming distance 2 (XOR is 10010) bit_index_t bit_array_hamming_distance(const BIT_ARRAY* arr1, const BIT_ARRAY* arr2); // Parity - returns 1 if odd number of bits set, 0 if even char bit_array_parity(const BIT_ARRAY* bitarr); // // Find indices of set/clear bits // // Find the index of the next bit that is set, at or after `offset` // Returns 1 if a bit is set, otherwise 0 // Index of next set bit is stored in the integer pointed to by result // If no next bit is set result is not changed char bit_array_find_next_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result); // Find the index of the next bit that is NOT set, at or after `offset` // Returns 1 if a bit is NOT set, otherwise 0 // Index of next zero bit is stored in the integer pointed to by `result` // If no next bit is zero, value at `result` is not changed char bit_array_find_next_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result); // Find the index of the previous bit that is set, before offset. // Returns 1 if a bit is set, otherwise 0 // Index of previous set bit is stored in the integer pointed to by `result` // If no previous bit is set result is not changed char bit_array_find_prev_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result); // Find the index of the previous bit that is NOT set, before offset. // Returns 1 if a bit is clear, otherwise 0 // Index of previous zero bit is stored in the integer pointed to by `result` // If no previous bit is zero result is not changed char bit_array_find_prev_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result); // Find the index of the first bit that is set. // Returns 1 if a bit is set, otherwise 0 // Index of first set bit is stored in the integer pointed to by `result` // If no bit is set result is not changed char bit_array_find_first_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result); // Find the index of the first bit that is NOT set. // Returns 1 if a bit is clear, otherwise 0 // Index of first zero bit is stored in the integer pointed to by `result` // If no bit is zero result is not changed char bit_array_find_first_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result); // Find the index of the last bit that is set. // Returns 1 if a bit is set, otherwise 0 // Index of last set bit is stored in the integer pointed to by `result` // If no bit is set result is not changed char bit_array_find_last_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result); // Find the index of the last bit that is NOT set. // Returns 1 if a bit is clear, otherwise 0 // Index of last zero bit is stored in the integer pointed to by `result` // If no bit is zero result is not changed char bit_array_find_last_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result); // // Sorting // // Put all the 0s before all the 1s void bit_array_sort_bits(BIT_ARRAY* bitarr); // Put all the 1s before all the 0s void bit_array_sort_bits_rev(BIT_ARRAY* bitarr); // // String and printing methods // // Construct a BIT_ARRAY from a string. void bit_array_from_str(BIT_ARRAY* bitarr, const char* bitstr); // Construct a BIT_ARRAY from a substring with given on and off characters. void bit_array_from_substr(BIT_ARRAY* bitarr, bit_index_t offset, const char* str, size_t len, const char *on, const char *off, char left_to_right); // Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in // length. Terminates string with '\0' char* bit_array_to_str(const BIT_ARRAY* bitarr, char* str); char* bit_array_to_str_rev(const BIT_ARRAY* bitarr, char* str); // Get a string representations for a given region, using given on/off // characters. // Note: does not null-terminate void bit_array_to_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char on, char off, char left_to_right); // Print this array to a file stream. Prints '0's and '1'. Doesn't print // newline. void bit_array_print(const BIT_ARRAY* bitarr, FILE* fout); // Print a string representations for a given region, using given on/off // characters. Reverse prints from highest to lowest -- this is useful for // printing binary numbers void bit_array_print_substr(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char on, char off, char left_to_right); // // Decimal // // Get bit array as decimal str (e.g. 0b1101 -> "13") size_t bit_array_to_decimal(const BIT_ARRAY *bitarr, char *str, size_t len); // Return number of characters used size_t bit_array_from_decimal(BIT_ARRAY *bitarr, const char* decimal); // // Hexidecimal // // Loads array from hex string // Returns the number of bits loaded (will be chars rounded up to multiple of 8) // (0 on failure) bit_index_t bit_array_from_hex(BIT_ARRAY* bitarr, bit_index_t offset, const char* str, size_t len); // Returns number of characters written size_t bit_array_to_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, char* str, char uppercase); // Print bit array as hex size_t bit_array_print_hex(const BIT_ARRAY* bitarr, bit_index_t start, bit_index_t length, FILE* fout, char uppercase); // // Clone and copy // // Copy a BIT_ARRAY struct and the data it holds - returns pointer to new object #define bit_array_dup bit_array_clone BIT_ARRAY* bit_array_clone(const BIT_ARRAY* bitarr); // Copy bits from one array to another // Note: use MACRO bit_array_copy // Destination and source can be the same bit_array and // src/dst regions can overlap void bit_array_copy(BIT_ARRAY* dst, bit_index_t dstindx, const BIT_ARRAY* src, bit_index_t srcindx, bit_index_t length); // copy all of src to dst. dst is resized to match src. void bit_array_copy_all(BIT_ARRAY* dst, const BIT_ARRAY* src); // // Logic operators // // BIT_ARRAYs can all be different or the same object // dest array will be resized if it is too short // void bit_array_and(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2); void bit_array_or (BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2); void bit_array_xor(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2); void bit_array_not(BIT_ARRAY* dest, const BIT_ARRAY* src); // // Comparisons // // Note: (bit_array_cmp(a,b) == 0) <=> (bit_array_cmp_big_endian(a,b) == 0) // comparison functions return: // 1 iff bitarr1 > bitarr2 // 0 iff bitarr1 == bitarr2 // -1 iff bitarr1 < bitarr2 // Compare two bit arrays by value stored, with index 0 being the Least // Significant Bit (LSB). Arrays do not have to be the same length. // Example: ..0101 (5) > ...0011 (3) [index 0 is LSB at right hand side] int bit_array_cmp(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2); // Compare two bit arrays by value stored, with index 0 being the Most // Significant Bit (MSB). Arrays do not have to be the same length. // Example: 10.. > 01.. [index 0 is MSB at left hand side] int bit_array_cmp_big_endian(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2); // compare bitarr with (bitarr2 << pos) int bit_array_cmp_words(const BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *bitarr2); // // Shift, interleave, reverse // // Shift array left/right. If fill is zero, filled with 0, otherwise 1 void bit_array_shift_right(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill); void bit_array_shift_left (BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill); // shift left without losing any bits. Resizes bitarr. void bit_array_shift_left_extend(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill); // Cyclic shift void bit_array_cycle_right(BIT_ARRAY* bitarr, bit_index_t dist); void bit_array_cycle_left (BIT_ARRAY* bitarr, bit_index_t dist); // Interleave // dst cannot point to the same bit array as src1 or src2 // src1, src2 may point to the same bit array // abcd 1234 -> a1b2c3d4 // 0011 0000 -> 00001010 // 1111 0000 -> 10101010 // 0101 1010 -> 01100110 // Extends dst if it is too short, but does not shrink it if it is too long // if dst is longer than length(src1)+length(src2), the end bits are not altered void bit_array_interleave(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2); // Reverse the whole array or part of it void bit_array_reverse(BIT_ARRAY* bitarr); void bit_array_reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len); // // Numeric // // Returns 1 on sucess, 0 if value in array is too big char bit_array_as_num(const BIT_ARRAY* bitarr, uint64_t* result); // 1 iff bitarr > value // 0 iff bitarr == value // -1 iff bitarr < value int bit_array_cmp_uint64(const BIT_ARRAY* bitarr, uint64_t value); // // Arithmetic // // bitarr will be extended if needed void bit_array_add_uint64(BIT_ARRAY* bitarr, uint64_t value); // Add `add` to `bitarr` at `pos` -- same as: // bitarr + (add << pos) // where pos can be bigger than the length of the array (bitarr will be resized) void bit_array_add_word(BIT_ARRAY *bitarr, bit_index_t pos, uint64_t add); // Add `add` to `bitarr` at `pos` void bit_array_add_words(BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *add); // If value is greater than bitarr, bitarr is not changed and 0 is returned // Returns 1 on success, 0 if value > bitarr char bit_array_sub_uint64(BIT_ARRAY* bitarr, uint64_t value); // minus `minus` from `bitarr` at `pos` -- same as: // bitarr + (minus << pos) // Returns 1 on success, 0 if value > bitarr char bit_array_sub_word(BIT_ARRAY *bitarr, bit_index_t pos, word_t minus); // minus `minus` from `bitarr` at `pos` // Returns 1 on success, 0 if value > bitarr char bit_array_sub_words(BIT_ARRAY* bitarr, bit_index_t pos, BIT_ARRAY* minus); // Multiply by some value void bit_array_mul_uint64(BIT_ARRAY *bitarr, uint64_t multiplier); // bitarr = round_down(bitarr / divisor) // rem = bitarr % divisor void bit_array_div_uint64(BIT_ARRAY *bitarr, uint64_t divisor, uint64_t *rem); // // Arithmetic between arrays // // dst = src1 + src2 // src1, src2 and dst can all be the same BIT_ARRAY // If dst is shorter than either of src1, src2, it is enlarged void bit_array_add(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2); // dst = src1 - src2 // src1, src2 and dst can all be the same BIT_ARRAY // If dst is shorter than src1, it will be extended to be as long as src1 // src1 must be greater than or equal to src2 (src1 >= src2) void bit_array_subtract(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2); // dst = src1 * src2 // Pointers cannot all point to the same BIT_ARRAY void bit_array_multiply(BIT_ARRAY *dst, BIT_ARRAY *src1, BIT_ARRAY *src2); // Results in: // quotient = dividend / divisor // dividend = dividend % divisor // (dividend is used to return the remainder) void bit_array_divide(BIT_ARRAY *dividend, BIT_ARRAY *quotient, BIT_ARRAY *divisor); // // Read/Write bit_array to a file // // File format is [8 bytes: for number of elements in array][data] // Number of bytes of data is: (int)((num_of_bits + 7) / 8) // // Saves bit array to a file // returns the number of bytes written bit_index_t bit_array_save(const BIT_ARRAY* bitarr, FILE* f); // Reads bit array from a file. bitarr is resized and filled. // Returns 1 on success, 0 on failure char bit_array_load(BIT_ARRAY* bitarr, FILE* f); // // Hash function // // Pass seed as 0 on first call, pass previous hash value if rehashing due // to a collision // Using bob jenkins hash lookup3 uint64_t bit_array_hash(const BIT_ARRAY* bitarr, uint64_t seed); // // Randomness // // Set bits randomly with probability prob : 0 <= prob <= 1 void bit_array_random(BIT_ARRAY* bitarr, float prob); // Shuffle the bits in an array randomly void bit_array_shuffle(BIT_ARRAY* bitarr); // Get the next permutation of an array with a fixed size and given number of // bits set. Also known as next lexicographic permutation. // Given a bit array find the next lexicographic orginisation of the bits // Number of possible combinations given by (size choose bits_set) i.e. nCk // 00011 -> 00101 -> 00110 -> 01001 -> 01010 -> // 01100 -> 10001 -> 10010 -> 10100 -> 11000 -> 00011 (back to start) void bit_array_next_permutation(BIT_ARRAY* bitarr); // // Generally useful functions // // Generalised 'binary to string' function // Adds bits to the string in order of lsb to msb // e.g. 0b11010 (26 in decimal) would come out as "01011" char* bit_array_word2str(const void *ptr, size_t num_of_bits, char *str); // Same as above but in reverse char* bit_array_word2str_rev(const void *ptr, size_t num_of_bits, char *str); #ifdef __cplusplus } #endif #endif BitArray-2.0/bit_macros.h000066400000000000000000000227411266571572500154110ustar00rootroot00000000000000/* bit_macros.h project: bit array C library url: https://github.com/noporpoise/BitArray/ author: Isaac Turner license: Public Domain, no warranty date: Dec 2013 */ #ifndef BITSET_H_ #define BITSET_H_ #include #include // trailing_zeros is number of least significant zeros // leading_zeros is number of most significant zeros #if defined(_WIN32) #define trailing_zeros(x) ({ __typeof(x) _r; _BitScanReverse64(&_r, x); _r; }) #define leading_zeros(x) ({ __typeof(x) _r; _BitScanForward64(&_r, x); _r; }) #else #define trailing_zeros(x) ((x) ? (__typeof(x))__builtin_ctzll(x) : (__typeof(x))sizeof(x)*8) #define leading_zeros(x) ((x) ? (__typeof(x))__builtin_clzll(x) : (__typeof(x))sizeof(x)*8) #endif // Get index of top set bit. If x is 0 return nbits #define top_set_bit(x) ((x) ? sizeof(x)*8-leading_zeros(x)-1 : sizeof(x)*8) #define roundup_bits2bytes(bits) (((bits)+7)/8) #define roundup_bits2words32(bits) (((bits)+31)/32) #define roundup_bits2words64(bits) (((bits)+63)/64) // Round a number up to the nearest number that is a power of two #define roundup2pow(x) (1UL << (64 - leading_zeros(x))) #define rot32(x,r) (((x)<<(r)) | ((x)>>(32-(r)))) #define rot64(x,r) (((x)<<(r)) | ((x)>>(64-(r)))) // need to check for length == 0, undefined behaviour if uint64_t >> 64 etc #define bitmask(nbits,type) ((nbits) ? ~(type)0 >> (sizeof(type)*8-(nbits)): (type)0) #define bitmask32(nbits) bitmask(nbits,uint32_t) #define bitmask64(nbits) bitmask(nbits,uint64_t) // A possibly faster way to combine two words with a mask //#define bitmask_merge(a,b,abits) ((a & abits) | (b & ~abits)) #define bitmask_merge(a,b,abits) (b ^ ((a ^ b) & abits)) // Swap lowest four bits. A nibble is 4 bits (i.e. half a byte) #define rev_nibble(x) ((((x)&1)<<3)|(((x)&2)<<1)|(((x)&4)>>1)|(((x)&8)>>3)) // // Bit array (bitset) // // bitsetX_wrd(): get word for a given position // bitsetX_idx(): get index within word for a given position #define _VOLPTR(x) ((volatile __typeof(x) *)(&(x))) #define _VOLVALUE(x) (*_VOLPTR(x)) #define _TYPESHIFT(arr,word,shift) \ ((__typeof(*(arr)))((__typeof(*(arr)))(word) << (shift))) #define bitsetX_wrd(wrdbits,pos) ((pos) / (wrdbits)) #define bitsetX_idx(wrdbits,pos) ((pos) % (wrdbits)) #define bitset32_wrd(pos) ((pos) >> 5) #define bitset32_idx(pos) ((pos) & 31) #define bitset64_wrd(pos) ((pos) >> 6) #define bitset64_idx(pos) ((pos) & 63) // // Bit functions on arrays // #define bitset2_get(arr,wrd,idx) (((arr)[wrd] >> (idx)) & 0x1) #define bitset2_set(arr,wrd,idx) ((arr)[wrd] |= _TYPESHIFT(arr,1,idx)) #define bitset2_del(arr,wrd,idx) ((arr)[wrd] &=~ _TYPESHIFT(arr,1,idx)) #define bitset2_tgl(arr,wrd,idx) ((arr)[wrd] ^= _TYPESHIFT(arr,1,idx)) #define bitset2_or(arr,wrd,idx,bit) ((arr)[wrd] |= _TYPESHIFT(arr,bit,idx)) #define bitset2_xor(arr,wrd,idx,bit) ((arr)[wrd] = ~((arr)[wrd] ^ (~_TYPESHIFT(arr,bit,idx)))) #define bitset2_and(arr,wrd,idx,bit) ((arr)[wrd] &= (_TYPESHIFT(arr,bit,idx) | ~_TYPESHIFT(arr,1,idx))) #define bitset2_cpy(arr,wrd,idx,bit) ((arr)[wrd] = ((arr)[wrd] &~ _TYPESHIFT(arr,1,idx)) | _TYPESHIFT(arr,bit,idx)) // // Thread safe versions // // They return the value of the bit (0 or 1) before it was updated #define bitset2_get_mt(arr,wrd,idx) bitset2_get(_VOLPTR(*(arr)),wrd,idx) #define bitset2_set_mt(arr,wrd,idx) ((__sync_fetch_and_or (_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,1,idx)) >> (idx))&1) #define bitset2_del_mt(arr,wrd,idx) ((__sync_fetch_and_and(_VOLPTR((arr)[wrd]), ~_TYPESHIFT(arr,1,idx)) >> (idx))&1) #define bitset2_tgl_mt(arr,wrd,idx) ((__sync_fetch_and_xor(_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,1,idx)) >> (idx))&1) #define bitset2_or_mt(arr,wrd,idx,bit) ((__sync_fetch_and_or (_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,bit,idx)) >> (idx))&1) #define bitset2_xor_mt(arr,wrd,idx,bit) ((__sync_fetch_and_xor(_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,bit,idx)) >> (idx))&1) #define bitset2_and_mt(arr,wrd,idx,bit) ((__sync_fetch_and_and(_VOLPTR((arr)[wrd]), (_TYPESHIFT(arr,bit,idx) | ~_TYPESHIFT(arr,1,idx))) >> (idx))&1) #define bitset2_cpy_mt(arr,wrd,idx,bit) ((bit) ? bitset2_set_mt(arr,wrd,idx) : bitset2_del_mt(arr,wrd,idx)) // // Auto detect size of type from pointer // #define bitset_wrd(arr,pos) bitsetX_wrd(sizeof(*(arr))*8,pos) #define bitset_idx(arr,pos) bitsetX_idx(sizeof(*(arr))*8,pos) #define bitset_op(func,arr,pos) func(arr, bitset_wrd(arr,pos), bitset_idx(arr,pos)) #define bitset_op2(func,arr,pos,bit) func(arr, bitset_wrd(arr,pos), bitset_idx(arr,pos), bit) // Auto-detect type size: bit functions #define bitset_get(arr,pos) bitset_op(bitset2_get, arr, pos) #define bitset_set(arr,pos) bitset_op(bitset2_set, arr, pos) #define bitset_del(arr,pos) bitset_op(bitset2_del, arr, pos) #define bitset_tgl(arr,pos) bitset_op(bitset2_tgl, arr, pos) #define bitset_or(arr,pos,bit) bitset_op2(bitset2_or, arr, pos, bit) #define bitset_xor(arr,pos,bit) bitset_op2(bitset2_xor, arr, pos, bit) #define bitset_and(arr,pos,bit) bitset_op2(bitset2_and, arr, pos, bit) #define bitset_cpy(arr,pos,bit) bitset_op2(bitset2_cpy, arr, pos, bit) // Auto-detect type size: thread safe bit functions // They return the value of the bit (0 or 1) before it was updated #define bitset_get_mt(arr,pos) bitset_op(bitset2_get_mt, arr, pos) #define bitset_set_mt(arr,pos) bitset_op(bitset2_set_mt, arr, pos) #define bitset_del_mt(arr,pos) bitset_op(bitset2_del_mt, arr, pos) #define bitset_tgl_mt(arr,pos) bitset_op(bitset2_tgl_mt, arr, pos) #define bitset_or_mt(arr,pos,bit) bitset_op2(bitset2_or_mt, arr, pos, bit) #define bitset_xor_mt(arr,pos,bit) bitset_op2(bitset2_xor_mt, arr, pos, bit) #define bitset_and_mt(arr,pos,bit) bitset_op2(bitset2_and_mt, arr, pos, bit) #define bitset_cpy_mt(arr,pos,bit) bitset_op2(bitset2_cpy_mt, arr, pos, bit) // Clearing a word does not return a meaningful value #define bitset_clear_word(arr,pos) ((arr)[bitset_wrd(arr,pos)] = 0) #define bitset_clear_word_mt(arr,pos) (_VOLVALUE((arr)[bitset_wrd(arr,pos)]) = 0) // // Compact bit array of spin locks // These are most effecient when arr is of type: volatile char* // // Acquire a lock #define bitlock_acquire_block(arr,pos,wait,abandon) do { \ size_t _w = bitset_wrd(arr,pos); \ __typeof(*(arr)) _o, _n, _b = _TYPESHIFT(arr, 1, bitset_idx(arr,pos)); \ do { \ while((_o = _VOLVALUE((arr)[_w])) & _b) { wait } \ abandon \ _n = _o | _b; \ } while(!__sync_bool_compare_and_swap(_VOLPTR((arr)[_w]), _o, _n)); \ __sync_synchronize(); /* Must not move commands to before acquiring lock */ \ } while(0) // Undefined behaviour if you do not already hold the lock #define bitlock_release(arr,pos) do { \ size_t _w = bitset_wrd(arr,pos); \ __typeof(*(arr)) _mask = ~_TYPESHIFT(arr, 1, bitset_idx(arr,pos)); \ __sync_synchronize(); /* Must get the lock before releasing it */ \ __sync_and_and_fetch(_VOLPTR((arr)[_w]), _mask); \ } while(0) #define bitlock_acquire(arr,pos) bitlock_acquire_block(arr,pos,{},{}) // calls yield if cannot acquire the lock #define bitlock_yield_acquire(arr,pos) bitlock_acquire_block(arr,pos,sched_yield();,{}) // Block until we get the lock or someone else does // sets the memory pointed to by retptr to 1 if we got the lock, 0 otherwise #define bitlock_try_acquire(arr,pos,retptr) do { \ *(retptr) = 1; /* default to success, set to zero if locked */ \ bitlock_acquire_block(arr,pos,{*(retptr)=0;break;},if(!*(retptr)){break;}); \ } while(0) /* * Byteswapping */ /* clang uses these to check for features */ #ifndef __has_feature #define __has_feature(x) 0 #endif #ifndef __has_builtin #define __has_builtin(x) 0 #endif /* GCC versions < 4.3 do not have __builtin_bswapX() */ #if ( defined(__clang__) && !__has_builtin(__builtin_bswap64) ) || \ ( !defined(__clang__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && \ ( (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)) ) #define byteswap64(x) ( (((uint64_t)(x) << 56)) | \ (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \ (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \ (((uint64_t)(x) << 8) & 0xff00000000ULL) | \ (((uint64_t)(x) >> 8) & 0xff000000ULL) | \ (((uint64_t)(x) >> 24) & 0xff0000ULL) | \ (((uint64_t)(x) >> 40) & 0xff00ULL) | \ (((uint64_t)(x) >> 56)) ) #define byteswap32(x) ( (((uint32_t)(x) << 24)) | \ (((uint32_t)(x) << 8) & 0xff0000U) | \ (((uint32_t)(x) >> 8) & 0xff00U) | \ (((uint32_t)(x) >> 24)) ) /* uint16_t type might be bigger than 2 bytes, so need to mask */ #define byteswap16(x) ( (((uint16_t)(x) & 0xff) << 8) | \ (((uint16_t)(x) >> 8) & 0xff) ) #else #define byteswap64(x) __builtin_bswap64(x) #define byteswap32(x) __builtin_bswap64(x) #define byteswap16(x) __builtin_bswap64(x) #endif #endif /* BITLOCK_H_ */ BitArray-2.0/dev/000077500000000000000000000000001266571572500136665ustar00rootroot00000000000000BitArray-2.0/dev/Makefile000066400000000000000000000016121266571572500153260ustar00rootroot00000000000000ifndef CC CC = gcc endif ifdef DEBUG OPT = -DDEBUG=1 --debug -g else OPT = -O3 endif CFLAGS = -Wall -Wextra -Wc++-compat all: bit_array_test bitlock_test bitlock_try_test bit_array_generate bit_array_test: bit_array_test.c ../bar.h ../libbitarr.a $(CC) $(OPT) $(CFLAGS) -I.. -L.. -o bit_array_test bit_array_test.c -lbitarr bitlock_test: bitlock_test.c ../bit_macros.h $(CC) $(OPT) $(CFLAGS) -I.. -o bitlock_test bitlock_test.c -lpthread bitlock_try_test: bitlock_try_test.c ../bit_macros.h $(CC) $(OPT) $(CFLAGS) -I.. -o bitlock_try_test bitlock_try_test.c -lpthread bit_array_generate: $(CC) $(OPT) $(CFLAGS) -o bit_array_generate bit_array_generate.c ../libbitarr.a: cd .. && make test: bit_array_test bitlock_test ./bit_array_test && ./bitlock_test clean: rm -rf bit_array_test bitlock_test bit_array_generate rm -rf bitarr_example.dump *.o *.dSYM *.greg .PHONY: all clean test BitArray-2.0/dev/bit_array_generate.c000066400000000000000000000056541266571572500176720ustar00rootroot00000000000000/* dev/bit_array_generate.c project: bit array C library url: https://github.com/noporpoise/BitArray/ author: Isaac Turner license: Public Domain, no warranty date: Dec 2013 */ // 64 bit words // Array length can be zero // Unused top bits must be zero #include #include #include #include #include // memset #include uint8_t reverse(uint8_t b) { uint8_t r = 0; int i; for(i = 0; i < 8; i++) { r <<= 1; r |= b & 0x1; b >>= 1; } return r; } // Print table of bytes -> byte reverse void generate_reverse() { int i; printf("static const word_t reverse_table[256] = \n{\n "); for(i = 0; i < 256; i++) { if(i % 8 == 0 && i > 0) printf("\n "); printf(" 0x%02X%c", reverse(i), i == 255 ? '\n' : ','); } printf("};\n\n"); } uint16_t morton(uint8_t b) { uint16_t r = 0; int i; for(i = 0; i < 8; i++) { r |= (b & 0x1) << (2*i); b >>= 1; } return r; } // a is either 0 or 1, and is how much to shift by void generate_morton(char a) { int i; printf("static const word_t morton_table%c[256] = \n{\n ", a ? '1' : '0'); for(i = 0; i < 256; i++) { if(i % 8 == 0 && i > 0) printf("\n "); uint16_t m = morton(i); if(a) m <<= 1; printf(" 0x%04X%c", m, i == 255 ? '\n' : ','); } printf("};\n\n"); } unsigned int next_permutation(unsigned int v) { unsigned int t = v | (v - 1); // t gets v's least significant 0 bits set to 1 // Next set to 1 the most significant bit to change, // set to 0 the least significant ones, and add the necessary 1 bits. //return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1)); return (t+1) | (((~t & (t+1)) - 1) >> (__builtin_ctz(v) + 1)); } long factorial(int k) { long r = k; for(k--; k > 1; k--) { r *= k; } return r; } void generate_shuffle() { int i; printf("static const uint8_t shuffle_permutations[9] = {1"); for(i = 1; i < 8; i++) { // nCk = n! / ((n-k)!k!) long combinations = factorial(8) / (factorial(8-i)*factorial(i)); printf(", %li", combinations); } printf(", 1};\n"); printf("static const uint8_t shuffle[9][70] = {\n"); for(i = 0; i <= 8; i++) { printf(" // %i bits set\n", i); unsigned char init = i == 0 ? 0 : (0x1 << i) - 1; printf(" {0x%02X", (int)init); unsigned int c = next_permutation(init); int num; for(num = 1; num < 70; c = next_permutation(c), num++) { printf(num % 10 == 0 ? ",\n " : ", "); printf("0x%02X", c <= 255 ? (int)c : 0); } printf(i < 8 ? "},\n" : "}\n};\n\n"); } } int main() { printf("// byte reverse look up table\n"); generate_reverse(); printf("// Morton table for interleaving bytes\n"); generate_morton(0); printf("// Morton table for interleaving bytes, shifted left 1 bit\n"); generate_morton(1); printf("// Tables for shuffling\n"); generate_shuffle(); return 1; } BitArray-2.0/dev/bit_array_test.c000066400000000000000000001636771266571572500170710ustar00rootroot00000000000000/* dev/bit_array_test.c project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sep 2014 */ #include #include #include #include #include #include // needed for rand() #include // need for getpid() for getting setting rand number #include "bit_array.h" // Constants const char test_filename[] = "bitarr_example.dump"; #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MIN(x,y) ((x) <= (y) ? (x) : (y)) #define RAND(x) (((x) * (long)rand())/RAND_MAX) // // Tests // const char *suite_name; char suite_pass; int suites_run = 0, suites_failed = 0, suites_empty = 0; int tests_in_suite = 0, tests_run = 0, tests_failed = 0; #define QUOTE(str) #str #define ASSERT(x) {tests_run++; tests_in_suite++; if(!(x)) \ { fprintf(stderr, "failed assert [%s:%i] %s\n", __FILE__, __LINE__, QUOTE(x)); \ suite_pass = 0; tests_failed++; }} void SUITE_START(const char *name) { suite_pass = 1; suite_name = name; suites_run++; tests_in_suite = 0; } void SUITE_END() { printf("Testing %s ", suite_name); size_t suite_i; for(suite_i = strlen(suite_name); suite_i < 80-8-5; suite_i++) printf("."); printf("%s\n", suite_pass ? " pass" : " fail"); if(!suite_pass) suites_failed++; if(!tests_in_suite) suites_empty++; } // // Utility functions // #define die(fmt,...) do { \ fprintf(stderr, "[%s:%i] Error: %s() "fmt"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ exit(EXIT_FAILURE); \ } while(0); void reverse_str(char *str) { size_t s, len = strlen(str); for(s = 0; s < len / 2; s++) { char tmp = str[s]; str[s] = str[len-s-1]; str[len-s-1] = tmp; } } word_t word_from_str(char *str) { word_t w = 0; int i; for(i = 0; i < 64 && str[i] != '\0'; i++) { w <<= 1; if(str[i] == '1') { w |= 1; } } return w; } // // Testing per function // void _test_copy(BIT_ARRAY *arr2, bit_index_t to, BIT_ARRAY *arr1, bit_index_t from, bit_index_t len) { char *str1 = (char*)malloc((bit_array_length(arr1)+1) * sizeof(char)); char *corr = (char*)malloc((bit_array_length(arr2)+to+len+1) * sizeof(char)); bit_array_to_str(arr1, str1); bit_array_to_str(arr2, corr); size_t arr2_len = strlen(corr); memmove(corr+to, str1+from, len * sizeof(char)); if(to+len > arr2_len) { corr[to+len] = '\0'; } // do copy bit_array_copy(arr2, to, arr1, from, len); char *str2 = (char*)malloc((bit_array_length(arr2)+1) * sizeof(char)); bit_array_to_str(arr2, str2); // compare ASSERT(strcmp(str2, corr) == 0); if(strcmp(str2, corr) != 0) { // debug output printf("str1: %s\n", str1); printf("str2: %s\n", str2); printf("corr: %s\n", corr); } free(str1); free(str2); free(corr); } void test_copy() { SUITE_START("copy"); BIT_ARRAY *arr = bit_array_create(200); bit_array_set_region(arr, 0, 20); _test_copy(arr, 30, arr, 0, 15); _test_copy(arr, 50, arr, 0, 50); _test_copy(arr, 100, arr, 0, 100); bit_index_t len = bit_array_length(arr); int shift = 3; bit_array_resize(arr, len + shift); _test_copy(arr, shift, arr, 0, len); _test_copy(arr, 0, arr, shift, len); bit_array_free(arr); SUITE_END(); } void test_arithmetic() { printf("== testing arithmetic ==\n"); char tmp[101]; BIT_ARRAY* arr1 = bit_array_create(100); BIT_ARRAY* arr2 = bit_array_create(100); int i = 0; for(i = 0; i < 99; i+=3) { bit_array_set_bit(arr1, i); bit_array_set_bit(arr2, i); bit_array_set_bit(arr2, i+1); } printf("Init:\n"); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("arr2: %s\n", bit_array_to_str(arr2, tmp)); printf("Increment: arr1++\n"); bit_array_add_uint64(arr1, 1); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Decrement: arr1--\n"); bit_array_sub_uint64(arr1, 1); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Sum: arr1 = arr1 + arr2\n"); bit_array_add(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Difference: arr1 = arr1 - arr2\n"); bit_array_subtract(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Difference: arr1 = arr1 - arr1\n"); bit_array_subtract(arr1, arr1, arr1); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Sum: arr1 = arr1 + arr2\n"); bit_array_add(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("Sum: arr1 = arr1 + arr2\n"); bit_array_add(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); bit_array_free(arr1); bit_array_free(arr2); printf("== End of testing arithmetic ==\n\n"); } void _check_first_last_bit_set(BIT_ARRAY* arr, char not_zero, bit_index_t first, bit_index_t last) { bit_index_t first_bit = 0, last_bit = 0; char bit_set0 = bit_array_find_first_set_bit(arr, &first_bit); char bit_set1 = bit_array_find_last_set_bit(arr, &last_bit); ASSERT(not_zero == bit_set0); ASSERT(not_zero == bit_set1); if(not_zero) { ASSERT(first_bit == first); ASSERT(last_bit == last); } else { ASSERT(first_bit == 0); ASSERT(last_bit == 0); } } void test_first_last_bit_set() { SUITE_START("first/last bit set"); BIT_ARRAY *arr = bit_array_create(100); _check_first_last_bit_set(arr, 0, 0, 0); bit_array_set_bits(arr, 6, 0, 5, 24, 64, 80, 99); _check_first_last_bit_set(arr, 1, 0, 99); bit_array_clear_bits(arr, 2, 0, 99); _check_first_last_bit_set(arr, 1, 5, 80); bit_array_clear_bits(arr, 2, 5, 80); _check_first_last_bit_set(arr, 1, 24, 64); bit_array_clear_bits(arr, 2, 24, 64); _check_first_last_bit_set(arr, 0, 0, 0); bit_array_set_bit(arr, 0); _check_first_last_bit_set(arr, 1, 0, 0); const int len = 9; bit_index_t set[] = {0, 1, 31, 62, 63, 64, 65, 98, 99}; int i; for(i = 0; i < len; i++) { bit_index_t pos = set[i]; bit_array_clear_all(arr); bit_array_set_bit(arr, pos); _check_first_last_bit_set(arr, 1, pos, pos); } bit_array_free(arr); SUITE_END(); } // Check finding set/clear bits return the same results bit_index_t _test_find_next_bit(BIT_ARRAY *arr, bit_index_t offset, bit_index_t *result) { bit_index_t pos1 = 0, pos2 = 0; char found1, found2; found1 = bit_array_find_next_set_bit(arr, offset, &pos1); bit_array_toggle_all(arr); found2 = bit_array_find_next_clear_bit(arr, offset, &pos2); bit_array_toggle_all(arr); // printf("next (%i,%zu) vs (%i,%zu)\n", (int)found1, (size_t)pos1, // (int)found2, (size_t)pos2); ASSERT(found1 == found2); ASSERT(pos1 == pos2); *result = pos1; return found1; } // Check finding set/clear bits return the same results bit_index_t _test_find_prev_bit(BIT_ARRAY *arr, bit_index_t offset, bit_index_t *result) { bit_index_t pos1 = 0, pos2 = 0; char found1, found2; found1 = bit_array_find_prev_set_bit(arr, offset, &pos1); bit_array_toggle_all(arr); found2 = bit_array_find_prev_clear_bit(arr, offset, &pos2); bit_array_toggle_all(arr); // printf("prev (%i,%zu) vs (%i,%zu)\n", (int)found1, (size_t)pos1, // (int)found2, (size_t)pos2); ASSERT(found1 == found2); ASSERT(pos1 == pos2); *result = pos1; return found1; } void test_next_prev_bit_set() { SUITE_START("next/prev bit set"); BIT_ARRAY *arr = bit_array_create(100); bit_index_t pos = 0; char found; // Simple test // Nothing set found = _test_find_next_bit(arr, 0, &pos); ASSERT(!found && pos == 0); found = _test_find_next_bit(arr, 50, &pos); ASSERT(!found && pos == 0); found = _test_find_next_bit(arr, 99, &pos); ASSERT(!found && pos == 0); found = _test_find_prev_bit(arr, 0, &pos); ASSERT(!found && pos == 0); found = _test_find_prev_bit(arr, 50, &pos); ASSERT(!found && pos == 0); found = _test_find_prev_bit(arr, 100, &pos); ASSERT(!found && pos == 0); bit_array_set_bit(arr, 0); found = _test_find_prev_bit(arr, 0, &pos); ASSERT(!found && pos == 0); found = _test_find_next_bit(arr, 0, &pos); ASSERT(found && pos == 0); bit_array_set_bit(arr, 99); found = _test_find_prev_bit(arr, 99, &pos); ASSERT(found && pos == 0); found = _test_find_next_bit(arr, 99, &pos); ASSERT(found && pos == 99); bit_array_set_bits(arr, 3, 10, 20, 64); found = _test_find_prev_bit(arr, 99, &pos); ASSERT(found && pos == 64); found = _test_find_prev_bit(arr, 64, &pos); ASSERT(found && pos == 20); found = _test_find_next_bit(arr, 1, &pos); ASSERT(found && pos == 10); found = _test_find_next_bit(arr, 11, &pos); ASSERT(found && pos == 20); // Automated test bit_index_t indices[] = {0, 1, 2, 5, 24, 50, 51, 64, 80, 99}; size_t i, n, num_idx = sizeof(indices)/sizeof(indices[0]); // Loop over setting diff numbers of bits - check next_bit_set for(n = 0; n <= num_idx; n++) { bit_array_clear_all(arr); for(i = 0; i < n; i++) bit_array_set_bit(arr, indices[i]); for(i = 0; i < n; i++) { found = _test_find_next_bit(arr, indices[i], &pos); ASSERT(found == 1); ASSERT(pos == indices[i]); // Check from bit after index if in range if(indices[i]+1 < arr->num_of_bits) { found = _test_find_next_bit(arr, indices[i]+1, &pos); if(i+1 < n) { ASSERT(found == 1); ASSERT(pos == indices[i+1]); } else { ASSERT(found == 0); } } } } // Loop over setting diff numbers of bits - check prev_bit_set for(n = 0; n <= num_idx; n++) { bit_array_clear_all(arr); for(i = 0; i < n; i++) bit_array_set_bit(arr, indices[num_idx-i-1]); for(i = 0; i < n; i++) { found = _test_find_prev_bit(arr, indices[num_idx-i-1], &pos); if(i+1 < n) { ASSERT(found == 1); ASSERT(pos == indices[num_idx-i-2]); } else { ASSERT(found == 0); } } } bit_array_free(arr); SUITE_END(); } void test_parity() { SUITE_START("parity"); BIT_ARRAY* arr = bit_array_create(100); ASSERT(bit_array_parity(arr) == 0); bit_array_set_bits(arr, 6, 0, 5, 24, 64, 80, 99); ASSERT(bit_array_parity(arr) == 0); bit_array_clear_bit(arr, 24); ASSERT(bit_array_parity(arr) == 1); bit_array_clear_bit(arr, 99); ASSERT(bit_array_parity(arr) == 0); bit_array_clear_bit(arr, 0); ASSERT(bit_array_parity(arr) == 1); bit_array_free(arr); SUITE_END(); } void _test_interleave(BIT_ARRAY* result, BIT_ARRAY *arr1, BIT_ARRAY *arr2) { bit_array_interleave(result, arr1, arr2); size_t result_len = bit_array_length(result); char *result_str = (char*)malloc((result_len+1) * sizeof(char)); bit_array_to_str(result, result_str); size_t len1 = bit_array_length(arr1); size_t len2 = bit_array_length(arr2); char *correct = (char*)malloc(result_len+1); memset(correct, '0', sizeof(char) * result_len); correct[result_len] = '\0'; size_t i, j = 0; size_t len = MAX(len1,len2); for(i = 0; i < len; i++) { if(i < len1) correct[j++] = bit_array_get_bit(arr1, i) ? '1' : '0'; if(i < len2) correct[j++] = bit_array_get_bit(arr2, i) ? '1' : '0'; } ASSERT(strcmp(correct, result_str) == 0); if(strcmp(correct, result_str) != 0) { // debug output printf("corr: %s\n", correct); printf("got : %s\n", result_str); } free(correct); free(result_str); } void test_interleave() { SUITE_START("interleave"); BIT_ARRAY* arr1 = bit_array_create(10); BIT_ARRAY* arr2 = bit_array_create(10); BIT_ARRAY* result = bit_array_create(0); bit_array_set_all(arr1); _test_interleave(result, arr1, arr2); bit_array_clear_all(arr1); bit_array_set_all(arr2); bit_array_resize(result, 25); _test_interleave(result, arr1, arr2); bit_array_set_all(arr1); bit_array_set_all(arr2); _test_interleave(result, arr1, arr2); bit_array_clear_all(arr1); bit_array_clear_all(arr2); _test_interleave(result, arr1, arr2); bit_array_resize(arr1, 100); bit_array_resize(arr2, 100); bit_array_clear_all(arr1); bit_array_set_all(arr2); _test_interleave(result, arr1, arr2); bit_array_free(arr1); bit_array_free(arr2); bit_array_free(result); SUITE_END(); } int cmp_strings(const char *str1, const char *str2, char rev) { size_t len1 = strlen(str1); size_t len2 = strlen(str2); size_t max_len = MAX(len1, len2); if(rev) { size_t i; for(i = 0; i < max_len; i++) { char a = (i < len1 ? str1[i] : '0'); char b = (i < len2 ? str2[i] : '0'); if(a != b) { return a > b ? 1 : -1; } } } else { size_t i; for(i = max_len; i > 0; i--) { char a = (i < len1 ? str1[i] : '0'); char b = (i < len2 ? str2[i] : '0'); if(a != b) { return str1[i] > str2[i] ? 1 : -1; } else if(i == 0) { break; } } } return len1 > len2 ? 1 : (len1 < len2 ? -1 : 0); } void _test_cmp(const char *str1, const char *str2, char rev) { // Get correct answer int cmp_correct = cmp_strings(str1, str2, rev); BIT_ARRAY* arr1 = bit_array_create(0); BIT_ARRAY* arr2 = bit_array_create(0); bit_array_from_str(arr1, str1); bit_array_from_str(arr2, str2); int cmp1, cmp2; if(rev) { cmp1 = bit_array_cmp_big_endian(arr1, arr2); cmp2 = bit_array_cmp_big_endian(arr2, arr1); } else { cmp1 = bit_array_cmp(arr1, arr2); cmp2 = bit_array_cmp(arr2, arr1); } cmp1 = (cmp1 < 0 ? -1 : (cmp1 > 0 ? 1 : 0)); ASSERT(cmp1 == cmp_correct); cmp2 = (cmp2 < 0 ? -1 : (cmp2 > 0 ? 1 : 0)); ASSERT(cmp2 == -cmp_correct); bit_array_free(arr1); bit_array_free(arr2); } void _test_cmps(char rev) { // Remember right hand side is msb _test_cmp("011010100", "001101010", rev); _test_cmp("0", "00", rev); _test_cmp("", "", rev); _test_cmp("100000", "10", rev); _test_cmp("11000000000000000000000000000000000000000000010000000000000000" "00000000000000000000000000000000001000000000000000000000000000" "00000000000000000000000100000000000000000000000000000000000000" "000000000000100000", "1000000000000000000000000000000000000000" "00000001000000000000000000000000000000000000000000000000001000" "00000000000000000000000000000000000000000000000100000000000000" "00000000000000000000000000000000000010000000000000000000000000" "0000000000000000000000000100000", 1); /* // Some random tests size_t i, j; char str1[1000], str2[1000]; for(i = 0; i < 20; i++) { size_t len1 = RAND(600); size_t len2 = RAND(600); for(j = 0; j < len1; j++) { str1[j] = rand() > RAND_MAX / 2 ? '1' : '0'; } str1[len1] = '\0'; for(j = 0; j < len2; j++) { str2[j] = rand() > RAND_MAX / 2 ? '1' : '0'; } str2[len2] = '\0'; _test_cmp(str1, str2, rev); }*/ } void test_compare() { SUITE_START("compare"); _test_cmps(0); SUITE_END(); } void test_compare2() { SUITE_START("compare (rev endian)"); _test_cmps(1); SUITE_END(); } /* void test_hash() { printf("== Testing hash ==\n"); BIT_ARRAY* arr = bit_array_create(0); char str[200]; printf("arr: %s\n", bit_array_to_str(arr, str)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_resize(arr, 10); printf("arr: %s\n", bit_array_to_str(arr, str)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_set_bits(arr, 3, 5,7,9); printf("arr: %s\n", bit_array_to_str(arr, str)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_resize(arr, 80); printf("arr: %s\n", bit_array_to_str(arr, str)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_set_bits(arr, 3, 50,57,59); printf("arr: %s\n", bit_array_to_str(arr, str)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_resize(arr, 1000); printf("len: %lu\n", (unsigned long)bit_array_length(arr)); printf("hash: %lu\n", (unsigned long)bit_array_hash(arr, 0)); printf("--\n"); bit_array_free(arr); printf("== End of testing hash ==\n\n"); } */ void _test_reverse(int len) { char str[1000], str2[1000]; BIT_ARRAY* arr = bit_array_create(len); bit_array_random(arr, 0.5f); bit_array_to_str(arr, str); reverse_str(str); bit_array_reverse(arr); bit_array_to_str(arr, str2); ASSERT(strcmp(str, str2) == 0); if(strcmp(str, str2) != 0) { // debug output printf("strrev: %s\n", str); printf("arrrev: %s\n", str2); } bit_array_free(arr); } void test_reverse() { SUITE_START("reverse"); _test_reverse(0); _test_reverse(10); _test_reverse(63); _test_reverse(64); _test_reverse(65); _test_reverse(100); _test_reverse(128); _test_reverse(506); SUITE_END(); } void _test_toggle_region(BIT_ARRAY *arr, size_t start, size_t region_len) { bit_index_t len = bit_array_length(arr); char *str1 = (char*)malloc((len+1) * sizeof(char)); bit_array_to_str(arr, str1); // Toggle string size_t i; for(i = start; i < start+region_len; i++) { str1[i] = (str1[i] == '1' ? '0' : '1'); } // Toggle array char *str2 = (char*)malloc((len+1) * sizeof(char)); bit_array_toggle_region(arr, start, region_len); bit_array_to_str(arr, str2); ASSERT(strcmp(str1, str2) == 0); free(str1); free(str2); } void test_toggle() { SUITE_START("toggle"); BIT_ARRAY* arr = bit_array_create(0); _test_toggle_region(arr, 0, 0); bit_array_resize(arr, 10); bit_array_set_bits(arr, 4, 2,3,6,9); _test_toggle_region(arr, 3, 6); _test_toggle_region(arr, 0, 10); bit_array_resize(arr, 80); bit_array_set_bits(arr, 8, 0,20,50,62,64,70,75,79); _test_toggle_region(arr, 25, 50); bit_array_resize(arr, 100); bit_array_clear_all(arr); _test_toggle_region(arr, 0, 100); _test_toggle_region(arr, 0, 0); _test_toggle_region(arr, 1, 1); _test_toggle_region(arr, 3, 1); _test_toggle_region(arr, 20, 80); _test_toggle_region(arr, 0, 64); _test_toggle_region(arr, 64, 36); int i; for(i = 0; i < 1000; i += 50) { bit_array_resize(arr, i); bit_array_random(arr, 0.5f); _test_toggle_region(arr, i / 2, i - i / 2); } bit_array_free(arr); SUITE_END(); } void _test_random_and_shuffle() { int i, j, k, maxi_sum = 0; BIT_ARRAY *arr = bit_array_create(0); int max_len = 500; int num_hits = 50; int num_repetitions = 50; float prob = 0.5f; int *counts = (int*)malloc(max_len*sizeof(int)); int *hist = (int*)malloc((num_hits+1)*sizeof(int)); for(k = 0; k < num_repetitions; k++) { int len = RAND(max_len-5)+5; ASSERT(len <= max_len); bit_array_resize(arr, len); bit_array_random(arr, prob); bit_index_t bitset = bit_array_num_bits_set(arr); memset(counts, 0, len*sizeof(int)); memset(hist, 0, (num_hits+1)*sizeof(int)); for(i = 0; i < num_hits; i++) { bit_array_shuffle(arr); for(j = 0; j < len; j++) if(bit_array_get_bit(arr, j)) counts[j]++; } ASSERT(bitset == bit_array_num_bits_set(arr)); // Get the most common number of times to be hit for(i = 0; i < len; i++) { hist[counts[i]]++; } int maxi = 0; for(i = 1; i <= num_hits; i++) { if(hist[i] > hist[maxi]) { maxi = i; } } maxi_sum += maxi; } double average = (double)maxi_sum / num_repetitions; double expected = num_hits * (double)prob; ASSERT(average >= expected-10 && average <= expected+10); free(hist); free(counts); bit_array_free(arr); } void test_random_and_shuffle() { SUITE_START("random and shuffle"); int i; for(i = 0; i < 100; i++) _test_random_and_shuffle(); SUITE_END(); } /* // used in test_random void _print_random_arr(BIT_ARRAY* arr, float *rates, int num_rates, char *tmp) { int i, j; for(i = 0; i < num_rates; i++) { float rate = rates[i]; printf("Random %f\n", rate); // 4 repetitions for(j = 0; j < 4; j++) { bit_array_random(arr, rate); printf("arr: %s [%i]\n", bit_array_to_str(arr, tmp), (int)bit_array_num_bits_set(arr)); } } } void test_random() { printf("== Testing random ==\n"); BIT_ARRAY* arr = bit_array_create(0); char str[200]; const int num_rates = 4; float rates[] = {0.0, 0.1, 0.5, 1.0}; printf("Initialise length 0; random\n"); bit_array_random(arr, 0.1); printf("arr: %s [%i]\n", bit_array_to_str(arr, str), (int)bit_array_num_bits_set(arr)); printf("resize length 10; set bits 2,3,6,9\n"); bit_array_resize(arr, 10); bit_array_set_bits(arr, 4, 2,3,6,9); printf("arr: %s [%i]\n", bit_array_to_str(arr, str), (int)bit_array_num_bits_set(arr)); _print_random_arr(arr, rates, num_rates, str); printf("resize length 80; clear all\n"); bit_array_resize(arr, 80); bit_array_clear_all(arr); _print_random_arr(arr, rates, num_rates, str); bit_array_free(arr); printf("== End of testing random ==\n\n"); } */ void _test_cycle(BIT_ARRAY *arr, size_t dist, char left) { BIT_ARRAY *clone = bit_array_clone(arr); size_t len = bit_array_length(arr); if(len > 0) dist = dist % len; char *str1 = (char*)malloc((len+1) * sizeof(char)); char *str2 = (char*)malloc((len+1) * sizeof(char)); str1[len] = '\0'; str2[len] = '\0'; // cycle in str if(left) { // shift away from index zero if(len > 0) { bit_array_to_substr(arr, 0, len-dist, str1, '1', '0', 0); bit_array_to_substr(arr, len-dist, dist, str1+len-dist, '1', '0', 0); } bit_array_cycle_left(arr, dist); } else { // shift towards index zero if(len > 0) { bit_array_to_substr(arr, dist, len-dist, str1+dist, '1', '0', 0); bit_array_to_substr(arr, 0, dist, str1, '1', '0', 0); } bit_array_cycle_right(arr, dist); } bit_array_to_substr(arr, 0, len, str2, '1', '0', 0); // printf("arr : %s\n", str2); // printf("str : %s\n", str1); // Assert that string shifting is the same as bit array shifting ASSERT(strcmp(str1, str2) == 0); free(str1); free(str2); // Now test if we cycle back it is identical to its clone if(left) { bit_array_cycle_right(arr, dist); } else { bit_array_cycle_left(arr, dist); } ASSERT(bit_array_cmp(arr, clone) == 0); bit_array_free(clone); } // Test cyclic shift / circular shift void test_cycle() { SUITE_START("cycle"); BIT_ARRAY* arr = bit_array_create(0); _test_cycle(arr, 3, 1); _test_cycle(arr, 0, 0); bit_array_resize(arr, 10); bit_array_set_bits(arr, 4, 2,3,6,9); _test_cycle(arr, 3, 1); _test_cycle(arr, 0, 0); _test_cycle(arr, 3, 1); _test_cycle(arr, 0, 0); _test_cycle(arr, 25, 1); _test_cycle(arr, 25, 0); bit_array_resize(arr, 80); bit_array_set_bits(arr, 8, 10, 12, 28, 32, 39, 63, 64, 79); _test_cycle(arr, 65, 1); _test_cycle(arr, 65, 0); // Set even bits int i; bit_array_clear_all(arr); for(i = 0; i < 80; i += 2) { bit_array_set_bit(arr, i); } bit_array_cycle_left(arr, 1); bit_array_cycle_right(arr, 1); _test_cycle(arr, 1, 1); _test_cycle(arr, 1, 0); _test_cycle(arr, 1, 0); _test_cycle(arr, 1, 1); // Random arrays for(i = 0; i < 10; i++) { size_t len = RAND(1000UL); size_t dist = RAND(1000UL); char left = rand() > RAND_MAX / 2 ? 1 : 0; bit_array_resize(arr, len); bit_array_random(arr, 0.5f); _test_cycle(arr, dist, left); } bit_array_resize(arr, 5); bit_array_clear_all(arr); bit_array_set_bit(arr, 1); bit_array_free(arr); SUITE_END(); } void _test_shift(BIT_ARRAY *arr, size_t dist, char left, char fill) { size_t len = bit_array_length(arr); // printf("dist: %i; len: %i; %s; fill: %i\n", // (int)dist, (int)len, left ? "left" : "right", fill); char *str1 = (char*)malloc((len+1) * sizeof(char)); char *str2 = (char*)malloc((len+1) * sizeof(char)); memset(str1, fill ? '1' : '0', len); str1[len] = '\0'; str2[len] = '\0'; bit_array_to_substr(arr, 0, len, str2, '1', '0', 0); // printf("orig: %s\n", str2); // cycle in str if(left) { // left is away from index zero if(dist < len) { // printf("0-%i -> %i\n", (int)(len-dist), (int)dist); // printf("str : %s\n", str1); bit_array_to_substr(arr, 0, len-dist, str1, '1', '0', 0); // printf("str : %s\n", str1); } bit_array_shift_left(arr, dist, fill); } else { if(dist < len) { bit_array_to_substr(arr, dist, len-dist, str1+dist, '1', '0', 0); } bit_array_shift_right(arr, dist, fill); } bit_array_to_substr(arr, 0, len, str2, '1', '0', 0); // printf("arr : %s\n", str2); // printf("str : %s\n", str1); ASSERT(strcmp(str1, str2) == 0); free(str1); free(str2); } // Test shift void test_shift() { SUITE_START("shift"); BIT_ARRAY* arr = bit_array_create(0); const char left = 1; const char right = 0; const char fill_zero = 0; const char fill_ones = 1; _test_shift(arr, 3, left, fill_ones); _test_shift(arr, 0, right, fill_ones); bit_array_resize(arr, 10); bit_array_set_bits(arr, 4, 2,3,6,9); _test_shift(arr, 3, left, fill_zero); _test_shift(arr, 0, left, fill_zero); _test_shift(arr, 3, right, fill_ones); _test_shift(arr, 0, right, fill_ones); _test_shift(arr, 25, left, fill_zero); _test_shift(arr, 25, right, fill_zero); bit_array_resize(arr, 80); bit_array_set_bits(arr, 8, 10, 12, 28, 32, 39, 63, 64, 79); _test_shift(arr, 65, right, fill_zero); _test_shift(arr, 65, left, fill_zero); // Set even bits bit_array_clear_all(arr); int i; for(i = 0; i < 80; i += 2) { bit_array_set_bit(arr, i); } _test_shift(arr, 1, left, fill_ones); _test_shift(arr, 1, right, fill_ones); bit_array_free(arr); SUITE_END(); } void _test_hamming(BIT_ARRAY *arr1, BIT_ARRAY *arr2) { bit_index_t bits_set1 = 0, bits_set2 = 0, dist = 0; bit_index_t len1 = bit_array_length(arr1); bit_index_t len2 = bit_array_length(arr2); bit_index_t i, max = MAX(len1, len2); for(i = 0; i < max; i++) { char a = i < len1 ? bit_array_get_bit(arr1, i) : 0; char b = i < len2 ? bit_array_get_bit(arr2, i) : 0; if(a) bits_set1++; if(b) bits_set2++; if(a != b) dist++; } ASSERT(bits_set1 == bit_array_num_bits_set(arr1)); ASSERT(bits_set2 == bit_array_num_bits_set(arr2)); ASSERT(dist == bit_array_hamming_distance(arr1, arr2)); } void test_hamming_weight() { SUITE_START("hamming weight"); BIT_ARRAY* arr1 = bit_array_create(0); BIT_ARRAY* arr2 = bit_array_create(0); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); bit_array_resize(arr1, 10); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); bit_array_set_bits(arr1, 3, 0, 2, 7); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); bit_array_resize(arr2, 10); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); bit_array_set_bits(arr2, 3, 0, 2, 7); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); bit_array_resize(arr1, 80); bit_array_clear_bit(arr1, 2); bit_array_set_region(arr1, 50, 20); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); // random int i; for(i = 0; i < 10; i++) { size_t len1 = RAND(1000UL); size_t len2 = RAND(1000UL); bit_array_resize(arr1, len1); bit_array_resize(arr2, len2); bit_array_random(arr1, 0.5f); bit_array_random(arr2, 0.5f); _test_hamming(arr1, arr2); _test_hamming(arr2, arr1); } bit_array_free(arr1); bit_array_free(arr2); SUITE_END(); } // Saves arr1 to file, then reloads it into arr2 and compares them void _test_save_load(BIT_ARRAY *arr1, BIT_ARRAY *arr2) { FILE *f = fopen(test_filename, "w"); if(f == NULL) { die("Couldn't open file to write: '%s'", test_filename); } bit_array_save(arr1, f); fclose(f); f = fopen(test_filename, "r"); if(f == NULL) { die("Couldn't open file to read: '%s'", test_filename); } if(!bit_array_load(arr2, f)) { die("Load returned warning [file: %s]", test_filename); } fclose(f); ASSERT(bit_array_cmp(arr1, arr2) == 0); if(bit_array_cmp(arr1, arr2) != 0 && bit_array_length(arr1) < 500) { // debug output bit_index_t len = MAX(bit_array_length(arr1), bit_array_length(arr2)); char *tmp = (char*)malloc((len+1) * sizeof(char)); printf("out: %s\n", bit_array_to_str(arr1, tmp)); printf("in : %s\n", bit_array_to_str(arr2, tmp)); free(tmp); } } void test_save_load() { SUITE_START("load and save"); BIT_ARRAY* arr1 = bit_array_create(0); BIT_ARRAY* arr2 = bit_array_create(0); // Empty array _test_save_load(arr1, arr2); // Ten 0s bit_array_resize(arr1, 10); _test_save_load(arr1, arr2); // Thousand 1s: bit_array_resize(arr1, 1000); bit_array_set_all(arr1); _test_save_load(arr1, arr2); // 1100111010 bit_array_resize(arr1, 10); bit_array_clear_bits(arr1, 4, 2,3,7,9); _test_save_load(arr1, arr2); // 100 x random lengths and bits int i; for(i = 0; i < 10; i++) { bit_array_resize(arr1, RAND(5000UL)); bit_array_random(arr1, 0.5f); _test_save_load(arr1, arr2); } bit_array_free(arr1); bit_array_free(arr2); SUITE_END(); } // // Aggregate testing // /* void test_zero_length_arrays() { printf("== Testing zero length arrays ==\n"); BIT_ARRAY* arr1 = bit_array_create(0); BIT_ARRAY* arr2 = bit_array_create(10); char tmp[101]; printf("Initial arr1[length:0] arr2[length:10]\n"); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("arr2: %s\n", bit_array_to_str(arr2, tmp)); printf("--\nResize arr2 to 0\n"); bit_array_resize(arr2, 0); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("arr2: %s\n", bit_array_to_str(arr2, tmp)); printf("--\nAnd (arr1, arr2)\n"); bit_array_and(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("arr2: %s\n", bit_array_to_str(arr2, tmp)); printf("--\nNot (arr1)\n"); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("--\nAnd (arr1, arr2)\n"); bit_array_and(arr1, arr1, arr2); printf("arr1: %s\n", bit_array_to_str(arr1, tmp)); printf("arr2: %s\n", bit_array_to_str(arr2, tmp)); bit_array_free(arr1); bit_array_free(arr2); printf("== End of testing zero length arrays ==\n\n"); } void test_multiple_actions() { printf("== testing all functions ==\n"); printf("Create bit array 100 bits long\n"); BIT_ARRAY* bitarr = bit_array_create(100); bit_array_print(bitarr, stdout); printf("\n"); printf("Set bit 2\n"); bit_array_set_bit(bitarr, 2); bit_array_print(bitarr, stdout); printf("\n"); printf("Set bit 5\n"); bit_array_set_bit(bitarr, 5); bit_array_print(bitarr, stdout); printf("\n"); printf("Set bit 99\n"); bit_array_set_bit(bitarr, 99); bit_array_print(bitarr, stdout); printf("\n"); printf("Set bit 0\n"); bit_array_set_bit(bitarr, 0); bit_array_print(bitarr, stdout); printf("\n"); // Test clone printf("Clone\n"); BIT_ARRAY* clone = bit_array_clone(bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf(" Clear bit 0\n"); bit_array_clear_bit(clone, 0); bit_array_print(bitarr, stdout); printf("\n"); printf(" Set bit 21\n"); bit_array_set_bit(clone, 21); bit_array_print(bitarr, stdout); printf("\n"); printf(" Set bit 63\n"); bit_array_set_bit(clone, 63); bit_array_print(bitarr, stdout); printf("\n"); printf(" End of clone\n"); bit_array_free(clone); // End of clone printf("Fill with zeros\n"); bit_array_clear_all(bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf("Fill with ones\n"); bit_array_set_all(bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf("Clear bit 1\n"); bit_array_clear_bit(bitarr, 1); bit_array_print(bitarr, stdout); printf("\n"); printf("Clear bits 0-39\n"); bit_array_clear_region(bitarr, 0, 40); bit_array_print(bitarr, stdout); printf("\n"); bit_index_t first_set_bit; if(bit_array_find_first_set_bit(bitarr, &first_set_bit)) { printf("first set bit: %i\n", (int)first_set_bit); } printf("Set bits 1-1\n"); bit_array_set_region(bitarr, 1, 1); bit_array_print(bitarr, stdout); printf("\n"); printf("Set bits (3,0)\n"); bit_array_set_region(bitarr, 3, 0); bit_array_print(bitarr, stdout); printf("\n"); printf("Clear bits 50-59\n"); bit_array_clear_region(bitarr, 50, 10); bit_array_print(bitarr, stdout); printf("\n"); printf("Negate\n"); bit_array_not(bitarr, bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf("Sort bits\n"); bit_array_sort_bits(bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf("Clear bits 1-51\n"); bit_array_clear_region(bitarr, 1, 51); bit_array_print(bitarr, stdout); printf("\n"); if(bit_array_find_first_set_bit(bitarr, &first_set_bit)) { printf("first set bit: %i\n", (int)first_set_bit); } printf("Set bits 1-51\n"); bit_array_set_region(bitarr, 1, 51); bit_array_print(bitarr, stdout); printf("\n"); if(bit_array_find_first_set_bit(bitarr, &first_set_bit)) { printf("first set bit: %i\n", (int)first_set_bit); } printf("Clear bit 98\n"); bit_array_clear_bit(bitarr, 98); bit_array_print(bitarr, stdout); printf("\n"); printf("Clear bit 99\n"); bit_array_clear_bit(bitarr, 99); bit_array_print(bitarr, stdout); printf("\n"); printf("Resize to 40 bits\n"); bit_array_resize(bitarr, 40); bit_array_print(bitarr, stdout); printf("\n"); printf("Resize to 100 bits\n"); bit_array_resize(bitarr, 100); bit_array_print(bitarr, stdout); printf("\n"); printf("Fill with ones\n"); bit_array_set_all(bitarr); bit_array_print(bitarr, stdout); printf("\n"); printf("Resize to 64 bits\n"); bit_array_resize(bitarr, 64); bit_array_print(bitarr, stdout); printf("\n"); printf("Resize to 100 bits\n"); bit_array_resize(bitarr, 100); bit_array_print(bitarr, stdout); printf("\n"); printf("Set clear 0,10,55:\n"); bit_array_clear_bits(bitarr, 3, 0, 10, 55); printf("Set bits 67,69,70:\n"); bit_array_set_bits(bitarr, 3, 67, 69, 70); bit_array_print(bitarr, stdout); printf("\n"); printf("Bits set: %i\n", (int)bit_array_num_bits_set(bitarr)); printf("Shift left 10 bits\n"); bit_array_shift_left(bitarr, 10, 0); bit_array_print(bitarr, stdout); printf("\n"); printf("Shift right 10 bits [fill with 1s]\n"); bit_array_shift_right(bitarr, 10, 1); bit_array_print(bitarr, stdout); printf("\n"); printf("Shift left 10 bits [fill with 1s]\n"); bit_array_shift_left(bitarr, 10, 1); bit_array_print(bitarr, stdout); printf("\n"); // Test cycle bits printf("Cycle right 10 bits\n"); bit_array_cycle_right(bitarr, 10); bit_array_print(bitarr, stdout); printf("\n"); printf("Cycle right 80 bits\n"); bit_array_cycle_right(bitarr, 80); bit_array_print(bitarr, stdout); printf("\n"); bit_array_free(bitarr); printf("\nNew bit array:\n"); bitarr = bit_array_create(210); // = ~3.28 words bit_array_set_region(bitarr, 0, 100); bit_array_set_bits(bitarr, 5, 200, 202, 204, 206, 208); bit_array_print(bitarr, stdout); printf("\n"); printf("Cycle right 80 bits\n"); bit_array_cycle_right(bitarr, 80); bit_array_print(bitarr, stdout); printf("\n"); // Test reverse printf("Reverse region [10,90]:\n"); bit_array_reverse_region(bitarr, 10, 90); bit_array_print(bitarr, stdout); printf("\n"); printf("Reverse region [10,90]:\n"); bit_array_reverse_region(bitarr, 10, 90); bit_array_print(bitarr, stdout); printf("\n"); printf("Reverse region [90,10]:\n"); bit_array_reverse_region(bitarr, 90, 10); bit_array_print(bitarr, stdout); printf("\n"); printf("Reverse region [90,10]:\n"); bit_array_reverse_region(bitarr, 90, 10); bit_array_print(bitarr, stdout); printf("\n"); // Test write/read file FILE* f; printf("Saving bitarray in %s\n", test_filename); f = fopen(test_filename, "w"); bit_array_save(bitarr, f); fclose(f); bit_array_print(bitarr, stdout); printf("\n"); // Deconstruct bit array bit_array_free(bitarr); printf("Loading bitarray in %s\n", test_filename); f = fopen(test_filename, "r"); bit_array_load(bitarr, f); fclose(f); bit_array_print(bitarr, stdout); printf("\n"); // Deconstruct bit array bit_array_free(bitarr); printf("== End of testing all functions ==\n\n"); } */ void _test_string_functions(BIT_ARRAY *arr) { size_t len = bit_array_length(arr); char *str = (char*)malloc((len+1) * sizeof(char)); BIT_ARRAY *arr2 = bit_array_create(0); // to/from string bit_array_to_str(arr, str); bit_array_from_str(arr2, str); // compare ASSERT(bit_array_cmp(arr, arr2) == 0); bit_array_resize(arr2, 0); // to/from substring size_t len1 = len / 2; size_t len2 = len - len1; bit_array_to_substr(arr, 0, len1, str, '1', '0', 1); bit_array_to_substr(arr, len1, len2, str+len1, '1', '0', 1); bit_array_from_substr(arr2, 0, str, len1, "1", "0", 1); bit_array_from_substr(arr2, len1, str+len1, len2, "1", "0", 1); // compare ASSERT(bit_array_cmp(arr, arr2) == 0); free(str); bit_array_free(arr2); } // bit_array_from_str(), bit_array_from_substr() // bit_array_to_str(), bit_array_to_substr() // bit_array_print(), bit_array_print_substr() void test_string_functions() { SUITE_START("string functions"); BIT_ARRAY *arr = bit_array_create(10); bit_array_set_bits(arr, 3, 1,5,7); _test_string_functions(arr); int i; for(i = 0; i < 10; i++) { bit_array_resize(arr, RAND(500UL)); bit_array_random(arr, 0.5f); _test_string_functions(arr); } bit_array_free(arr); SUITE_END(); } // Convert string of hex to bit array and back, then compare void _test_hex_functions(BIT_ARRAY *arr, const char* hex, int offset, char upper, const char* correct) { char tmp_buf[1000]; char *tmp = tmp_buf; if(hex[0] == '0' && hex[1] == 'x') { tmp_buf[0] = '0'; tmp_buf[1] = 'x'; tmp = tmp_buf + 2; } // Reset BIT_ARRAY by resizing to zero bit_array_resize(arr, 0); bit_array_from_hex(arr, offset, hex, strlen(hex)); bit_array_to_hex(arr, offset, bit_array_length(arr)-offset, tmp, upper); ASSERT(strcmp(tmp_buf, correct) == 0); if(strcmp(tmp_buf, correct) != 0) { printf("in : %s\n", hex); printf("out: %s\n", tmp_buf); printf("cor: %s\n\n", correct); } } // bit_array_from_hex(), bit_array_to_hex(), bit_array_print_hex() void test_hex_functions() { SUITE_START("hex functions"); BIT_ARRAY *arr = bit_array_create(0); // lowercase then uppercase _test_hex_functions(arr, "123456789ABcDeF0", 0, 0, "123456789abcdef0"); _test_hex_functions(arr, "123456789ABcDeF0", 0, 1, "123456789ABCDEF0"); _test_hex_functions(arr, "0x123456789ABcDeF0", 0, 0, "0x123456789abcdef0"); _test_hex_functions(arr, "0x123456789ABcDeF0", 0, 1, "0x123456789ABCDEF0"); _test_hex_functions(arr, "0x123456789Agadsfasdf", 0, 0, "0x123456789a"); _test_hex_functions(arr, "0x123456789ABcDeF0", 1, 0, "0x123456789abcdef0"); _test_hex_functions(arr, "0x123456789ABcDeF0", 40, 0, "0x123456789abcdef0"); bit_array_free(arr); SUITE_END(); } void _next_permutation_str(char *str, size_t len) { if(len == 0) return; size_t i, j; for(i = 1; i < len; i++) { if(str[i-1] == '1' && str[i] == '0') { str[i-1] = '0'; str[i] = '1'; break; } } // Set 0..i (inclusive) to 0 int bits_set = 0; for(j = 0; j < i; j++) { if(str[j] == '1') { bits_set++; } } memset(str, '1', bits_set); memset(str+bits_set, '0', i - bits_set); } void test_next_permutation() { SUITE_START("next permutation"); BIT_ARRAY *arr = bit_array_create(0); char tmp[2000], correct[2000]; size_t len; #define TMPN 12 size_t lens[TMPN] = {0, 1, 2, 2, 2, 5, 5, 10, 101, 302, 512, 600}; size_t bset[TMPN] = {0, 1, 0, 1, 2, 0, 1, 1, 1, 2, 2, 0}; int i, j; for(i = 0; i < TMPN; i++) { len = lens[i]; bit_array_resize(arr, lens[i]); bit_array_clear_all(arr); bit_array_set_region(arr, 0, bset[i]); for(j = 0; j < 100; j++) { bit_array_to_str(arr, correct); _next_permutation_str(correct, len); bit_array_next_permutation(arr); bit_array_to_str(arr, tmp); ASSERT(strcmp(tmp, correct) == 0); } } bit_array_free(arr); SUITE_END(); } void _test_num_cmp(BIT_ARRAY *arr, uint64_t value, uint64_t num) { int cmp1, cmp2; cmp1 = bit_array_cmp_uint64(arr, num); cmp1 = cmp1 > 0 ? 1 : (cmp1 < 0 ? -1 : 0); cmp2 = value > num ? 1 : (value < num ? -1 : 0); ASSERT(cmp1 == cmp2); } void _test_as_num_cmp_num(uint64_t value) { BIT_ARRAY *arr = bit_array_create(0); bit_array_add_uint64(arr, value); // as_num returns 1 on success uint64_t num = 0; ASSERT(bit_array_as_num(arr, &num) == 1); ASSERT(value == num); _test_num_cmp(arr, value, value-1); _test_num_cmp(arr, value, value); _test_num_cmp(arr, value, value+1); bit_array_free(arr); } // Test bit_array_as_num(), bit_array_cmp_uint64() void test_as_num_cmp_num() { SUITE_START("as_num and num_cmp"); _test_as_num_cmp_num(0); _test_as_num_cmp_num(10); _test_as_num_cmp_num(100000000); _test_as_num_cmp_num(ULONG_MAX); int i; for(i = 0; i < 10; i++) { _test_as_num_cmp_num(rand()); } SUITE_END(); } void _test_add_single_word_small(unsigned long init, unsigned long add, int offset) { BIT_ARRAY *arr1 = bit_array_create(0); BIT_ARRAY *arr2 = bit_array_create(0); uint64_t a, b, result; bit_array_add_uint64(arr1, init); bit_array_as_num(arr1, &a); bit_array_add_uint64(arr2, add); if(offset > 0) { bit_array_resize(arr2, bit_array_length(arr2)+offset); bit_array_shift_left(arr2, offset, 0); } bit_array_as_num(arr2, &b); if(b != add << offset) { fprintf(stderr, "Warning: b != add << offset\n"); } bit_array_add_word(arr1, offset, add); bit_array_as_num(arr1, &result); ASSERT(a + b == result); // printf(" %lu + %lu = %lu [%s]\n", // (unsigned long)a, (unsigned long)b, (unsigned long)result, // (a + b == result ? "Pass" : "Fail")); bit_array_free(arr1); bit_array_free(arr2); } void test_add_single_word() { SUITE_START("add single word"); _test_add_single_word_small(0, 3, 0); _test_add_single_word_small(0, 3, 1); _test_add_single_word_small(3, 3, 0); _test_add_single_word_small(3, 3, 1); _test_add_single_word_small(0, 0, 3); // 0111010 [58] // + 1010000 [5 << 4] _test_add_single_word_small(58, 5, 4); _test_add_single_word_small(ULONG_MAX-(1<<4), 1, 4); SUITE_END(); } void _test_minus_single_word() { uint64_t word = rand(); // char tmp[1000]; int shift = RAND(1000); // int shift = 5; shift = MAX(shift, 5); BIT_ARRAY *arr = bit_array_create(0); // Add word twice, shift left (i.e. * 2^shift) bit_array_add_uint64(arr, word); bit_array_add_uint64(arr, word); bit_array_resize(arr, bit_array_length(arr)+shift); bit_array_shift_left(arr, shift, 0); // Subtract word shifted left (i.e. word * 2^shift) ASSERT(bit_array_sub_word(arr, shift, word) == 1); // Shift to the right again (should now be equal to word again) bit_array_shift_right(arr, shift, 0); BIT_ARRAY *arr2 = bit_array_create(0); bit_array_add_uint64(arr2, word); ASSERT(bit_array_cmp_words(arr, 0, arr2) == 0); // Shift to the left and compare with word << shift bit_array_shift_left(arr, shift, 0); ASSERT(bit_array_cmp_words(arr, shift, arr2) == 0); bit_array_free(arr); bit_array_free(arr2); } void test_minus_single_word() { SUITE_START("minus single word"); int i; for(i = 0; i < 100; i++) { _test_minus_single_word(); } SUITE_END(); } void _test_add_words() { BIT_ARRAY *arr1 = bit_array_create(RAND(10)); BIT_ARRAY *arr2 = bit_array_create(RAND(10)); bit_array_random(arr1, 0.5f); bit_array_random(arr2, 0.5f); int shift1 = RAND(10); int shift2 = RAND(10); BIT_ARRAY *shifted_sum = bit_array_create(0); bit_array_add_words(shifted_sum, shift1, arr1); bit_array_add_words(shifted_sum, shift2, arr2); bit_array_resize(arr1, bit_array_length(arr1)+shift1); bit_array_shift_left(arr1, shift1, 0); bit_array_resize(arr2, bit_array_length(arr2)+shift2); bit_array_shift_left(arr2, shift2, 0); bit_array_add(arr1, arr1, arr2); // arr1 = arr1 + arr2 ASSERT(bit_array_cmp_words(arr1, 0, shifted_sum) == 0); bit_array_free(arr1); bit_array_free(arr2); bit_array_free(shifted_sum); } void test_add_words() { SUITE_START("add multiple words"); int i; for(i = 0; i < 20; i++) _test_add_words(); SUITE_END(); } void _test_minus_words() { // Check that: A< 0 && bit_array_get_bit(accum, shift2)) shift2--; bit_array_set_bit(accum, shift2); bit_array_sub_words(accum, shift1, arr); ASSERT(bit_array_get_bit(accum, shift2)); bit_array_clear_bit(accum, shift2); ASSERT(bit_array_cmp_uint64(accum, 0) == 0); bit_array_free(accum); bit_array_free(arr); } void test_minus_words() { SUITE_START("minus multiple words"); int i; for(i = 0; i < 1000; i++) _test_minus_words(); // Check minus word of length 0 BIT_ARRAY *arr1 = bit_array_create(RAND(100)); bit_array_random(arr1, 0.5f); BIT_ARRAY *arr2 = bit_array_clone(arr1); BIT_ARRAY *zero = bit_array_create(0); for(i = 0; i < 100; i++) { bit_array_sub_words(arr1, 0, zero); bit_array_sub_words(arr1, RAND(bit_array_length(arr1)), zero); } ASSERT(bit_array_cmp(arr1, arr2) == 0); bit_array_free(arr1); bit_array_free(arr2); bit_array_free(zero); SUITE_END(); } void _test_multiply_small(uint64_t a, uint64_t b) { BIT_ARRAY *arr = bit_array_create(0); uint64_t product; bit_array_add_uint64(arr, a); bit_array_mul_uint64(arr, b); bit_array_as_num(arr, &product); ASSERT(a * b == product); if(a * b != product) { // debug output printf("%lu * %lu = %lu [expected: %lu]\n", (unsigned long)a, (unsigned long)b, (unsigned long)product, (unsigned long)(a * b)); } bit_array_free(arr); } void test_multiply() { SUITE_START("small multiply"); _test_multiply_small(2, 4); _test_multiply_small(4, 2); _test_multiply_small(3, 6); _test_multiply_small(9, 256); _test_multiply_small(10, 100); _test_multiply_small(0, 0); _test_multiply_small(10, 0); _test_multiply_small(0, 10); SUITE_END(); } void _test_small_product(uint64_t a, uint64_t b, char expect_overflow) { BIT_ARRAY *arr1 = bit_array_create(0); BIT_ARRAY *arr2 = bit_array_create(0); int i; for(i = 0; i < 2; i++) { bit_array_resize(arr1, 0); bit_array_resize(arr2, 0); bit_array_add_uint64(arr1, a); bit_array_add_uint64(arr2, b); BIT_ARRAY *result = (i == 0 ? arr1 : arr2); bit_array_multiply(result, arr1, arr2); // printf(" = %s\n", bit_array_to_str(result, tmp)); uint64_t c; char overflowed = !bit_array_as_num(result, &c); ASSERT(expect_overflow == overflowed); if(!expect_overflow) { ASSERT(a*b == c); } if(expect_overflow != overflowed || (!expect_overflow && a*b != c)) { // debug output printf("expect: %lu * %lu = %lu [overflow: %s]\n", (unsigned long)a, (unsigned long)b, (unsigned long)(a*b), expect_overflow ? "yes" : "no"); printf("got: %lu [overflow: %s]\n", (unsigned long)c, overflowed ? "yes" : "no"); } } bit_array_free(arr1); bit_array_free(arr2); } void test_small_products() { SUITE_START("product with small numbers"); _test_small_product(1, 1, 0); _test_small_product(0, 1, 0); _test_small_product(0, 0, 0); _test_small_product(2, 2, 0); _test_small_product(3, 9, 0); _test_small_product(3, 90000, 0); _test_small_product(300000, 90000, 0); _test_small_product(123456789123132, 9876543212351234, 1); SUITE_END(); } void _test_div(uint64_t nom, uint64_t denom) { BIT_ARRAY *nom_arr = bit_array_create(0); bit_array_add_uint64(nom_arr, nom); uint64_t quotient, rem; bit_array_div_uint64(nom_arr, denom, &rem); bit_array_as_num(nom_arr, "ient); ASSERT(quotient * denom + rem == nom); if(quotient * denom + rem != nom) { // Debug output printf("%lu = %lu * %lu + %lu [%s]\n", (unsigned long)nom, (unsigned long)quotient, (unsigned long)denom, (unsigned long)rem, (quotient * denom + rem == nom ? "Pass" : "Fail")); } bit_array_free(nom_arr); } void test_div() { SUITE_START("bit_array_div"); _test_div(10, 2); _test_div(100, 100); _test_div(0, 100); _test_div(12, 3); _test_div(13, 7); _test_div(12, 9); _test_div(64, 8); _test_div(9, 9); _test_div(10000000, 13); SUITE_END(); } void _test_to_from_decimal(char *str) { size_t len = strlen(str); char new_str[len+1]; BIT_ARRAY *bitarr = bit_array_create(0); bit_array_from_decimal(bitarr, str); bit_array_to_decimal(bitarr, new_str, len+1); ASSERT(atoi(str) == atoi(new_str)); if(atoi(str) != atoi(new_str)) { // debug output printf("%s -> %s\n", str, new_str); } bit_array_free(bitarr); } void test_to_from_decimal() { SUITE_START("to/from decimal"); _test_to_from_decimal("1"); _test_to_from_decimal("0"); _test_to_from_decimal("1234"); _test_to_from_decimal("5678"); _test_to_from_decimal("012"); SUITE_END(); } void _test_product_divide() { // Rand number between 0-255 inclusive int num_digits; do { num_digits = rand() & 255; } while(num_digits == 0); // Set BIT_ARRAY *arr = bit_array_create(num_digits); BIT_ARRAY *divisor = bit_array_create(num_digits); while(bit_array_num_bits_set(divisor) == 0) { do { bit_array_random(arr, (float)((double)rand() / RAND_MAX)); } while(bit_array_num_bits_set(arr) == 0); do { bit_array_random(divisor, (float)((double)rand() / RAND_MAX)); } while(bit_array_num_bits_set(divisor) == 0); while(bit_array_cmp(arr, divisor) < 0) { bit_array_shift_right(divisor, 1, 0); } if(bit_array_cmp_uint64(divisor, 1) > 0 && rand() < RAND_MAX / 2) { bit_array_shift_right(divisor, 1, 0); } } // Copy BIT_ARRAY *tmp = bit_array_clone(arr); BIT_ARRAY *quotient = bit_array_create(0); // Divide bit_array_divide(tmp, quotient, divisor); // Multiply and add bit_array_multiply(quotient, quotient, divisor); bit_array_add(tmp, tmp, quotient); // Compare ASSERT(bit_array_cmp_words(tmp, 0, arr) == 0); // int cmp = bit_array_cmp_words(tmp, 0, arr); // printf("product & divide: [%s]\n\n", cmp == 0 ? "Pass" : "Fail"); bit_array_free(tmp); bit_array_free(quotient); bit_array_free(arr); bit_array_free(divisor); } void test_product_divide() { SUITE_START("product and divide"); int i; for(i = 0; i < 10; i++) { _test_product_divide(); } SUITE_END(); } void _test_add_and_minus_multiple_words() { // Rand number between 0-511 inclusive int num_digits; do { num_digits = rand() & 0x1ff; } while(num_digits == 0); // Set BIT_ARRAY *big = bit_array_create(num_digits); BIT_ARRAY *small = bit_array_create(num_digits); int offset = RAND(num_digits-1); while(bit_array_num_bits_set(small) == 0) { do { bit_array_random(big, (float)((double)rand() / RAND_MAX)); } while(bit_array_num_bits_set(big) == 0); do { bit_array_random(small, (float)((double)rand() / RAND_MAX)); } while(bit_array_num_bits_set(small) == 0); while(bit_array_cmp_words(big, offset, small) < 0) { bit_array_shift_right(small, 1, 0); } } // Copy // tmp = big BIT_ARRAY *tmp = bit_array_clone(big); // tmp -= small bit_array_sub_words(tmp, offset, small); // tmp += small bit_array_add_words(tmp, offset, small); // Compare tmp and big ASSERT(bit_array_cmp_words(tmp, 0, big) == 0); // int cmp = bit_array_cmp_words(tmp, 0, big); // printf(" add/minus words: [%s]\n\n", cmp == 0 ? "Pass" : "Fail"); bit_array_free(tmp); bit_array_free(big); bit_array_free(small); } void test_add_and_minus_multiple_words() { SUITE_START("add/minus multiple words"); int i; for(i = 0; i < 10; i++) { _test_add_and_minus_multiple_words(); } SUITE_END(); } void _test_add_and_minus_single_word() { // Rand number between 0-511 inclusive int num_digits; do { num_digits = rand() & 0x1ff; } while(num_digits == 0); // Set BIT_ARRAY *arr = bit_array_create(num_digits); int offset = RAND(num_digits+2); do { bit_array_random(arr, (float)((double)rand() / RAND_MAX)); } while(bit_array_num_bits_set(arr) == 0); word_t word = rand(); // Copy // tmp = arr BIT_ARRAY *tmp = bit_array_clone(arr); // tmp += word bit_array_add_word(tmp, offset, word); // tmp -= word bit_array_sub_word(tmp, offset, word); // Compare tmp and arr ASSERT(bit_array_cmp_words(tmp, 0, arr) == 0); bit_array_free(tmp); bit_array_free(arr); } void test_add_and_minus_single_word() { SUITE_START("add/minus single word"); int i; for(i = 0; i < 10; i++) { _test_add_and_minus_single_word(); } SUITE_END(); } void test_get_set_bytes() { SUITE_START("get/set byte"); #define NBYTES 8 uint8_t bytes[NBYTES] = {0x55,0x31,0x5A,0x12,0x02,0x31,0xC3,0x1E}; size_t s, i; BIT_ARRAY *arr = bit_array_create(NBYTES*8); // Set bytes using different offsets for(s = 0; s < 8; s++) { bit_array_clear_all(arr); for(i = 0; i < NBYTES-1; i++) { bit_array_set_word8(arr, i*8+s, bytes[i]); ASSERT(bit_array_get_word8(arr, i*8+s) == bytes[i]); } // Setting bytes doesn't extend the array - check last byte with masking i = NBYTES-1; bit_array_set_word8(arr, i*8+s, bytes[i]); ASSERT(bit_array_get_word8(arr, i*8+s) == (bytes[i] & (0xff>>s))); // Check bits for(i = 0; i < s; i++) ASSERT(bit_array_get(arr, i) == 0); for(i = s; i < NBYTES*8; i++) ASSERT(bit_array_get(arr, i) == bitset_get(bytes, i-s)); } bit_array_free(arr); #undef NBYTES SUITE_END(); } // test new features of bitarr library. // - resize from 0 // - get/set/clear/toggle/assign auto-resize // - copy auto-resize // - copy_all function (with auto-resize) // - extended shift left // - find clear bit // exercise short names. (some of them anyway) #include "bar.h" void test_bar_wrapper() { SUITE_START("testing bar wrapper"); bar bars[7]; bar foo; int i, j; uint64_t res, w; bit_index_t tb, rv, k; // test bit. // make sure this works, too. w = 0x00000FFFFFFFFF00; rv = leading_zeros(w); // printf("clz=%d\n", rv); ASSERT(rv == 20); rv = trailing_zeros(w); ASSERT(rv == 8); rv = __builtin_ctzll(w); ASSERT(rv == 8); rv = __builtin_clzll(w); ASSERT(rv == 20); w = 0xFFFFFFFFFFFFFFFF; w >>= 5; rv = __builtin_clzll(w); ASSERT(rv == 5); // initialize to zeros. memset((void *)bars, 0, sizeof(bars)); memset((void *)&foo, 0, sizeof(bar)); // test resize from nothing. ASSERT(barlen(&foo) == 0); rv = bit_array_resize(&foo, 23); ASSERT(rv == 1); ASSERT(barlen(&foo) == 23); barfree(&foo); ASSERT(barlen(&foo) == 0); // Auto-resizing array turned off, so these tests fail // run through get/set/clear/toggle/assign. // start with 0, increase size each time. tb = 7; ASSERT(barlen(&foo) == 0); rv = barrget(&foo, tb); ASSERT(rv == 0); ASSERT(barlen(&foo) >= tb); tb *= 4; barrset(&foo, tb); ASSERT(barlen(&foo) >= tb); ASSERT(barrget(&foo, tb) == 1); tb *= 4; barrclr(&foo, tb); ASSERT(barlen(&foo) >= tb); ASSERT(barrget(&foo, tb) == 0); tb *= 4; barrflip(&foo, tb); ASSERT(barlen(&foo) >= tb); ASSERT(barrget(&foo, tb) == 1); tb *= 4; barrmake(&foo, tb, 1); ASSERT(barlen(&foo) >= tb); ASSERT(barrget(&foo, tb) == 1); tb *= 4; for (i = 1; i < 5; i++) { barcpy(&(bars[i]), &foo); ASSERT(barlen(&(bars[i])) == barlen(&foo)); rv = barlen(&(bars[i])); rv += 57; barrset(&(bars[i]), rv); ASSERT(barlen(&(bars[i])) >= rv); barcpy(&foo, &(bars[i])); ASSERT(barlen(&foo) >= rv); } barfree(&foo); ASSERT(barlen(&foo) == 0); barrset(&foo, 100); barfill(&foo); rv = barffz(&foo, &res); ASSERT(rv == 0); barrclr(&foo, 50); rv = barffz(&foo, &res); ASSERT(rv == 1); ASSERT(res == 50); barclr(&foo, 77); rv = barfnz(&foo, res+1, &res); ASSERT(rv == 1); ASSERT(res == 77); barfree(&foo); ASSERT(barlen(&foo) == 0); barrset(&foo, 1); barrset(&foo, 3); barrset(&foo, 15); ASSERT(barlen(&foo) >= 15); j = 9; k = barlen(&foo); for (i = 0; i < 10; i++) { bareshl(&foo, j, 0); k += j; j *= 2; } ASSERT(barlen(&foo) >= k); barfree(&foo); SUITE_END(); } int main(int argc, char* argv[]) { if(argc != 1) { printf(" Unused args '%s..'\n", argv[1]); printf("Usage: ./bit_array_test\n"); exit(EXIT_FAILURE); } // Initialise random number generator srand((unsigned int)time(NULL) + getpid()); printf(" Test bit_array C library:\n\n"); // Test functions test_copy(); test_get_set_bytes(); test_parity(); test_interleave(); test_reverse(); test_toggle(); test_cycle(); test_shift(); test_compare(); test_compare2(); test_first_last_bit_set(); test_next_prev_bit_set(); test_hamming_weight(); test_save_load(); test_hex_functions(); test_string_functions(); test_to_from_decimal(); test_as_num_cmp_num(); test_add_single_word(); test_minus_single_word(); test_add_words(); test_minus_words(); test_add_and_minus_single_word(); test_add_and_minus_multiple_words(); test_multiply(); test_div(); test_small_products(); test_product_divide(); test_bar_wrapper(); // slooow test_next_permutation(); test_random_and_shuffle(); // Tests that need re-writing // test_hash(); // To do // test_crc(); // test_multiple_actions(); // test_zero_length_arrays(); // test_arithmetic(); printf("\n"); printf(" %i / %i suites failed\n", suites_failed, suites_run); printf(" %i / %i suites empty\n", suites_empty, suites_run); printf(" %i / %i tests failed\n", tests_failed, tests_run); printf("\n THE END.\n"); return tests_failed ? EXIT_FAILURE : EXIT_SUCCESS; } BitArray-2.0/dev/bitlock_test.c000066400000000000000000000102161266571572500165200ustar00rootroot00000000000000/* dev/bitlock_test.c project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Aug 2014 */ #include #include #include #include #include #include #include "bit_macros.h" typedef struct { pthread_t th; size_t id, result; } TestThread; // Do a million loops #define NUM_LOOPS 10000 char *locks, *data; pthread_mutex_t *mutexes; void* worker_bitlock(void *ptr) { TestThread *wrkr = (TestThread*)ptr; size_t i; for(i = 0; i < NUM_LOOPS; i++) { bitlock_yield_acquire(locks, i); wrkr->result += i + *(volatile char *)&data[i]; data[i] = wrkr->id; usleep(5); bitlock_release(locks, i); usleep(5); } return NULL; } void* worker_bitlock_spin(void *ptr) { TestThread *wrkr = (TestThread*)ptr; size_t i; for(i = 0; i < NUM_LOOPS; i++) { bitlock_acquire(locks, i); wrkr->result += i + *(volatile char *)&data[i]; data[i] = wrkr->id; usleep(5); bitlock_release(locks, i); usleep(5); } return NULL; } void* worker_mutex(void *ptr) { TestThread *wrkr = (TestThread*)ptr; size_t i; for(i = 0; i < NUM_LOOPS; i++) { pthread_mutex_lock(&mutexes[0]); wrkr->result += i + *(volatile char *)&data[i]; data[i] = wrkr->id; usleep(5); pthread_mutex_unlock(&mutexes[0]); usleep(5); } return NULL; } void* worker_mutexes(void *ptr) { TestThread *wrkr = (TestThread*)ptr; size_t i; for(i = 0; i < NUM_LOOPS; i++) { pthread_mutex_lock(&mutexes[i]); wrkr->result += i + *(volatile char *)&data[i]; data[i] = wrkr->id; usleep(5); pthread_mutex_unlock(&mutexes[i]); usleep(5); } return NULL; } // sum of 0 up to num (inclusive) #define cumm_sum(num) ((num)*(((num)+1)/2)+(((num)&1) ? 0 : (num)/2)) int main(int argc, char **argv) { char *method = "Bitlocks"; void* (*func)(void*) = worker_bitlock; if(argc == 2 && strcmp(argv[1],"bits") == 0) {} else if(argc == 2 && strcmp(argv[1],"mutex") == 0) { method = "Mutex"; func = worker_mutex; } else if(argc == 2 && strcmp(argv[1],"mutexes") == 0) { method = "Mutexes"; func = worker_mutexes; } else if(argc == 2 && strcmp(argv[1],"spin") == 0) { method = "Bitlocks-Spin"; func = worker_bitlock_spin; } else if(argc != 1) { fprintf(stderr, "usage: ./bitlock_test \n"); exit(-1); } printf("\nTesting %s\n\n", method); int rc; size_t i, num_threads = 30; TestThread workers[num_threads]; locks = (char*)calloc(1, (NUM_LOOPS+7)/8); data = (char*)calloc(1, NUM_LOOPS); mutexes = (pthread_mutex_t*)calloc(NUM_LOOPS, sizeof(pthread_mutex_t)); for(i = 0; i < NUM_LOOPS; i++) pthread_mutex_init(&mutexes[i], NULL); // for(i = 0; i < num_threads; i++) // printf("cummulative sum %zu: %zu\n", i, cumm_sum(i)); // Create threads for(i = 0; i < num_threads; i++) { workers[i] = (TestThread){.id = i+1, .result = 0}; rc = pthread_create(&workers[i].th, NULL, func, &workers[i]); if(rc) { fprintf(stderr, "pthread error: %s\n", strerror(rc)); exit(-1); } } // Wait for threads to finish for(i = 0; i < num_threads; i++) { rc = pthread_join(workers[i].th, NULL); if(rc) { fprintf(stderr, "pthread error: %s\n", strerror(rc)); exit(-1); } } for(i = 0; i < NUM_LOOPS; i++) pthread_mutex_destroy(&mutexes[i]); size_t sum = 0, expsum = 0; for(i = 0; i < NUM_LOOPS; i++) sum += data[i]; for(i = 0; i < num_threads; i++) sum += workers[i].result; // for(i = 0; i < num_threads; i++) printf("got: %zu %zu\n", i, workers[i].result); // 0 1 2 3 4 5 // 0 1 3 6 10 expsum = cumm_sum(NUM_LOOPS-1)*num_threads + cumm_sum(num_threads)*NUM_LOOPS; // expsum = cumm_sum(NUM_LOOPS-1) * num_threads; bool pass = (sum == expsum); for(i = 0; i < sizeof(locks) && locks[i] == 0; i++) {} if(i < sizeof(locks)) { printf("locks not zeroed!\n"); pass = false; } printf("sum: %zu exp: %zu\n", sum, expsum); printf("%s.\n\n", pass ? "Pass" : "Fail"); free(mutexes); free(data); free(locks); return pass ? EXIT_SUCCESS : EXIT_FAILURE; } BitArray-2.0/dev/bitlock_try_test.c000066400000000000000000000040471266571572500174230ustar00rootroot00000000000000/* dev/bitlock_try_test.c project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sep 2014 */ #include #include #include #include #include #include #include "bit_macros.h" // Use the bitlock_try_acquire() macro to visit each number exactly once typedef struct { pthread_t th; size_t id, result; uint8_t *locks; } TestThread; #define NWORKERS 10 #define LIMIT 10000 void* worker(void *ptr) { TestThread *wrkr = (TestThread*)ptr; size_t i, locked; for(i = 0; i < LIMIT; i++) { bitlock_try_acquire(wrkr->locks, i, &locked); wrkr->result += locked*i; if((i&0xff) == 0xff) { usleep(50); sched_yield(); } } return NULL; } // sum of 0 up to num (inclusive) #define cumm_sum(num) ((num)*(((num)+1)/2)+(((num)&1) ? 0 : (num)/2)) int main(int argc, char **argv) { (void)argc; (void)argv; size_t i; int rc; uint8_t *locks = (uint8_t*)calloc((LIMIT+7)/8, sizeof(uint8_t)); TestThread *workers = (TestThread*)calloc(NWORKERS, sizeof(TestThread)); // Create threads for(i = 0; i < NWORKERS; i++) { workers[i] = (TestThread){.id = i, .result = 0, .locks = locks}; rc = pthread_create(&workers[i].th, NULL, worker, &workers[i]); if(rc) { fprintf(stderr, "pthread error: %s\n", strerror(rc)); exit(-1); } } // Wait for threads to finish for(i = 0; i < NWORKERS; i++) { rc = pthread_join(workers[i].th, NULL); if(rc) { fprintf(stderr, "pthread error: %s\n", strerror(rc)); exit(-1); } } size_t sum = 0, expsum = cumm_sum(LIMIT-1); for(i = 0; i < NWORKERS; i++) sum += workers[i].result; bool pass = (sum == expsum); for(i = 0; i < sizeof(locks) && locks[i] == 0xff; i++) {} if(i < sizeof(locks)) { printf("locks not all ones!\n"); pass = false; } printf("sum: %zu exp: %zu\n", sum, expsum); printf("%s.\n\n", pass ? "Pass" : "Fail"); free(workers); free(locks); return pass ? EXIT_SUCCESS : EXIT_FAILURE; } BitArray-2.0/examples/000077500000000000000000000000001266571572500147265ustar00rootroot00000000000000BitArray-2.0/examples/Makefile000066400000000000000000000006431266571572500163710ustar00rootroot00000000000000ifndef CC CC = gcc endif CFLAGS = -Wall -Wextra -I.. -L.. all: example_cpp example_c example_cpp: example.cpp ../libbitarr.a $(CXX) $(CFLAGS) -o example_cpp example.cpp -lbitarr example_c: example.c ../libbitarr.a $(CC) $(CFLAGS) -Wc++-compat -o example_c example.c -lbitarr test: example_c example_cpp ./example_c ./example_cpp clean: rm -rf example_cpp example_c *.o *.dSYM *.greg .PHONY: all clean test BitArray-2.0/examples/example.c000066400000000000000000000012301266571572500165210ustar00rootroot00000000000000/* examples/example.c project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sep 2014 */ #include #include #include "bit_array.h" int main(int argc, char* argv[]) { if(argc > 1) { printf("Unused args '%s..'\n", argv[1]); printf("Usage: ./%s\n", argv[0]); return EXIT_FAILURE; } BIT_ARRAY *bitarr = bit_array_create(10); bit_array_print(bitarr, stdout); fputc('\n', stdout); bit_array_set_bits(bitarr, 3, 1,2,5); bit_array_print(bitarr, stdout); fputc('\n', stdout); return EXIT_SUCCESS; } BitArray-2.0/examples/example.cpp000066400000000000000000000012101266571572500170570ustar00rootroot00000000000000/* examples/example.cpp project: bit array C library url: https://github.com/noporpoise/BitArray/ maintainer: Isaac Turner license: Public Domain, no warranty date: Sep 2014 */ #include #include "bit_array.h" int main(int argc, char* argv[]) { using namespace std; if(argc > 1) { cout << " Unused args '" << argv[1] << "..'\n"; cout << "Usage: " << argv[0] << "\n"; return -1; } BIT_ARRAY *bitarr = bit_array_create(10); bit_array_print(bitarr, stdout); cout << "\n"; bit_array_set_bits(bitarr, 3, 1,2,5); bit_array_print(bitarr, stdout); cout << "\n"; return 0; }