Compare commits
3 Commits
c78a3e7285
...
4fa4a5d366
| Author | SHA1 | Date | |
|---|---|---|---|
| 4fa4a5d366 | |||
| e25d79e95b | |||
| 4be7c56b88 |
@@ -10,13 +10,10 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
|
||||
*/
|
||||
|
||||
/* Warning: Work in progress. Should not work before I spend a lot of time
|
||||
* on it, the original "bitboard" approach for moves generation is pretty
|
||||
* difficult to program/debug
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "pool.h"
|
||||
@@ -25,182 +22,279 @@
|
||||
#include "list.h"
|
||||
|
||||
typedef enum {
|
||||
A = 0,
|
||||
B,
|
||||
C,
|
||||
D
|
||||
A, B, C, D
|
||||
} amphipod_t;
|
||||
|
||||
static const u64 cost[] = {
|
||||
1, 10, 100, 1000
|
||||
};
|
||||
|
||||
/*
|
||||
* #############
|
||||
* #abcdefghijk#
|
||||
* ###l#m#n#o###
|
||||
* #p#q#r#s#
|
||||
* #########
|
||||
*
|
||||
* From initial position, we can go:
|
||||
* - to a, b, d, f, h, j, k
|
||||
* - to destination room if room unlocked
|
||||
* To unlock a room:
|
||||
* - Room must be empty or contain only correct amphipods
|
||||
*
|
||||
*
|
||||
*
|
||||
* From a, b, d, f, h, j, k, we can go only to correct unlocked room
|
||||
*
|
||||
* Init:
|
||||
* from start position
|
||||
* bit 63: a
|
||||
* bit 62: b
|
||||
* bit 61: c
|
||||
* ...
|
||||
* bit 59: e
|
||||
* ...
|
||||
* bit 57: g
|
||||
* ...
|
||||
* bit 55: i
|
||||
* ...
|
||||
* bit 53: k
|
||||
* ...
|
||||
* bit 61 - 16 = 45: l
|
||||
* bit 59 - 16 = 43: m
|
||||
* ...
|
||||
* bit 53 - 16 = 39: o
|
||||
* ...
|
||||
* bit 61 - 32 = 29: p
|
||||
* ...
|
||||
* bit 55 - 32 = 23: s
|
||||
*
|
||||
* move up is current << 16
|
||||
* move left is current << 1
|
||||
* move right is current >> 1
|
||||
*
|
||||
* forbidden mask:
|
||||
* abcdefghijk l m n o p q r s
|
||||
* ...........1111111.1.1.1.111111111.1.1.1.11111111111111111111111
|
||||
* 0000000000011111110101010111111111010101011111111111111111111111
|
||||
*
|
||||
* allowed mask:
|
||||
* abcdefghijk l m n o p q r s
|
||||
* 1111111111100000001010101000000000101010100000000000000000000000
|
||||
* ...........1111111.1.1.1.111111111.1.1.1.11111111111111111111111
|
||||
* 0000000000011111110101010111111111010101011111111111111111111111
|
||||
*/
|
||||
#define FORBIDDEN 0x1FD57FD57FFFFFUL
|
||||
#define ALLOWED (~FORBIDDEN)
|
||||
#define HALLWAY 0x7F /* 0x000001111111 */
|
||||
#define ROOM_A 0xF00 /* 0x111100000000*/
|
||||
#define ROOM_B 0xF000 /* 0x1111000000000000*/
|
||||
#define ROOM_C 0xF0000 /* 0x11110000000000000000*/
|
||||
#define ROOM_D 0xF00000 /* 0x111100000000000000000000*/
|
||||
#define ROOM 0xFFFF00
|
||||
|
||||
static u32 rooms[4] = { ROOM_A, ROOM_B, ROOM_C, ROOM_D };
|
||||
|
||||
typedef struct pos {
|
||||
u64 amp[4]; /* bitboards */
|
||||
u32 amp[4]; /* bitboards */
|
||||
int moves;
|
||||
u64 cost;
|
||||
u64 occupied;
|
||||
u64 available; /* ALLOWED & ~occupied */
|
||||
struct list_head list_pos;
|
||||
u32 occupied;
|
||||
u32 available; /* ALLOWED & ~occupied */
|
||||
u32 final; /* 1 if final destination */
|
||||
int ok; /* amphipods in correct place */
|
||||
struct list_head list;
|
||||
} pos_t;
|
||||
|
||||
pool_t *pool_pos;
|
||||
pos_t *pos_zero;
|
||||
typedef struct hash {
|
||||
u32 amp[4]; /* bitboards */
|
||||
u64 cost;
|
||||
//u32 count; /* collisions */
|
||||
struct list_head list;
|
||||
} hash_t;
|
||||
|
||||
static inline u64 get_occupancy(pos_t *pos)
|
||||
#define HASH_SIZE 4096
|
||||
struct {
|
||||
u32 count;
|
||||
struct list_head list;
|
||||
} hasht[HASH_SIZE];
|
||||
|
||||
static inline u32 get_occupancy(pos_t *pos)
|
||||
{
|
||||
return pos->amp[A] | pos->amp[B] | pos->amp[C] | pos->amp[D];
|
||||
}
|
||||
|
||||
/* here we allow mask to be in forbidden places
|
||||
pool_t *pool_pos;
|
||||
pool_t *pool_hash;
|
||||
|
||||
LIST_HEAD(pos_queue);
|
||||
|
||||
static void init_hash()
|
||||
{
|
||||
for (int i = 0; i < HASH_SIZE; ++i) {
|
||||
hasht[i].count = 0;
|
||||
INIT_LIST_HEAD(&hasht[i].list);
|
||||
}
|
||||
}
|
||||
|
||||
/* get a position from memory pool
|
||||
*/
|
||||
static void mask_print(u64 bits)
|
||||
static hash_t *get_hash(pos_t *pos)
|
||||
{
|
||||
//log_f(1, "A=%lx %lu\n", pos->amp[A], pos->amp[A]);
|
||||
//log(1, " B=%lx %lu\n", pos->amp[B], pos->amp[B]);
|
||||
//log(1, " C=%lx %lu\n", pos->amp[C], pos->amp[C]);
|
||||
//log(1, " D=%lx %lu\n", pos->amp[D], pos->amp[D]);
|
||||
|
||||
u64 tmp;
|
||||
int count;
|
||||
log(4, "%lu: popcount=%d ", bits, popcount64(bits));
|
||||
bit_for_each64_2(count, tmp, bits) {
|
||||
log(4, " %d", count);
|
||||
}
|
||||
log(4, "\n");
|
||||
|
||||
log(1, "#############\n#");
|
||||
for (int i = 63; i > 21; --i) {
|
||||
u64 mask = 1UL << i;
|
||||
//log(1, "bit = %d A=%d B=%d C=%d D=%d\n");
|
||||
if (bits & mask)
|
||||
printf("X");
|
||||
else if (FORBIDDEN & mask)
|
||||
printf("#");
|
||||
else
|
||||
printf(".");
|
||||
|
||||
if (i == 52) { /* hallway line */
|
||||
printf("\n");
|
||||
i -= 3;
|
||||
} else if (i == (52 - 16)) { /* rooms top line */
|
||||
printf("\n ");
|
||||
i -= 5;
|
||||
} else if (i == (52 - 32)) { /* rooms bottom line */
|
||||
printf("\n ");
|
||||
i -= 5;
|
||||
}
|
||||
}
|
||||
printf("\n #########\n");
|
||||
hash_t *new;
|
||||
if (!(new = pool_get(pool_hash)))
|
||||
return NULL;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
new->amp[i] = pos->amp[i];
|
||||
new->cost = pos->cost;
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
return new;
|
||||
}
|
||||
|
||||
static void burrow_print(pos_t *pos)
|
||||
static void hash_stats()
|
||||
{
|
||||
log_f(4, "A=%lx %lu\n", pos->amp[A], pos->amp[A]);
|
||||
log(4, " B=%lx %lu\n", pos->amp[B], pos->amp[B]);
|
||||
log(4, " C=%lx %lu\n", pos->amp[C], pos->amp[C]);
|
||||
log(4, " D=%lx %lu\n", pos->amp[D], pos->amp[D]);
|
||||
u64 ncollisions = 0;
|
||||
u32 min = -1, max = 0;
|
||||
|
||||
for (int i = 0; i < HASH_SIZE; ++i) {
|
||||
ncollisions += hasht[i].count;
|
||||
if (hasht[i].count < min)
|
||||
min = hasht[i].count;
|
||||
if (hasht[i].count > max)
|
||||
max = hasht[i].count;
|
||||
}
|
||||
log_f(1, "hash stats: size=%d min=%u max=%u total=%lu average=%lu\n", HASH_SIZE,
|
||||
min, max, ncollisions, ncollisions / HASH_SIZE);
|
||||
}
|
||||
|
||||
/* hash function from:
|
||||
* http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
*/
|
||||
#define FNV_offset_basis 2166136261
|
||||
#define FNV_prime 16777619
|
||||
|
||||
static hash_t *hash(pos_t *pos)
|
||||
{
|
||||
hash_t *cur;
|
||||
u32 val = FNV_offset_basis;
|
||||
|
||||
/* we use the 2 first chars of the amps 32, this should be enough
|
||||
* to avoid most collisions
|
||||
*/
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
u64 tmp;
|
||||
int count;
|
||||
log(4, "%c: popcount=%d ", i + 'A', popcount64(pos->amp[i]));
|
||||
bit_for_each64_2(count, tmp, pos->amp[i]) {
|
||||
log(4, " %d", count);
|
||||
uchar *input = (uchar *) &pos->amp[i];
|
||||
for (int c = 0; c < 2; ++c) {
|
||||
val ^= input[c];
|
||||
val *= FNV_prime;
|
||||
}
|
||||
log(4, "\n");
|
||||
}
|
||||
//val ^= pos->cost;
|
||||
log_f(1, "hash=%u", val);
|
||||
val %= HASH_SIZE;
|
||||
log_f(1, " ->%u, count=%d\n", val, hasht[val].count);
|
||||
list_for_each_entry(cur, &hasht[val].list, list) {
|
||||
if (pos->amp[0] == cur->amp[0] &&
|
||||
pos->amp[1] == cur->amp[1] &&
|
||||
pos->amp[2] == cur->amp[2] &&
|
||||
pos->amp[3] == cur->amp[3]) {
|
||||
if (pos->cost >= cur->cost) {
|
||||
log(1, "collision, worse cost.\n");
|
||||
return NULL;
|
||||
} else {
|
||||
log(1, "collision, better cost.\n");
|
||||
cur->cost = pos->cost; /* adjust better solution */
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
hasht[val].count++;
|
||||
log(1, "adding hash count=%u\n", hasht[val].count);
|
||||
cur = get_hash(pos);
|
||||
list_add(&cur->list, &hasht[val].list);
|
||||
return cur;
|
||||
}
|
||||
|
||||
log(1, "#############\n#");
|
||||
for (int i = 63; i > 21; --i) {
|
||||
u64 mask = 1UL << i;
|
||||
if (FORBIDDEN & mask)
|
||||
printf("#");
|
||||
else {
|
||||
//log(1, "bit = %d A=%d B=%d C=%d D=%d\n");
|
||||
if (pos->amp[A] & mask)
|
||||
printf("A");
|
||||
else if (pos->amp[B] & mask)
|
||||
printf("B");
|
||||
else if (pos->amp[C] & mask)
|
||||
printf("C");
|
||||
else if (pos->amp[D] & mask)
|
||||
printf("D");
|
||||
else
|
||||
printf(".");
|
||||
/*
|
||||
* #############
|
||||
* #ab.c.d.e.fg#
|
||||
* ###h#i#j#k###
|
||||
* #l#m#n#o#
|
||||
* #p#q#r#s#
|
||||
* #t#u#v#w#
|
||||
* #########
|
||||
*
|
||||
* We name a-g H1-H7, h & l are A1-A2, i & m are B1 & B2, etc...
|
||||
*/
|
||||
|
||||
/* note that to determine of hallway space is left or right,
|
||||
* we simply need to divide the room number by 4.
|
||||
*/
|
||||
typedef enum {
|
||||
_H1 = 0, _H2, _H3, _H4, _H5, _H6, _H7, /* 0-6 */
|
||||
_WRONG = 7, /* 7 */
|
||||
_A1 = 8, _A2, _A3, _A4, /* 8-11 */
|
||||
_B1, _B2, _B3, _B4, /* 12-15 */
|
||||
_C1, _C2, _C3, _C4, /* 16-19 */
|
||||
_D1, _D2, _D3, _D4, /* 20-23 */
|
||||
} _space_t;
|
||||
|
||||
#define LEFT 0
|
||||
#define RIGHT 1
|
||||
|
||||
static s32 room_exit[4][2][6] = {
|
||||
{ { _H2, _H1, -1 }, /* room A left */
|
||||
{ _H3, _H4, _H5, _H6, _H7, -1 } /* room A right */
|
||||
},
|
||||
{ { _H3, _H2, _H1, -1 }, /* room B left */
|
||||
{ _H4, _H5, _H6, _H7, -1 } /* room B right */
|
||||
},
|
||||
{ { _H4, _H3, _H2, _H1, -1 }, /* room C left */
|
||||
{ _H5, _H6, _H7, -1 } /* room C right */
|
||||
},
|
||||
{ { _H5, _H4, _H3, _H2, _H1, -1 }, /* room D left */
|
||||
{ _H6, _H7, -1 } /* room D right */
|
||||
}
|
||||
if (i == 52) { /* hallway line */
|
||||
printf("\n");
|
||||
i -= 3;
|
||||
} else if (i == (52 - 16)) { /* rooms top line */
|
||||
printf("\n ");
|
||||
i -= 5;
|
||||
} else if (i == (52 - 32)) { /* rooms bottom line */
|
||||
printf("\n ");
|
||||
i -= 5;
|
||||
};
|
||||
|
||||
#define BIT(c) (1 << (c))
|
||||
|
||||
static char *int2bin(u32 mask, char *ret)
|
||||
{
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
ret[31-i] = mask & BIT(i)? '1': '0';
|
||||
}
|
||||
ret[32] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *cells[] = {
|
||||
"H1", "H2", "H3", "H4", "H5", "H6", "H7", "BAD",
|
||||
"A1", "A2", "A3", "A4",
|
||||
"B1", "B2", "B3", "B4",
|
||||
"C1", "C2", "C3", "C4",
|
||||
"D1", "D2", "D3", "D4",
|
||||
};
|
||||
|
||||
static char *int2pos(u32 mask)
|
||||
{
|
||||
static char res[1024];
|
||||
char *p = res;
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
if (mask & BIT(i)) {
|
||||
*p++ = ' ';
|
||||
strcpy(p, cells[i]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
printf("\n #########\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
H1 = BIT(_H1), H2 = BIT(_H2), H3 = (_H3), H4 = (_H4),
|
||||
H5 = (_H5), H6 = (_H6), H7 = (_H7),
|
||||
A1 = (_A1), A2 = (_A2), A3 = (_A3), A4 = (_A4),
|
||||
B1 = (_B1), B2 = (_B2), B3 = (_B3), B4 = (_B4),
|
||||
C1 = (_C1), C2 = (_C2), C3 = (_C3), C4 = (_C4),
|
||||
D1 = (_D1), D2 = (_D2), D3 = (_D3), D4 = (_D4),
|
||||
} space_t;
|
||||
|
||||
/* Steps to move to hallway
|
||||
*/
|
||||
typedef enum {
|
||||
A1H = 1, A2H = 2, A3H = 3, A4H = 4
|
||||
} out_t;
|
||||
|
||||
/* Mask which disallow moves to hallway
|
||||
*/
|
||||
typedef struct {
|
||||
u32 from, to; /* unused */
|
||||
uint mask, dist;
|
||||
} move_t;
|
||||
|
||||
/* Steps to move from space outside room to hallway destination
|
||||
*/
|
||||
typedef enum {
|
||||
AH1 = 2, AH2 = 1, AH3 = 1, AH4 = 3, AH5 = 5, AH6 = 7 , AH7 = 8,
|
||||
BH1 = 4, BH2 = 3, BH3 = 1, BH4 = 1, BH5 = 3, BH6 = 5 , BH7 = 6,
|
||||
CH1 = 6, CH2 = 5, CH3 = 3, CH4 = 1, CH5 = 1, CH6 = 3 , CH7 = 4,
|
||||
DH1 = 8, DH2 = 7, DH3 = 5, DH4 = 3, DH5 = 1, DH6 = 1 , DH7 = 2
|
||||
} steps_t;
|
||||
|
||||
/*
|
||||
* #############
|
||||
* #ab.c.d.e.fg#
|
||||
* ###h#i#j#k###
|
||||
* #l#m#n#o#
|
||||
* #p#q#r#s#
|
||||
* #t#u#v#w#
|
||||
* #########
|
||||
*/
|
||||
|
||||
/* set bits between 2 positions
|
||||
* setbits(2, 4) -> 0011100
|
||||
*/
|
||||
static u32 setbits(int from, int to)
|
||||
{
|
||||
u32 ret = 0;
|
||||
for (int i = from; i < to; ++i)
|
||||
ret |= BIT(i);
|
||||
log_f(4, "(%d, %d) = %d\n", from, to, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int h2h[][7] = {
|
||||
{ 2, 1, 1, 3, 5, 7, 8 }, /* A */
|
||||
{ 4, 3, 1, 1, 3, 5, 6 }, /* B */
|
||||
{ 6, 5, 3, 1, 1, 3, 4 }, /* C */
|
||||
{ 8, 7, 5, 3, 1, 1, 2 } /* D */
|
||||
};
|
||||
|
||||
/* get a position from memory pool
|
||||
*/
|
||||
static pos_t *get_pos(pos_t *from)
|
||||
@@ -210,136 +304,376 @@ static pos_t *get_pos(pos_t *from)
|
||||
return NULL;
|
||||
if (!from) {
|
||||
new->amp[0] = new->amp[1] = new->amp[2] = new->amp[3] = 0;
|
||||
new->moves = 0;
|
||||
new->moves = new->ok = 0;
|
||||
new->cost = new->occupied = new->available = 0;
|
||||
} else {
|
||||
*new = *from;
|
||||
}
|
||||
INIT_LIST_HEAD(&new->list_pos);
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
return new;
|
||||
}
|
||||
|
||||
static u64 move_up(pos_t *pos, int amphipod)
|
||||
/* release a position from list
|
||||
*/
|
||||
static void free_pos(pos_t *pos)
|
||||
{
|
||||
pos_t newpos = *pos;
|
||||
u64 new, tmp, occupied = 0;
|
||||
new = pos->amp[amphipod] << 16;
|
||||
if (pos) {
|
||||
pool_add(pool_pos, pos);
|
||||
} else {
|
||||
log(1, "Fatal bar\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
log(1, "********* moving %c up\n", amphipod + 'A');
|
||||
/* push position to stack
|
||||
*/
|
||||
static void push_pos(pos_t *pos)
|
||||
{
|
||||
if (pos) {
|
||||
list_add(&pos->list, &pos_queue);
|
||||
} else {
|
||||
log(1, "Fatal foo\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* pop a position from stack
|
||||
*/
|
||||
static pos_t *pop_pos()
|
||||
{
|
||||
pos_t *pos;
|
||||
pos = list_first_entry_or_null(&pos_queue, pos_t, list);
|
||||
if (pos)
|
||||
list_del(&pos->list);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void print_moves(move_t (*moves)[24])
|
||||
{
|
||||
int hallway, room;
|
||||
|
||||
log(4, "From rooms to hallway\n");
|
||||
for (room = _A1; room <= _D4; ++room) {
|
||||
for (hallway = _H1; hallway <= _H7; ++hallway) {
|
||||
log(4, "%s -> %s ", cells[room], cells[hallway]);
|
||||
log(4, "dist=%d ", moves[room][hallway].dist);
|
||||
log(4, "mask=");
|
||||
log(4, "%s\n", int2pos(moves[room][hallway].mask));
|
||||
}
|
||||
}
|
||||
log(4, "From hallway to rooms\n");
|
||||
for (hallway = _H1; hallway <= _H7; ++hallway) {
|
||||
for (room = _A1; room <= _D4; ++room) {
|
||||
log(4, "%s -> %s ", cells[hallway], cells[room]);
|
||||
log(4, "dist=%d ", moves[hallway][room].dist);
|
||||
log(4, "mask=");
|
||||
log(4, "%s\n", int2pos(moves[hallway][room].mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mask_print(u32 bits)
|
||||
{
|
||||
u32 tmp;
|
||||
int count;
|
||||
int rows = popcount32(bits);
|
||||
|
||||
if (rows > 4)
|
||||
rows /= 4;
|
||||
log(1, "%u: popcount=%d ", bits, popcount32(bits));
|
||||
bit_for_each32_2(count, tmp, bits) {
|
||||
log(1, " %d", count);
|
||||
}
|
||||
log(1, "\n");
|
||||
|
||||
log(1, "#############\n#");
|
||||
for (int i = _H1; i <= _H7; ++i) { /* hallway */
|
||||
if (i >= 2 && i <= 5)
|
||||
log(1, "$");
|
||||
log(1, "%c", bits & BIT(i)? 'X': '.');
|
||||
}
|
||||
log(1, "#\n");
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
int pos = 8 + i;
|
||||
log(1, i? " #": "###");
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
log(1, "%c#", bits & BIT(pos + 4 * j)? 'X': '.');
|
||||
}
|
||||
log(1, i? "\n": "##\n");
|
||||
}
|
||||
log(1, " #########\n");
|
||||
}
|
||||
|
||||
static void burrow_print(pos_t *pos)
|
||||
{
|
||||
u32 tmp;
|
||||
int count;
|
||||
int rows = popcount32(pos->amp[0]);
|
||||
log_f(1, "rows=%d moves=%d cost=%lu\n", rows, pos->moves, pos->cost);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
occupied |= pos->amp[i];
|
||||
bit_for_each32_2(count, tmp, pos->amp[i]) {
|
||||
log(1, " %d", count);
|
||||
}
|
||||
log(1, "occupied:\n");
|
||||
mask_print(occupied);
|
||||
log(1, "current occupation:\n");
|
||||
mask_print(pos->amp[amphipod]);
|
||||
log(1, "new occupation:\n");
|
||||
mask_print(new);
|
||||
|
||||
tmp = new & ALLOWED;
|
||||
if (tmp != new)
|
||||
log(1, "%c cannot go up (forbidden)\n", amphipod + 'A');
|
||||
tmp = new & ~occupied;
|
||||
if (tmp != new)
|
||||
log(1, "%c cannot go up (occupied)\n", amphipod + 'A');
|
||||
newpos.amp[amphipod] = new & ALLOWED & ~occupied;
|
||||
//burrow_print(&newpos);
|
||||
return newpos.amp[amphipod];
|
||||
}
|
||||
|
||||
static u64 move_down(pos_t *pos, int amphipod)
|
||||
log(1, "\n");
|
||||
|
||||
log(1, "#############\n#");
|
||||
for (int i = _H1; i <= _H7; ++i) { /* hallway */
|
||||
if (i >= 2 && i <= 5)
|
||||
log(1, "$");
|
||||
log(1, "%c",
|
||||
BIT(i) & pos->amp[A]? 'A':
|
||||
BIT(i) & pos->amp[B]? 'B':
|
||||
BIT(i) & pos->amp[C]? 'C':
|
||||
BIT(i) & pos->amp[D]? 'D': '.');
|
||||
}
|
||||
log(1, "#\n");
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
int offset = 8 + i;
|
||||
log(1, i? " #": "###");
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
log(1, "%c#",
|
||||
BIT(offset + 4 * j) & pos->amp[A]? 'A':
|
||||
BIT(offset + 4 * j) & pos->amp[B]? 'B':
|
||||
BIT(offset + 4 * j) & pos->amp[C]? 'C':
|
||||
BIT(offset + 4 * j) & pos->amp[D]? 'D': '.');
|
||||
//log(4, "%c#", bits & BIT(pos + 4 * j)? 'X': '.');
|
||||
}
|
||||
log(1, i? "\n": "##\n");
|
||||
}
|
||||
log(1, " #########\n");
|
||||
}
|
||||
|
||||
/* generate possible moves between hallway and rooms
|
||||
*/
|
||||
static move_t moves[24][24];
|
||||
|
||||
/* calculate distance and move mask for all possible moves
|
||||
* (room -> hallway ans hallway -> room)
|
||||
*/
|
||||
static move_t (*init_moves())[24]
|
||||
{
|
||||
pos_t newpos = *pos;
|
||||
u64 new;
|
||||
log(1, "********* moving %c down\n", amphipod + 'A');
|
||||
new = pos->amp[amphipod] >> 16;
|
||||
printf("occupied\n");
|
||||
mask_print(pos->occupied);
|
||||
printf("available\n");
|
||||
mask_print(pos->available);
|
||||
newpos.amp[amphipod] = new & pos->available;
|
||||
return newpos.amp[amphipod];
|
||||
int hallway, room, dist, pos;
|
||||
u32 mask_h, mask_r;
|
||||
char bin[64];
|
||||
|
||||
for (room = _A1; room <= _D4; ++room) {
|
||||
pos = (room >> 2) - 2;
|
||||
for (hallway = _H1; hallway <= _H7; ++hallway) {
|
||||
dist = h2h[pos][hallway] + room % 4 + 1;
|
||||
log(3, "\ndist(%s, %s) = %d - h2h=%d pos=%d\n", cells[room],
|
||||
cells[hallway], dist,
|
||||
h2h[pos][hallway], pos);
|
||||
|
||||
/* from rool to hallway */
|
||||
if (room >> 2 > hallway)
|
||||
mask_h = setbits(hallway, room >> 2);
|
||||
else
|
||||
mask_h = setbits(room >> 2, hallway + 1);
|
||||
mask_r = setbits(room & ~3, room);
|
||||
log(5, "\tmask_r=%d <%s>", mask_r, int2bin(mask_r, bin));
|
||||
log(5, "\tmask_h=%d <%s>\n", mask_h, int2bin(mask_h, bin));
|
||||
log(3, "%s\n", int2pos(mask_r | mask_h));
|
||||
moves[room][hallway].mask = mask_r | mask_h;
|
||||
moves[room][hallway].dist = dist;
|
||||
|
||||
/* from hallway to room */
|
||||
if (room >> 2 > hallway)
|
||||
mask_h = setbits(hallway + 1, room >> 2);
|
||||
else
|
||||
mask_h = setbits(room >> 2, hallway);
|
||||
mask_r = setbits(room & ~3, room + 1);
|
||||
log(5, "\tmask_r=%d <%s>", mask_r, int2bin(mask_r, bin));
|
||||
log(5, "\tmask_h=%d <%s>\n", mask_h, int2bin(mask_h, bin));
|
||||
log(3, "%s\n", int2pos(mask_r | mask_h));
|
||||
moves[hallway][room].mask = mask_r | mask_h;
|
||||
moves[hallway][room].dist = dist;
|
||||
|
||||
}
|
||||
log(3, "\n");
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
|
||||
static u64 move_left(pos_t *pos, int amphipod)
|
||||
static pos_t *newmove(pos_t *pos, amphipod_t amp, u32 from, u32 to)
|
||||
{
|
||||
pos_t newpos = *pos;
|
||||
u64 new, tmp, occupied = 0;
|
||||
new = pos->amp[amphipod] << 1;
|
||||
int rows = popcount32(pos->amp[0]);
|
||||
move_t *move = &moves[from][to];
|
||||
pos_t *newpos;
|
||||
hash_t *collision;
|
||||
|
||||
log(1, "********* moving %c left\n", amphipod + 'A');
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
occupied |= pos->amp[i];
|
||||
log_f(1, "rows=%d amp=%c from=%s to=%s dist=%u ok=%d cost=%lu\n",
|
||||
rows, amp + 'A',
|
||||
cells[from], cells[to],
|
||||
move->dist, pos->ok, move->dist * cost[amp]);
|
||||
|
||||
collision = hash(pos);
|
||||
if (!collision) {
|
||||
log(1, "collision, skipping move :\n");
|
||||
burrow_print(pos);
|
||||
// free_pos(newpos);
|
||||
return NULL;
|
||||
}
|
||||
log(1, "occupied:\n");
|
||||
mask_print(occupied);
|
||||
log(1, "current occupation:\n");
|
||||
mask_print(pos->amp[amphipod]);
|
||||
log(1, "new occupation:\n");
|
||||
mask_print(new);
|
||||
if (!(newpos = get_pos(pos)))
|
||||
return NULL;
|
||||
|
||||
tmp = new & ALLOWED;
|
||||
if (tmp != new)
|
||||
log(1, "%c cannot go left (forbidden)\n", amphipod + 'A');
|
||||
tmp = new & ~occupied;
|
||||
if (tmp != new)
|
||||
log(1, "%c cannot go left (occupied)\n", amphipod + 'A');
|
||||
newpos.amp[amphipod] = new & ALLOWED & ~occupied;
|
||||
//burrow_print(&newpos);
|
||||
return newpos.amp[amphipod];
|
||||
newpos->amp[amp] ^= BIT(from);
|
||||
newpos->amp[amp] |= BIT(to);
|
||||
newpos->occupied ^= BIT(from);
|
||||
newpos->occupied |= BIT(to);
|
||||
newpos->moves++;
|
||||
newpos->cost += move->dist * cost[amp];
|
||||
|
||||
|
||||
if (to >= A1) { /* final destination */
|
||||
newpos->ok++;
|
||||
newpos->final |= BIT(to);
|
||||
log(1, "Final destination %s, ok=%d, final=%s\n", cells[to],
|
||||
newpos->ok, int2pos(newpos->final));
|
||||
if (newpos->ok == rows * 4) {
|
||||
log(1, "found solution! cost=%lu\n", newpos->cost);
|
||||
burrow_print(newpos);
|
||||
free_pos(newpos);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u64 move_right(pos_t *pos, int amphipod)
|
||||
}
|
||||
|
||||
burrow_print(newpos);
|
||||
push_pos(newpos);
|
||||
log(1, "New position: moves=%d cost=%lu\n", newpos->moves, newpos->cost);
|
||||
return newpos;
|
||||
}
|
||||
|
||||
/* generate all moves from a given position
|
||||
*/
|
||||
static void genmoves(pos_t *pos)
|
||||
{
|
||||
pos_t newpos = *pos;
|
||||
u64 new;
|
||||
log(1, "********* moving %c right\n", amphipod + 'A');
|
||||
new = pos->amp[amphipod] >> 1;
|
||||
printf("occupied\n");
|
||||
mask_print(pos->occupied);
|
||||
printf("available\n");
|
||||
mask_print(pos->available);
|
||||
newpos.amp[amphipod] = new & ALLOWED & pos->available;
|
||||
return newpos.amp[amphipod];
|
||||
amphipod_t amp;
|
||||
u32 tmp;
|
||||
int bit;
|
||||
int rows = popcount32(pos->amp[0]);
|
||||
|
||||
log_f(1, "position :\n");
|
||||
burrow_print(pos);
|
||||
for (amp = A; amp <= D; ++amp) {
|
||||
u32 cur = pos->amp[amp];
|
||||
bit_for_each32_2(bit, tmp, cur) {
|
||||
char s1[64], s2[64];
|
||||
int2bin(bit, s1);
|
||||
int2bin(ROOM, s2);
|
||||
log(3, "-> %s %d\n %s\n", s1, bit, s2);
|
||||
|
||||
if (bit >= _A1) { /* in a room */
|
||||
if (BIT(bit) & pos->final) {
|
||||
log(1, "position already ok\n");
|
||||
continue;
|
||||
}
|
||||
/* TODO: already in dest room */
|
||||
int room = (bit >> 2) - 2;
|
||||
log(3, "room = %d\n", room);
|
||||
for (int side = LEFT; side <= RIGHT; ++side) {
|
||||
int *d = room_exit[room][side];
|
||||
for (; *d != -1; ++d) {
|
||||
log(3, "checking %c from %s to %s\n",
|
||||
amp + 'A', cells[bit], cells[*d]);
|
||||
move_t *move = &moves[bit][*d];
|
||||
log(3, "mask=%s dist=%d cost=%lu\n",
|
||||
int2bin(move->mask, s1), move->dist,
|
||||
move->dist * cost[amp]);
|
||||
if (move->mask & pos->occupied) {
|
||||
log(3, "blocked!\n");
|
||||
break;
|
||||
}
|
||||
newmove(pos, amp, bit, *d);
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* hallway */
|
||||
u32 room = rooms[amp], found = 0;
|
||||
int dest, count = 0, tmp;
|
||||
char foo[64];
|
||||
|
||||
log(1, "%c from %s to %#x (%s)\n", amp + 'A', cells[bit], room,
|
||||
int2bin(room, foo));
|
||||
if (room & pos->occupied && !(room & pos->final)) {
|
||||
log(1, "room is occupied\n");
|
||||
continue; /* skip current amp pos */
|
||||
}
|
||||
log(1, "room is available\n");
|
||||
bit_for_each32_2(dest, tmp, room) {
|
||||
move_t *move = &moves[bit][dest];
|
||||
log(1, " %d(%s) -> %d(%s) : ", bit, cells[bit],
|
||||
dest, cells[dest]);
|
||||
if (move->mask & pos->occupied) {
|
||||
log(1, "blocked\n");
|
||||
break;
|
||||
}
|
||||
log(1, "valid\n");
|
||||
found = dest;
|
||||
if (++count >= rows)
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
log(1, "found %s -> %s\n", cells[bit], cells[found]);
|
||||
newmove(pos, amp, bit, found);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* minimal parsing: We just read the 3-5 lines to get
|
||||
* the amphipods location in side rooms
|
||||
*/
|
||||
static void read_input()
|
||||
static char *part2str[] = { " #D#C#B#A#", " #D#B#A#C#" };
|
||||
|
||||
static pos_t *read_input(int part)
|
||||
{
|
||||
size_t alloc = 0;
|
||||
ssize_t buflen;
|
||||
char *buf = NULL;
|
||||
int line = 0;
|
||||
int line = 0, adjline = 0;
|
||||
pos_t *pos = get_pos(NULL);
|
||||
u32 bit;
|
||||
|
||||
while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
|
||||
buf[--buflen] = 0;
|
||||
printf("line=%d str=%s\n", line, buf);
|
||||
log(1, "line=%d str=%s\n", line, buf);
|
||||
if (line == 2 || line == 3) {
|
||||
u64 bit = 45 - 16 * (line - 2);
|
||||
//printf("bit = %d\n", bit);
|
||||
|
||||
if (part == 2 && line == 3) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
bit = 8 + adjline - 2;
|
||||
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
int amp = part2str[i][j * 2 + 3] - 'A';
|
||||
pos->amp[amp] |= BIT(bit);
|
||||
log(3, "setting bit %d to %c\n", bit, amp + 'A');
|
||||
bit += 4;
|
||||
}
|
||||
adjline++;
|
||||
}
|
||||
}
|
||||
bit = 8 + adjline - 2;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int amp = buf[i * 2 + 3] - 'A';
|
||||
//printf("bit = %lu char = %c\n", bit, amp + 'A');
|
||||
pos->amp[amp] |= 1UL << bit;
|
||||
bit -= 2;
|
||||
pos->amp[amp] |= BIT(bit);
|
||||
log(3, "setting bit %d to %c\n", bit, amp + 'A');
|
||||
bit += 4;
|
||||
}
|
||||
}
|
||||
line++;
|
||||
adjline++;
|
||||
}
|
||||
pos->occupied = get_occupancy(pos);
|
||||
pos->available = ALLOWED & ~pos->occupied;
|
||||
burrow_print(pos);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
mask_print(move_up(pos, i));
|
||||
mask_print(move_left(pos, i));
|
||||
mask_print(move_down(pos, i));
|
||||
mask_print(move_right(pos, i));
|
||||
}
|
||||
free(buf);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static s64 part1()
|
||||
@@ -361,6 +695,7 @@ static int usage(char *prg)
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
int opt, part = 1;
|
||||
pos_t *pos;
|
||||
|
||||
while ((opt = getopt(ac, av, "d:p:")) != -1) {
|
||||
switch (opt) {
|
||||
@@ -380,8 +715,28 @@ int main(int ac, char **av)
|
||||
return usage(*av);
|
||||
|
||||
pool_pos = pool_create("pos", 1024, sizeof(pos_t));
|
||||
pool_hash = pool_create("hash", 1024, sizeof(hash_t));
|
||||
init_hash();
|
||||
|
||||
read_input();
|
||||
init_moves();
|
||||
print_moves(moves);
|
||||
pos = read_input(part);
|
||||
push_pos(pos);
|
||||
/*
|
||||
for (int i = 0; i < 4; ++ i) {
|
||||
log(1, "------------ %c\n", 'A' + i);
|
||||
mask_print(pos->amp[i]);
|
||||
}
|
||||
*/
|
||||
mask_print(pos->occupied);
|
||||
|
||||
burrow_print(pos);
|
||||
|
||||
while ((pos = pop_pos())) {
|
||||
genmoves(pos);
|
||||
free_pos(pos);
|
||||
}
|
||||
hash_stats();
|
||||
|
||||
printf("%s : res=%ld\n", *av, part == 1? part1(): part2());
|
||||
|
||||
|
||||
185
2021/day24/aoc-c.c
Normal file
185
2021/day24/aoc-c.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/* aoc-c.c: Advent of Code 2021, day 24 parts 1 & 2
|
||||
*
|
||||
* Copyright (C) 2022 Bruno Raoult ("br")
|
||||
* Licensed under the GNU General Public License v3.0 or later.
|
||||
* Some rights reserved. See COPYING.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this
|
||||
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "bits.h"
|
||||
|
||||
/*
|
||||
* All 14 blocks have the same code, with a1, b1, c1 differing :
|
||||
* inp w
|
||||
* mul x 0
|
||||
* add x z # x = z (0)
|
||||
* mod x 26 # x %= 26 (0)
|
||||
* # = z % 26
|
||||
* div z 1 a1 # z = z / a1 (0)
|
||||
* add x 13 b1 # x += b1 (13)
|
||||
* # = (z % 26) + b1
|
||||
* eql x w # x = (x == w) (0)
|
||||
* eql x 0 # x = !!x (1)
|
||||
* # if (w == (z % 26 + b1)
|
||||
* # x = 0
|
||||
* # else
|
||||
* # x = 1
|
||||
* mul y 0
|
||||
* add y 25 # y = 25 (25)
|
||||
* mul y x # y *= x (25)
|
||||
* # if (w == (z % 26 + b1)
|
||||
* # y = 0
|
||||
*
|
||||
* add y 1 # y++ (26)
|
||||
* # if (w == (z % 26 + b1)
|
||||
* # y = 1
|
||||
* # else
|
||||
* # y = 26
|
||||
*
|
||||
* mul z y # z *= y (0)
|
||||
* # if (w != (z % 26 + b1)
|
||||
* # z *= 26
|
||||
* mul y 0
|
||||
* add y w # y = w
|
||||
* add y 10 c1 # y += c1 (w + 10)
|
||||
* # = w + c1
|
||||
* mul y x # y *= x (w + 10) * 1
|
||||
* # if (w == (z % 26 + b1)
|
||||
* # y = 0
|
||||
* add z y # z += y (w + 10) * 1
|
||||
* # if (w != (z % 26 + b1))
|
||||
* # z = z / a1 * 26 + w + c1
|
||||
* # else
|
||||
* # z = z / a1
|
||||
*
|
||||
* So we end up with :
|
||||
* if (w == (z % 26 + b))
|
||||
* z = z / a
|
||||
* else
|
||||
* z = z / a * 26 + w + c
|
||||
*
|
||||
* a1 is 1 or 26. it means we can have an upper limit MAXZ at each of
|
||||
* the 14 steps : if z > MAXZ, final will not be able to end at zero.
|
||||
* MAXZ = 26^n, n being the number of A=26 from current step to 14th.
|
||||
*/
|
||||
|
||||
struct var {
|
||||
int a, b, c;
|
||||
s64 maxz;
|
||||
} var[14];
|
||||
|
||||
static inline s64 step(int n, int w, s64 z)
|
||||
{
|
||||
return z % 26 + var[n].b - w ? z / var[n].a * 26 + w + var[n].c : z / var[n].a;
|
||||
}
|
||||
|
||||
static void read_input()
|
||||
{
|
||||
size_t alloc = 0;
|
||||
ssize_t buflen;
|
||||
char *buf = NULL;
|
||||
int line = 0;
|
||||
u64 maxz = 1;
|
||||
|
||||
while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
|
||||
buf[--buflen] = 0;
|
||||
|
||||
/* each block is 18 lines */
|
||||
switch (line % 18) {
|
||||
case 4: /* a */
|
||||
sscanf(buf, "%*s %*s %d", &(var[line / 18].a));
|
||||
break;
|
||||
case 5: /* b */
|
||||
sscanf(buf, "%*s %*s %d", &(var[line / 18].b));
|
||||
break;
|
||||
case 15: /* c */
|
||||
sscanf(buf, "%*s %*s %d", &(var[line / 18].c));
|
||||
break;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
/* adjust maxz */
|
||||
for (int i = 13; i >= 0; --i) {
|
||||
maxz *= var[i].a;
|
||||
var[i].maxz = maxz;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
||||
static char *part1(int n, s64 z, char *s)
|
||||
{
|
||||
if (n == 14)
|
||||
return z? NULL: s;
|
||||
else if (z > var[n].maxz)
|
||||
return NULL;
|
||||
|
||||
for(int i = 9; i >= 1; --i) {
|
||||
if (part1(n + 1, step(n, i, z), s)) {
|
||||
s[n] = '0' + i;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *part2(int n, s64 z, char *s)
|
||||
{
|
||||
if (n == 14)
|
||||
return z? NULL: s;
|
||||
else if (z > var[n].maxz)
|
||||
return NULL;
|
||||
|
||||
for (int i = 1; i <= 9; ++i) {
|
||||
if (part2(n + 1, step(n, i, z), s)) {
|
||||
s[n] = '0' + i;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int usage(char *prg)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-d debug_level] [-p part]\n", prg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
int opt, part = 1;
|
||||
static char res[16];
|
||||
|
||||
while ((opt = getopt(ac, av, "d:p:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
debug_level_set(atoi(optarg));
|
||||
break;
|
||||
case 'p': /* 1 or 2 */
|
||||
part = atoi(optarg);
|
||||
if (part < 1 || part > 2)
|
||||
return usage(*av);
|
||||
break;
|
||||
default:
|
||||
return usage(*av);
|
||||
}
|
||||
}
|
||||
if (optind < ac)
|
||||
return usage(*av);
|
||||
|
||||
read_input();
|
||||
|
||||
printf("%s : res=%s\n", *av, part == 1? part1(0, 0, res): part2(0, 0, res));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -77,6 +77,28 @@ static inline int ctz64(u64 n)
|
||||
# endif
|
||||
}
|
||||
|
||||
static inline int ctz32(u32 n)
|
||||
{
|
||||
# if __has_builtin(__builtin_ctz)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin ctz.\n");
|
||||
# endif
|
||||
return __builtin_ctzl(n);
|
||||
|
||||
# elif __has_builtin(__builtin_clz)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin clz.\n");
|
||||
# endif
|
||||
return __WORDSIZE - (__builtin_clz(n & -n) + 1);
|
||||
|
||||
# else
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "emulated.\n");
|
||||
# endif
|
||||
return popcount32((n & −n) − 1);
|
||||
# endif
|
||||
}
|
||||
|
||||
/* count leading zeroes : 00101000 -> 2
|
||||
* ^^
|
||||
*/
|
||||
@@ -100,7 +122,30 @@ static inline int clz64(u64 n)
|
||||
q = (n > 0xF ) << 2; n >>= q; r |= q;
|
||||
q = (n > 0x3 ) << 1; n >>= q; r |= q;
|
||||
r |= (n >> 1);
|
||||
return __WORDSIZE - r - 1;
|
||||
return 64 - r - 1;
|
||||
# endif
|
||||
}
|
||||
|
||||
static inline int clz32(u32 n)
|
||||
{
|
||||
# if __has_builtin(__builtin_clz)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin.\n");
|
||||
# endif
|
||||
return __builtin_clz(n);
|
||||
|
||||
# else
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "emulated.\n");
|
||||
# endif
|
||||
u32 r, q;
|
||||
|
||||
r = (n > 0xFFFF) << 4; n >>= r;
|
||||
q = (n > 0xFF ) << 3; n >>= q; r |= q;
|
||||
q = (n > 0xF ) << 2; n >>= q; r |= q;
|
||||
q = (n > 0x3 ) << 1; n >>= q; r |= q;
|
||||
r |= (n >> 1);
|
||||
return 32 - r - 1;
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -113,7 +158,7 @@ static inline uint ffs64(u64 n)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin ffsl.\n");
|
||||
# endif
|
||||
return __builtin_ffsll(n);
|
||||
return __builtin_ffsl(n);
|
||||
|
||||
# elif __has_builtin(__builtin_ctzl)
|
||||
# ifdef DEBUG_BITS
|
||||
@@ -131,6 +176,33 @@ static inline uint ffs64(u64 n)
|
||||
# endif
|
||||
}
|
||||
|
||||
static inline uint ffs32(u32 n)
|
||||
{
|
||||
# if __has_builtin(__builtin_ffs)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin ffs.\n");
|
||||
# endif
|
||||
return __builtin_ffs(n);
|
||||
|
||||
# elif __has_builtin(__builtin_ctz)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin ctz.\n");
|
||||
# endif
|
||||
if (n == 0)
|
||||
return (0);
|
||||
return __builtin_ctz(n) + 1;
|
||||
|
||||
# else
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "emulated.\n");
|
||||
# endif
|
||||
return popcount32(n ^ ~-n);
|
||||
# endif
|
||||
}
|
||||
|
||||
/* count set bits: 10101000 -> 3
|
||||
* ^ ^ ^
|
||||
*/
|
||||
static inline int popcount64(u64 n)
|
||||
{
|
||||
# if __has_builtin(__builtin_popcountl)
|
||||
@@ -152,10 +224,31 @@ static inline int popcount64(u64 n)
|
||||
# endif
|
||||
}
|
||||
|
||||
/** bit_for_each64 - iterate over an u64 bits
|
||||
static inline int popcount32(u32 n)
|
||||
{
|
||||
# if __has_builtin(__builtin_popcount)
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "builtin.\n");
|
||||
# endif
|
||||
return __builtin_popcount(n);
|
||||
|
||||
# else
|
||||
# ifdef DEBUG_BITS
|
||||
log_f(1, "emulated.\n");
|
||||
# endif
|
||||
int count = 0;
|
||||
while (n) {
|
||||
count++;
|
||||
n &= (n - 1);
|
||||
}
|
||||
return count;
|
||||
# endif
|
||||
}
|
||||
|
||||
/** bit_for_each - iterate over an u64/u32 bits
|
||||
* @pos: an int used as current bit
|
||||
* @tmp: a temp u64 used as temporary storage
|
||||
* @ul: the u64 to loop over
|
||||
* @tmp: a temp u64/u32 used as temporary storage
|
||||
* @ul: the u64/u32 to loop over
|
||||
*
|
||||
* Usage:
|
||||
* u64 u=139, _t; // u=b10001011
|
||||
@@ -170,9 +263,15 @@ static inline int popcount64(u64 n)
|
||||
#define bit_for_each64(pos, tmp, ul) \
|
||||
for (tmp = ul, pos = ffs64(tmp); tmp; tmp &= (tmp - 1), pos = ffs64(tmp))
|
||||
|
||||
#define bit_for_each32(pos, tmp, ul) \
|
||||
for (tmp = ul, pos = ffs32(tmp); tmp; tmp &= (tmp - 1), pos = ffs32(tmp))
|
||||
|
||||
/** or would it be more useful (counting bits from zero instead of 1) ?
|
||||
*/
|
||||
#define bit_for_each64_2(pos, tmp, ul) \
|
||||
for (tmp = ul, pos = ctz64(tmp); tmp; tmp ^= 1UL << pos, pos = ctz64(tmp))
|
||||
|
||||
#define bit_for_each32_2(pos, tmp, ul) \
|
||||
for (tmp = ul, pos = ctz32(tmp); tmp; tmp ^= 1U << pos, pos = ctz32(tmp))
|
||||
|
||||
#endif /* BITS_H */
|
||||
|
||||
Reference in New Issue
Block a user