---
# System prepended metadata

title: 2024 Homework4 (quiz3+4)
tags: [linux kernel]

---

# 2024 Homework4 (quiz3+4)
## 第三周測驗
[2024q1 第 3 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz3)
### 測驗 `1` : Using bitwise operations to compute square root
先看原程式碼
```c
#include <math.h>
int i_sqrt(int N)
{
    int msb = (int) log2(N);
    int a = 1 << msb;
    int result = 0;
    while (a != 0) {
        if ((result + a) * (result + a) <= N)
            result += a;
        a >>= 1;
    }
    return result;
}
```
編譯方式 
```
$ gcc -o i_sqrt i_sqrt.c -lm
```
> 因為呼叫 log2，所以需要連結 libm (math library)，即 -lm 選項


假設 N = $36_{10}$ =$100100_2$，`msb = 5` => a = $100000_2$
接著進入迴圈中

| 迴圈次數 | a | result |是否進入迴圈|
| -------- | -------- | -------- |- |
| 1     | $100000_2$     | 0     |y |
| 2     | $10000_2$     | 0     |y |
| 3     | $1000_2$     | 0     |y |
| 4     | $100_2$     | $100_2$     |y |
| 5     | $10_2$     | $110_2$     |y |
| 6     | $1_2$     | 0     |n |

在此 `bitwise` 操作中， `(result + a)` 的平方逐一比較 `N` 值，當值較小時就疊加當前的值，即 `result += a` ，直到為 N 的開方根值。
> 此題一開始用十進位去看比較容易明白， `a >>= 1` 看作 `a / 2`。
編譯方式：`$ gcc -o i_sqrt i_sqrt.c -lm`

版本二使用右移操作直到 n 值為 0 ，藉此計算 `msb`
```clike
int msb = 0;
int n = N;
while (n > 1) {
    n >>= 1;
    msb++;
}
```
版本三使用 [Digit-by-digit calculation](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Digit-by-digit_calculation) 方法進行開方根。將平方數拆成 2 的冪相加
令 $x=N^2=(a_n+a_{n-1}+...+a_0)^2$ ，其中 N 即為所求的開方根

$P_m=a_n+a_{n-1}+...+a_m$
$P_0=a_n+a_{n-1}+...+a_0=N$ 即為所求

推論出:
$P_m=P_{m+1}+a_m$

後續將求出所有 $a_m=2^m$ or $0$，從 $m = n$ 一直到 $m = 0$，而求得 $a_m$ 只須比較$P_m^2 \le N^2$，有兩種情況：
\begin{cases}
P_m=P_{m+1}+2^m \ \ ;\ P_m^2 \le N^2\\
P_m=P_{m+1}\ \ \ \ \ \ \ \ \ \ \ \ ;\ otherwise
\end{cases}
由於每輪逐一比較的運算成本過高，提出了另個想法
將$N^2-P_m^2$ 的值表示位$X_m$

\begin{cases}
X_m=N^2-P_m^2--(1) \\
X_{m+1}=N^2-P_{m+1}^2--(2)
\end{cases}
將(1)式減(2)式可得 $X_m-X_{m+1}=-P_m^2+P_{m+1}^2--(3)$

其中 $P_m$ 平方為

$P_m^2=P_{m+1}^2+2a_mP_{m+1}+a_m^2=P_{m+1}^2+(2P_{m+1}+a_m)a_m$

令 $Y_m= P_m^2-P_{m+1}^2=(2P_{m+1}+a_m)a_m$

最後將 $Y_m$ 代回到(3)式可得：

$X_m=X_{m+1}-Y_m$

將 $Y_m$ 拆解 $c_m,d_m$

$c_m=P_{m+1}2^{m+1}$
$d_m=(2^m)^2$

得：
\begin{cases}
Y_m=c_m+d_m\ ;if\ a_m=2^m\\
Y_m=0\ \ \ \ \ \ \ \ \ \ \ \ \ \ ;if\ a_m=0
\end{cases}
藉由位元運算從$c_m,d_m$推出下一輪

$c_{m-1}=P_m2^m=(P_{m+1}+a_m)2^m=P_{m+1}2^m+a_m2^m$
\begin{cases}
c_m/2+d_m\ \ if\ a_m=2^m\\
c_m/2\ \ \ \ \ \ \ \ \ \ \ \ if\ a_m=0\\
\end{cases}
$d_{m-1}=d_m/4$

**Linux 核心原始程式碼** [linux/block/blk-wbt.c](https://github.com/torvalds/linux/blob/39cd87c4eb2b893354f3b850f916353f2658ae6f/block/blk-wbt.c#L9)
```c
static void rwb_arm_timer(struct rq_wb *rwb)
{
	struct rq_depth *rqd = &rwb->rq_depth;

	if (rqd->scale_step > 0) {
		/*
		 * We should speed this up, using some variant of a fast
		 * integer inverse square root calculation. Since we only do
		 * this for every window expiration, it's not a huge deal,
		 * though.
		 */
		rwb->cur_win_nsec = div_u64(rwb->win_nsec << 4,
					int_sqrt((rqd->scale_step + 1) << 8));
	} else {
		/*
		 * For step < 0, we don't want to increase/decrease the
		 * window size.
		 */
		rwb->cur_win_nsec = rwb->win_nsec;
	}

	blk_stat_activate_nsecs(rwb->cb, rwb->cur_win_nsec);
}
```
尚未完成
### 測驗 `2` : Using bitwise operations to perform mod 10 and div 10
參考《[Hacker's Delight](https://web.archive.org/web/20180517023231/http://www.hackersdelight.org/divcMore.pdf)》，採用 bitwise operation 來實作除法

$n\ \dfrac{a}{2^N}$ 此題選用 a=13 N=7 => $n\ \dfrac{13}{2^7}$ ，其中 $\dfrac{13}{2^7}$ 為 $0.10156$ 。

完整程式碼
```c
unsigned d2, d1, d0, q, r;

d0 = q & 0b1;
d1 = q & 0b11;
d2 = q & 0b111;
q = ((((tmp >> 3) + (tmp >> 1) + tmp) << 3) + d0 + d1 + d2) >> 7;
r = tmp - (((q << 2) + q) << 1);
```
將 $tmp\times \dfrac{13}{2^7}$ 改寫成 $tmp\times 13 \div 16 \div 8$，其中 $tmp\times \dfrac{13}{8}\times 8=13tmp$ 可從下方程式碼得到
```c
(((tmp >> 3) + (tmp >> 1) + tmp) << 3)
```
由於 `tmp` 右移三位會遺失原本最低三位的 bit ，因此將分別用 `d0` 、 `d1` 、 `d2` 來儲存原先的 bit ，再將遺失的 bit 補回來，最後在除以128，即為 $\dfrac{13}{128} tmp$，得到一個近似 $tmp\div 10$ 的結果 ( `q` )。
```c
q = ((((tmp >> 3) + (tmp >> 1) + tmp) << 3) + d0 + d1 + d2) >> 7;
```
:::info
上述程式碼中不太理解為何補足遺失的最低三位 bit 是加上 `+ d0 + d1 + d2` ，在我實作中發現就算缺少這段程式碼也不會影響其結果，其大致的觀念我能理解是為了增加其精度，但又為何不直接加上 `+ d2` 就好。
:::
下方程式碼根據餘數定理：$f-g\times Q=r$，可以得到 $r$ (餘數)
- $f$ : 被除式
- $g$ : 除式
- $Q$ : 商
- $r$ : 餘數
```c
r = tmp - (((q << 2) + q) << 1);
```
其中`(((q << 2) + q) << 1)` 等於 $(q\times 4+q)\times2=q\times 10$

將程式碼包裝成
```c
#include <stdint.h>
void divmod_10(uint32_t in, uint32_t *div, uint32_t *mod)
{
    uint32_t x = (in | 1) - (in >> 2); /* div = in/10 ==> div = 0.75*in/8 */
    uint32_t q = (x >> 4) + x;
    x = q;
    q = (q >> 8) + x;
    q = (q >> 8) + x;
    q = (q >> 8) + x;
    q = (q >> 8) + x;

    *div = (q >> 3);
    *mod = in - ((q & ~0x7) + (*div << 1));   
}
```
`uint32_t x = (in | 1) - (in >> 2)` 得到 $\dfrac{3}{4}in$ 
`uint32_t q = (x >> 4) + x;` 得到 $q=\dfrac{3}{4}in\div 16+\dfrac{3}{4}in=\dfrac{51}{64}in$ ，其中 $\dfrac{51}{64}in$約為 $0.796875in$ 近似 $\dfrac{8}{10}$

再經由 bitwise 操作使 0.796875 更接近 0.8
```c
    q = (q >> 8) + x; // q = 0.799987793
    q = (q >> 8) + x; // q = 0.799999952
    q = (q >> 8) + x; // q = 0.799999998
    q = (q >> 8) + x; // q = 0.8
```
最後 `*div = (q >> 3);` 即為 $*div=\dfrac{8}{10}in\div 8=\dfrac{1}{10}in$
`((q & ~0x7) + (*div << 1));` 為 $\dfrac{8}{10}in+\dfrac{1}{10}in\times 2=\dfrac{in}{10}\times10$，其中 $\dfrac{in}{10}$ 為商。

在由餘式定理：$f-g\times Q=r$ 求得 `*mod` 值為何

**練習撰寫不依賴任何除法指令的 % 9 (modulo 9) 和 % 5 (modulo 5) 程式碼**

尚未進行
### 測驗 `3` : Using bitwise operations to calculate the logarithm base 2
用 `ilog2` 計算以 2 為底的對數，且其輸入和輸出皆為整數。

稍微改寫一下原程式碼方便識讀，舉例 $i = 4_{10} = 100_2$
| while ( i >= 2 ) | 原 i | 後 i | log |
| -------- | -------- | -------- | - |
| 第 1 次迴圈     | 100     | 10     | 1 |
| 第 2 次迴圈     | 10     | 1     | 2 |

輸出 `log = 2`

```diff
int ilog2(int i)
{
-   int log = -1;
+   int log = 0;
-   while (i){
+   while (i >= 2) {
        i >>= 1;
        log++;
    }
    return log;
}
```

改寫後的程式碼，將 `i` 用一定數量的位元去進行判斷，有效提升數字較大時的計算速度，其原因為原程式碼每個位元逐一去判斷，反覆進行多次的迴圈，改後後判斷分成 4 個階段，若 `i` 大於 $2^{16}、2^8、2^4、2^1$則直接進行位元操作。
```c
static size_t ilog2(size_t i)
{
    size_t result = 0;
    while (i >= 65536) {
        result += 16;
        i >>= 16;
    }
    while (i >= 256) {
        result += 8;
        i >>= 8;
    }
    while (i >= 16) {
        result += 4;
        i >>= 4;
    }
    while (i >= 2) {
        result += 1;
        i >>= 1;
    }
    return result;
}
```
**在 Linux 核心原始程式碼找出 log2 的相關程式碼 (或類似的形式)，並解說應用案例**

看到 log2 為底的形式就想到 `clz` 、 `ffs` 、 `fls` 等相關的操作都有類似的手法。

在 [linux/arch/arc/include/asm/bitops.h](https://github.com/torvalds/linux/blob/026e680b0a08a62b1d948e5a8ca78700bfac0e6e/arch/arc/include/asm/bitops.h#L28) 中找到 fls 的實作，其作用為返回最高有效 bit 位(由最低位往左數)，當判斷式為 0 進行位移操作，其手法相似 log2 ，不同於位元操作向左進行。

該順序不同於 `log2` ， `constant_fls` 從 1 起始，當沒有有效位返回 0 。
```c
static inline int constant_fls(unsigned int x)
{
	int r = 32;

	if (!x)
		return 0;
	if (!(x & 0xffff0000u)) {
		x <<= 16;
		r -= 16;
	}
	if (!(x & 0xff000000u)) {
		x <<= 8;
		r -= 8;
	}
	if (!(x & 0xf0000000u)) {
		x <<= 4;
		r -= 4;
	}
	if (!(x & 0xc0000000u)) {
		x <<= 2;
		r -= 2;
	}
	if (!(x & 0x80000000u))
		r -= 1;
	return r;
}
```
### 測驗 `4` : Using bitwise to calculate EWMA
> 參考 [stevendd543](https://hackmd.io/@stevennnnnn/linux2024-homework4#%E7%AC%AC%E5%9B%9B%E9%A1%8C)、[2024q1 第 3 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz3#%E6%B8%AC%E9%A9%97-4)


**Exponentially Weighted Moving Average**
賦予資料指數衰減的權重，主要目的使越舊的資料權重越低，反之越新的資料對整體平均值的影響越大，以下為此統計手法的公式：
$$
S_t = 
\begin{cases}
Y_0 \quad\quad &t=0
\\
\alpha Y_t + (1-\alpha) \cdot S_{t-1} \quad\quad &t>0
\end{cases}
$$
-  $\alpha$ 表示歷史資料加權降低的程度，介在 0 ～ 1 之間，越高的 $\alpha$ 會使歷史資料減少的越快
-  $Y_t$表示時間 t 時的資料點
-  $S_{t}$ 表示時間 t 時的 EWMA

```c
void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight)
{
    if (!is_power_of_2(weight) || !is_power_of_2(factor))
        assert(0 && "weight and factor have to be a power of two!");

    avg->weight = ilog2(weight);
    avg->factor = ilog2(factor);
    avg->internal = 0;
}
```
`@internal` : 看作 $t$ 當前的位置
`@factor` : 縮放因子，必須是 2 的冪次
`@weight` : 資料的權重，在這 $\alpha=2^{avg->weight}$ 透過右移來達到$\alpha=2^{-(avg->weight)}=\dfrac{1}{2^{avg->weight}}$，必須是 2 的冪次

```c
struct ewma *ewma_add(struct ewma *avg, unsigned long val)
{
    avg->internal = avg->internal
                        ? (((avg->internal << weight) - avg->internal) +
                           (val << avg->factor)) >> avg->weight
                        : (val << avg->factor);
    return avg;
}
```
一開始 $t=0$ ，因此 $S_t=Y_0$ 對應到 `avg->internal = (val << avg->factor);`
，接著 `avg->internal = (((avg->internal << avg->weight) - avg->internal) + (val << avg->factor)) >> avg->weight` 的理解較為困難，看起來與題意的公式不同，在 [stevendd543](https://hackmd.io/@stevennnnnn/linux2024-homework4#%E7%AC%AC%E5%9B%9B%E9%A1%8C) 筆記中的舉例有非常清楚解釋：

令 $\alpha\alpha^{-1}=1$，將公式改寫成

$\alpha^{-1}S_t=\alpha^{-1}(\alpha Y_t+(1-\alpha)S_{t-1})$
$\ \ \ \ \ \ \ \ \ \ =Y_t+(\alpha^{-1}-1)S_{t-1}$

其中 $(\alpha^{-1}-1)S_{t-1}$ 等效於 `((avg->internal << avg->weight) - avg->internal)`，$Y_t$ 等效於 `(val << avg->factor)` ，最後將整個公式 `>> avg->weight` 即為 $\alpha^{-1}S_t\times \alpha=S_t$ (當時 $t$ 的EWMA)。
```c
unsigned long ewma_read(const struct ewma *avg)
{
    return avg->internal >> avg->factor;
}
```
將計算的平均值除以縮放因子而得到真正的平均值。

**在 Linux 核心原始程式碼找出 EWMA 的相關程式碼 (或類似的形式)，至少該涵蓋無線網路裝置驅動程式**

### 測驗 `5` : Test 3 Extension
延伸測驗 3，已知輸入必為大於 0 的數值 (即 x > 0)，以下程式碼可計算 [log2] ，也就是 [ceil](https://man7.org/linux/man-pages/man3/ceil.3.html) 和 log2 的組合並轉型為整數。

`x--` 目的為避免 `x` 為 2 的冪，接著使用類似測驗 `3` 的作法，在指定的位元寬度中比較，若條件成立就會進行位元操作，下方程式碼寫的更加複雜因此實際帶入數值去進一步解釋。
```c
int ceil_ilog2(uint32_t x)
{
    uint32_t r, shift;

    x--;
    r = (x > 0xFFFF) << 4;                                                   
    x >>= r;
    shift = (x > 0xFF) << 3;
    x >>= shift;
    r |= shift;
    shift = (x > 0xF) << 2;
    x >>= shift;
    r |= shift;
    shift = (x > 0x3) << 1;
    x >>= shift;
    return (r | shift | x > 1) + 1;       
}
```
case `1`
x = 0000 0000 0000 0000 0000 1000 0000 0000

```
x-- = 0000 0000 0000 0000 0000 0111 1111 1111
r = (x > 0xFFFF) << 4 = 0 << 4 = 0
x = x >> r = 0000 0000 0000 0000 0000 0111 1111 1111
shift = (x > 0xFF) << 3 = 1 << 3 = 8
x >>= shift = 0000 0000 0000 0000 0000 0000 0000 0111
r |= shift = 8
shift = (x > 0xF) << 2 = 0 << 2 = 0
x >>= shift = 0000 0000 0000 0000 0000 0000 0000 0111
r |= shift = 8
shift = (x > 0x3) << 1 = 1 << 1 = 2
x >>= shift = 0000 0000 0000 0000 0000 0000 0000 0001
return (r | shift | x > 1) + 1 = return (11)
```
- 透過 `shift = (x > 0xFF) << 3` 決定 `x` 要右移的大小
- `r |= shift` 等效 `r += shift` ，用來記錄由第 0 bit 往左數到 msb 的有效位 
- 最後結果把先前 `x--` 的值加回來

**改進程式碼，使其得以處理 x = 0 的狀況，並仍是 branchless**

```diff
int ceil_ilog2(uint32_t x)
{
    uint32_t r, shift;
+   bool mark = x;
+   x -= !!x 
-   x--;
    r = (x > 0xFFFF) << 4;                                                   
    x >>= r;
    shift = (x > 0xFF) << 3;
    x >>= shift;
    r |= shift;
    shift = (x > 0xF) << 2;
    x >>= shift;
    r |= shift;
    shift = (x > 0x3) << 1;
    x >>= shift;
-   return (r | shift | x > 1) + 1;  
+   return (r | shift | x > 1) + mark;
}
```
當 `x = 0` 時 `ceil_ilog2` 應要回傳 0 ，因此設立一個 `mark` 來紀錄最後是否 +1 。

**Linux 核心原始程式碼**
尚未完成

## 第四周測驗
[2024q1 第 4 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz4#%E6%B8%AC%E9%A9%97-1)
### 測驗 `1` : Total Hamming Distance
閱讀 [population count](https://en.wikichip.org/wiki/population_count#google_vignette) 了解計算二進位表示式中有多少個位元為 `1` 、 了解 [Hamming distance](https://en.wikipedia.org/wiki/Hamming_weight) 所代表的意思。

更進階的操作參照[測驗1](https://hackmd.io/@sysprog/linux2024-quiz4#%E6%B8%AC%E9%A9%97-1)，大致內容為每4個位元為一個單位，求出其中的 set bits 個數，32位元中總共有 8 組，將每組算得的 set bits 個數加總在一起，裡面的推導過程需詳讀就能得知最後為何 `v >> 24` 即為 popcount 。

考題針對 [LeetCode 477. Total Hamming Distance](https://leetcode.com/problems/total-hamming-distance/description/)
```c
int totalHammingDistance(int* nums, int numsSize)
{
    int total = 0;;
    for (int i = 0;i < numsSize;i++)
        for (int j = 0; j < numsSize;j++)
            total += __builtin_popcount(nums[i] ^ nums[j]); 
    return total >> 1;
}
```
Leetcode 範例描述到
```
Input: nums = [4,14,2]
The answer will be:
HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6.
```

在考題程式碼中為兩兩成對的去計算 popcount ，因此會出現HammingDistance(4, 14)與HammingDistance(14, 4)這種情況，因此最後回傳要除以 2 (即  `return total >> 1`)。

**不依賴 popcount，嘗試以上述歸納的規則，針對 [LeetCode 477. Total Hamming Distance](https://leetcode.com/problems/total-hamming-distance/description/) 撰寫出更高效的程式碼**

參考 solution 中的解答
```c
int totalHammingDistance(int* nums, int numsSize){
    int ans = 0;
    for (int i = 0; i < 32; ++i) {
        int c = 0;
        for (int j = 0; j < numsSize; ++j) {
            c += (nums[j] >> i) & 1;
        }
        ans += c * (numsSize - c);
    }
    return ans;
}
```
使用 bitwise 逐一比較二進制中每個位元是否為 1 ，其判斷式 `(nums[j] >> i) & 1` 
每當比較完一位元，便計算其 HammingDistance 的值 ( `ans += c * (numsSize - c)` )


| n 'th bit | 3 | 2 | 1 | 0 |
| -------- | -------- | -------- | - | - |
| input  4    | 0     | 1     | 0 | 0 |
| input  14    |1     | 1     | 1 | 0 |
| input  2    | 0     | 0     | 1 | 0 |

1. 第 0 個 bit 觀察:
全部都是 0 => Hamming Distance = 0
2. 第 1 個 bit 觀察:
1 有兩個 => Hamming Distance = (2)* (numsSize - 2)
3. 第 3 個 bit 觀察:
1 有一個 => Hamming Distance = (1)* (numsSize - 1)

### 測驗`2` : Remainder by Summing digits
了解[ [ 離散數學 ] 同餘（Mod）](https://ithelp.ithome.com.tw/articles/10205727)的性質及定義
詳讀 [Hacker's Delight](https://web.archive.org/web/20180517023231/http://www.hackersdelight.org/divcMore.pdf) 中提及 Remainder by Summing digits

以除 3 為例， $1\equiv 1\pmod 3$ , $2\equiv -1\pmod 3$

$2^k\equiv$
\begin{cases}
  1\equiv 1\pmod 3, & \text{if $k$ is even} \\
  2\equiv -1\pmod 3, & \text{if $k$ is odd}
\end{cases}
n 以二進制表示成 $n=b_{n-1}b_{n-2}...b_1b_0$
\begin{aligned}
  n &= {\sum\limits_{i=0}^{n-1}} b_i2^{i} 由以上推論出\  n \equiv {\sum\limits_{i=0}^{n-1}} b_i(-1)^i\pmod 3
\end{aligned}
將上式結合 popcount 寫成程式
```c
n = popcount(n & 0x55555555) - popcount(n & 0xAAAAAAAA)
```
> 0x55 = 0b01010101
> 0xAA = 0b10101010

再由定理
$popcount(x \And \overline{m}) - popcount(x \And m) = popcount(x \bigoplus m) - popcount(m)$
> 定理證明在 [Hacker's Delight](https://web.archive.org/web/20180517023231/http://www.hackersdelight.org/divcMore.pdf) 裡有提到，頁碼：P13 

化簡成
```c
n = popcount(n ^ 0xAAAAAAAA) - 16
```
```c
int mod3(unsigned n)
{
    n = popcount(n ^ 0xAAAAAAAA) + 23; //n = popcount(n ^ 0xAAAAAAAA) - 16 + 39;
    n = popcount(n ^ 0x2A) - 3;
    return n + ((n >> 31) & 3);        //避免 n 為負數值
}
```
為了避免 `n` 變成負數，因此加上一個足夠大的 3 的倍數，範例中為加 39 ，在第二輪應用使 `n` 介於 -3~2 之間而非 -3~3 之間。
`return n + ((n >> 31) & 3)`主要用作避免 n 為負數值，若為 n 為正則不受影響。
```
舉例 若 n = -1
-1 -> 0b1111 1111 1111 1111 1111 1111 1111 1111  # n
0b11                                             # (n >> 31) & 3
0b10                                             # n + ((n >> 31) & 3)
```
lookup table
```c
int mod3(unsigned n)
{
    static char table[33] = {2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1 };
    n = popcount(n ^ 0xAAAAAAAA);
    return table[n];
}
```
使用查表的方式去尋找對應的餘數。
**tictactoe.c**

**將上述手法應用於第三次作業中，追求更有效的賽局判定機制。**
### 測驗 `3` : XTree
[AVL Tree](https://josephjsf2.github.io/data/structure/and/algorithm/2019/06/22/avl-tree.html) 是一種Binary Search Tree (BST) 實作方法，透過高度計算及 balance factor 判斷是否需要旋轉來平衡。

[red-black tree](https://clu.gitbook.io/data-structure-note/1.4.3-red-black-tree) 遵循以下規則來達成平衡：
1. 節點只有黑或紅
2. 根節點必為黑
3. 若節點是紅色的，則其子節點必為黑色，反之不必為真(黑色節點的下個節點可為黑或紅)
4. 所有從該節點走到其子樹下的末端節點 (leaf) 的上之黑色節點數量必定

此題 XTree 的平衡機制與 AVL tree 相似，利用 hint 來評估是否需要做 rotate，然而 xt_node 的 hint 的計算方式是，其左右節點 hint 最大值 +1 並且只有在被呼叫到 xt_update 時才會被更新。

```c
#define xt_root(r) (r->root)
#define xt_left(n) (n->left)
#define xt_right(n) (n->right)
#define xt_rparent(n) (xt_right(n)->parent)
#define xt_lparent(n) (xt_left(n)->parent)
#define xt_parent(n) (n->parent)
```
**`xt_create`** : 初始化 `XTree`
**`xt_destroy`**

利用遞迴
```c
    if (xt_root(tree))
        __xt_destroy(tree, xt_root(tree));
```
將樹裡的每個節點釋放，在 `__xt_destroy()` 中
```c
    if (xt_left(n))
        __xt_destroy(tree, xt_left(n));

    if (xt_right(n))
        __xt_destroy(tree, xt_right(n));
```
若 `xt_left` (左子樹)或 `xt_right` (右子樹)存在則繼續往下走直到盡頭 `tree->destroy_node(n)` 來釋放。

**`xt_first`**

用遞迴的方式尋找最左邊的節點
```c
    if (!xt_left(n))
        return n;

    return xt_first(xt_left(n));
```
**`xt_last`**

與 `xt_first` 一樣，但變成尋找最右變的節點

**`xt_rotate_left`**
```c
    struct xt_node *l = xt_left(n), *p = xt_parent(n);

    xt_parent(l) = xt_parent(n);
    xt_left(n) = xt_right(l);
    xt_parent(n) = l;
    xt_right(l) = n;
```
這段程式碼為下圖操作
```graphviz
digraph Tree {
    graph[ordering=out]
    p -> n
    n -> l
    n -> r
    l -> A
    l -> B
    label=before
}
```
```graphviz
digraph Tree {
    graph[ordering=out]
    p -> l
    l -> A
    l -> n
    n -> B
    n -> r
    label=after
}
```
```c
    if (p && xt_left(p) == n)
        xt_left(p) = l;
    else if (p)
        xt_right(p) = l;
```
假設 p 的右子樹不存在
```graphviz
digraph Tree {
    graph[ordering=out]
    null [label="" shape=plaintext]
    null1 [label="" shape=plaintext]
    p -> l
    p -> null
    l -> null1
    l -> n
    label=before
}
```
```graphviz
digraph Tree {
    graph[ordering=out]
    null [label="" shape=plaintext]
    null1 [label="" shape=plaintext]
    p -> null
    p -> l
    l -> null1
    l -> n
    label=after
}
```
```c
    if (xt_left(n))
        xt_lparent(n) = n;
```
重新定義 n 節點的 parent

**`xt_rotate_right`**

操作手法與 `xt_rotate_left` 相似，因此僅用圖示
```graphviz
digraph Tree {
    graph[ordering=out]
    p -> n
    n -> l
    n -> r
    r -> A
    r -> B
    label=before
}
```
```graphviz
digraph Tree {
    graph[ordering=out]
    p -> r
    r -> n
    n -> l
    n -> A
    r -> B
    label=after
}
```
**`xt_balance`** 

與 AVL Tree 中 Balance Factor of T, BF(T) 的平衡判斷相似，這裡的 hint 為左子樹高 - 右子樹高

**`xt_max_hint`**

選擇左子樹與右子樹的 hint 的最大值 `+1` 即為節點 n 的 hint 值(更新 hint)

**`xt_update`**

若樹不平衡 `xt_balance < -1` 進行 `xt_rotate_right` , `xt_balance > 1` 進行 `xt_rotate_left`

**`xt_insert`**

插入新節點在樹中

**`xt_remove`**

當移除一節點 `del` 時，將該右子樹的最左來填補，即 `least` 來替換掉原本的 `del` ，並檢查是否平衡 `xt_update`，反之則用左子樹的最右節點來點補，若被刪除的節點為 `leaf` 將不用進行交換。
```c
static void __xt_remove(struct xt_node **root, struct xt_node *del)
{
    if (xt_right(del)) {
        struct xt_node *least = xt_first(xt_right(del));
        if (del == *root)
            *root = least;

        xt_replace_right(del, least);
        xt_update(root, xt_right(least));
        return;
    }

    if (xt_left(del)) {
        struct xt_node *most = xt_last(xt_left(del));
        if (del == *root)
            *root = most;

        xt_replace_left(del, most);
        xt_update(root, xt_left(most));
        return;
    }

    if (del == *root) {
        *root = 0;
        return;
    }

    /* empty node */
    struct xt_node *parent = xt_parent(del);

    if (xt_left(parent) == del)
        xt_left(parent) = 0;
    else
        xt_right(parent) = 0;

    xt_update(root, parent);
}
```