# 2023q1 Homework3 (quiz3)
contributed by <terry23304>
## 測驗 1
```c
typedef struct __node {
uintptr_t color;
struct __node *left, *right;
struct __node *next;
long value;
} node_t __attribute__((aligned(sizeof(long))));
```
透過 ` __attribute__((aligned(sizeof(long))))` 把 `struct __node` 對齊 `sizeof(long)`
```c
typedef enum { CMAP_RED = 0, CMAP_BLACK } color_t;
#define rb_parent(r) ((node_t *) ((r)->__rb_parent_color & ~(sizeof(long)-1)))
#define rb_color(r) ((color_t) (r)->color & 1)
```
捨棄最低位的 $log(sizeof(long))$ 個位元,將結果轉為 pointer to node_t 就可以得到 parent 的位址
```c
#define rb_set_red(r) \
do { \
r->color &= 0; \
} while (0)
#define rb_set_black(r) \
do { \
r->color |= 1; \
} while (0)
```
最低位元儲存顏色,若為 0 是紅色, 1 則是黑色
`cmap_insert`
比較要插入的節點與目前節點的大小,較小往左走,反之往右。
```c
for (node_t *cur = obj->head;;) {
int res = obj->comparator(&node->value, &cur->value);
if (!res) /* If the key matches something else, don't insert */
assert(0 && "not support repetitive value");
if (res < 0) { // 往左 traverse
if (!cur->left) { // 無左子則插入
cur->left = node;
rb_set_parent(node, cur);
cmap_fix_colors(obj, node);
break;
}
cur = cur->left;
} else { // 往右 traverse
if (!cur->right) { // 無右子則插入
cur->right = node;
rb_set_parent(node, cur);
cmap_fix_colors(obj, node);
break;
}
cur = cur->right;
}
}
```
插入節點,並將節點依照順序串起來,最後把末端的 `next` 指向 `NULL`
```c
void tree_sort(node_t **list)
{
node_t **record = list;
cmap_t map = cmap_new(sizeof(long), sizeof(NULL), cmap_cmp_int);
while (*list) {
cmap_insert(map, *list, NULL);
list = &(*list)->next;
}
node_t *node = cmap_first(map), *first = node;
for (; node; node = cmap_next(node)) {
*list = node;
list = &(*list)->next;
}
*list = NULL;
*record = first;
free(map);
}
```
## 測驗 2
parent_balance: combination of @parent and @balance (lowest two bits)
```c
struct avl_node {
unsigned long parent_balance;
struct avl_node *left, *right;
} AVL_NODE_ALIGNED;
```
parent_balance 最後兩 bit 儲存 balance 資訊,去除最後 3 bit 即可得到 parent address
```c
static inline struct avl_node *avl_parent(struct avl_node *node)
{
return (struct avl_node *) (node->parent_balance & ~3);
}
```
要取得 balance 狀態則取得 `node->parent_balance` 最後兩 bit 即可
```c
static inline enum avl_node_balance avl_balance(const struct avl_node *node)
{
return (enum avl_node_balance)(node->parent_balance & 3);
}
```
設定 node 只需把 address 與 balance 狀態結合
```c
static void avl_set_parent(struct avl_node *node, struct avl_node *parent)
{
node->parent_balance =
(unsigned long) parent | parent->parent_balance & 3;
}
static void avl_set_balance(struct avl_node *node,
enum avl_node_balance balance)
{
node->parent_balance = (unsigned long) node | balance;
}
```
插入新節點後確認是否還平衡,先檢查插入節點是左子還是右子,再檢查 parent 的 balance 狀態在插入新節點後是否平衡,若新結點為右子,且 parent 平衡狀態為 `AVL_RIGHT` 則需要旋轉來調整平衡,若為 `AVL_NEUTRAL` 則把 parent balance 狀態改為 `AVL_RIGHT` 即可,而若是 `AVL_LEFT` 在右子插入一節點後 parent 左右子樹高度會相等,把平衡狀態改為 `AVL_NEUTRAL`
```c
void avl_insert_balance(struct avl_node *node, struct avl_root *root)
{
struct avl_node *parent;
/* go tree upwards and fix the nodes on the way */
while ((parent = avl_parent(node))) {
if (avl_is_right_child(node)) {
switch (avl_balance(parent)) {
default:
case AVL_RIGHT:
/* compensate double right balance by rotation
* and stop afterwards
*/
switch (avl_balance(node)) {
default:
case AVL_RIGHT:
case AVL_NEUTRAL:
avl_rotate_left(node, parent, root);
break;
case AVL_LEFT:
avl_rotate_rightleft(node, parent, root);
break;
}
parent = NULL;
break;
case AVL_NEUTRAL:
/* mark balance as right and continue upwards */
avl_set_balance(parent, AVL_RIGHT);
break;
case AVL_LEFT:
/* new right child + left leaning == balanced
* nothing to propagate upwards after that
*/
avl_set_balance(parent, AVL_NEUTRAL);
parent = NULL;
break;
}
} else {
switch (avl_balance(parent)) {
default:
case AVL_RIGHT:
/* new left child + right leaning == balanced
* nothing to propagate upwards after that
*/
avl_set_balance(parent, AVL_NEUTRAL);
parent = NULL;
break;
case AVL_NEUTRAL:
/* mark balance as left and continue upwards */
avl_set_balance(parent, AVL_LEFT);
break;
case AVL_LEFT:
/* compensate double left balance by rotation
* and stop afterwards
*/
switch (avl_balance(node)) {
default:
case AVL_LEFT:
case AVL_NEUTRAL:
avl_rotate_right(node, parent, root);
break;
case AVL_RIGHT:
avl_rotate_leftright(node, parent, root);
break;
}
parent = NULL;
break;
}
}
if (!parent)
break;
node = parent;
}
}
```
## 測驗 3
`log_table_256[x]` 值為 $\left \lfloor{log_2 x}\right \rfloor$, `_(n)` 展開後會是 16 個 n
```c
static const char log_table_256[256] = {
#define _(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3,
3, 3, 3, 3, 3, _(4), _(5), _(5), _(6), _(6), _(6),
_(6), _(7), _(7), _(7), _(7), _(7), _(7), _(7), _(7),
#undef _
};
```
輸入為 64 位元,而查表只能查到 256 個 log 值,將 64 位元切成八個區塊與測驗四一樣使用 binary search 來找到 v 的大小並補上 log 值
```c
uint64_t log2_64(uint64_t v)
{
unsigned r;
uint64_t t, tt, ttt;
ttt = v >> 32;
if (ttt) {
tt = ttt >> 16;
if (tt) {
t = tt >> 8;
if (t) {
// 32 + 16 + 8 = 56
r = 56 + log_table_256[t];
} else {
// 32 + 16 = 48
r = 48 + log_table_256[tt];
}
} else {
t = ttt >> 8;
if (t) {
// 32 + 8 = 40
r = 40 + log_table_256[t];
} else {
r = 32 + log_table_256[ttt];
}
}
} else {
tt = v >> 16;
if (tt) {
t = tt >> 8;
if (t) {
// 16 + 8 = 24
r = 24 + log_table_256[t];
} else {
r = 16 + log_table_256[tt];
}
} else {
t = v >> 8;
if (t) {
r = 8 + log_table_256[t];
} else {
r = 0 + log_table_256[v];
}
}
}
return r;
}
```
```c
unsigned int bucket_number(uint64_t x)
{
uint64_t mask111 = (1 << (N_BITS + 1)) - 1;
uint64_t mask011 = (1 << (N_BITS)) - 1; /* one 1 less */
unsigned char leq = ((x & mask111) < N_BUCKETS);
/* leq (less or equal) is 0 or 1. */
return (leq * (x & mask111)) +
((1 - leq) * ((x >> (N_BITS + 1)) & mask011));
/* 'x >> (N_BITS + 1)' : take different set of bits -> better uniformity.
* '... & mask011' guarantees that the result is less or equal N_BUCKETS.
*/
}
```
## 測驗 4
輸入為 32 位元的 unsigned int
利用 binary search 概念找出 leading 1 在哪一區塊
若 `x > 0xFFFF` 則 `x` 右移 16 bit 留下 x 左半部分的位元
```c
r = (x > 0xFFFF) << 4;
x >>= r;
```
若剩下的 16 bit 左邊 8 bit 不為零則再捨棄 x 右半邊,留下左半部分的位元
```c
shift = (x > 0xFF) << 3;
x >>= shift;
r |= shift;
```
剩下 8 bit 左邊 4 bit 不為零則捨棄 x 右 4 bit 的位元
```c
shift = (x > 0xF) << 2;
x >>= shift;
r |= shift;
```
檢查最後 4 bit leading 1 在前兩 bit 還是後兩 bit
```C
shift = (x > 0x3) << 1;
x >>= shift;
```
總共 32 bit,取 log 後可用 5 bit 表示,左 3 bit 由 `r` 紀錄,第二位元由 `shift` 決定, 因最後的 `x` 只會剩下兩 bit , lsb 由 `x>>1`來得知剩下兩 bit leading 1 位置。
`r | shift | x >> 1) + 1`
+1 為補上一開始的 -1 來達到上高斯的效果