# 2023q1 Homework1 (quiz1)
contributed by < `SPFishcool` >
## 測驗一
### 運作原理
此為快速排序法,其程式碼分析如下:
- 程式開始時宣告兩個 `list_head` 為 `list_less` 與 `list_greater`,兩者為快速排序時依照 `pivot` 分類其他節點加到這兩條 linked list 上。
```c
//initial two heads
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
```
```c
//list_add "less" or "greater" by comparsion
if (cmpint(&itm->i, &pivot->i) < 0)
list_add (&itm->list, &list_less);
else
list_add (&itm->list, &list_greater);
```
- `pivot` 為 linked list 的第一個節點,取出後從原來的 list 上移除。
```c
struct item *pivot = list_first_entry(head, AAA, BBB);
list_del(&pivot->list);
```
- 分類完後會分成兩條分別以 `list_less` 與 `list_greater` 為 head 的 linked list,接者對兩條 list 再進行快速排序,排序完最後將依照 `list_less` -> `pivot` -> `list_greater` 將原本的 list 接在一起。
```c
//do sort
list_sort(&list_less);
list_sort(&list_greater);
// merge this node
list_add(&pivot->list, head);
list_splice(&list_less, head);
list_splice_tail(&list_greater, head);
```
### 改進方案
### hybrid sort
## 測驗二
### 運作原理
此為「非遞迴」Quick Sort,原理是運用堆疊儲存每一次 partition 後的狀態。
```c
struct list_head stack[MAX_DEPTH];
for (int i = 0; i < MAX_DEPTH; i++)
INIT_LIST_HEAD(&stack[i]);
```
一開始會將原始 linked list 存入堆疊最底下
```c
int top = -1;
list_splice_init(head, &stack[++top]);
```
用 `top` 來記錄目前堆疊高度,迴圈一開始會初始化 `partition` 並將目前 `top` 高度的 linked list 取出
```c
INIT_LIST_HEAD(&partition);
list_splice_init(&stack[top--], &partition);
```
並與遞迴一樣,初始化 `list_greater` 、 `list_less`,取第一個數值當作 `pivot`
```c
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
struct item *pivot =
list_first_entry(&partition, struct item, list);
list_del(&pivot->list);
INIT_LIST_HEAD(&pivot->list);
```
接下來將 `partition` 的節點依照大小分類至 `list_greater` 、 `list_less`
```c
GGGG (itm, is, &partition, list) {
list_del(&itm->list);
if (cmpint(&itm->i, &pivot->i) < 0)
list_move(&itm->list, &list_less);
else
list_move(&itm->list, &list_greater);
}
HHHH (&pivot->list, &list_less);
```
上列 `GGGG` 應該是要將每個節點的值取出,除了 container 和 list 還有兩個變數,所以 `GGGG` 應該填 `list_for_each_safe`
`HHHH` 函式內是一個 node 和一個 list,`list_less` 內的節點是比 `pivot` 還小的節點,因此如果要將 `pivot` 加入 `list_less` 應該加在後面,所以 `HHHH` 為 `list_move_tail`。
```c
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, IIII);
if (!list_empty(&list_less))
list_splice_tail(&list_less, JJJJ);
```
接下來上述 `IIII`、`JJJJ` 與 `stack` 有關,由此可猜測這兩行是要將 `list_greater` 、 `list_less` 放入 `stack`,目前 `top` 是記錄到存放 list 的最高層,所以應該往上存入,因此 `IIII` 和 `JJJJ` 應為 `&stack[++top]` (`list_splice_tail` 內部要放 pointer)。
### 效能比較
### 改進方案
### Intro Sort
## 測驗三
此為一個 XOR list 的實作程式碼,其 function 的實作原理與 linux 的 [list.h](https://github.com/SPFishcool/lab0-c/blob/master/list.h) 相似,只是其鏈結原理不一樣。
XOR list 只有一個 pointer,其 pointer 並不是真的指到一個 node 的所在位址,而是兩個 node 位址做 XOR 運算的結果,藉由 XOR 的特性(`a ^ (a ^ b)` = `b`),只要知道兩個相鄰的 node,就可以藉由 XOR 運算指到這條 linked list 的所有 node。
在測驗三的程式裡,`XOR_COMP` 就是將兩個 pointer 作 XOR 運算的 function。
```c
#define XOR_COMP(a, b) ((xor_node_t *) (LLLL))
```
要將 pointer 做運算,必須先將 type 轉變為 `uintptr_t`,所以 `LLLL` 應為 `(uintptr_t) a ^ (uintptr_t) b`。
接下來 XOR list 有三個 `struct`: `xor_node_t` 、 `xor_list_t` 、 `test_node`
- `_xorlist_node` 裡存的是 `*cmp`,就是前面提到的 pointer。
```graphviz
digraph list {
rankdir=LR;
node[shape=record, style=bold];
cmp [label="cmp"];
}
```
- `_xor_list_struct` 是整條 list 的 head,裡面存有 `count` 、`head` 、 `tail`,其中 `head` 和 `tail` 是分別指向 list 第一個和最後一個的 `_xorlist_node` 結構,並不是儲存 XOR 運算的結果。
```graphviz
digraph list {
rankdir=LR;
node[shape=record, style=bold];
subgraph cluster_0 {
count [label="{count}"];
head [label="{head}|{<cmp>cmp}"];
tail [label="{tail}|{<cmp>cmp}"];
label=xor_list_t
}
}
```
- `test_node` 則是 `_xorlist_node` 的 container
```graphviz
digraph list {
rankdir=LR;
node[shape=record, style=bold];
subgraph cluster_0 {
value [label="{value}"];
cmp [label="{xornode}|{<cmp>cmp}"];
label=test_node;
}
}
```
知道了 XOR list 的運算原理,最後來看 `xorlist_add`
```c=
int xorlist_add(xor_list_t *list, xor_node_t *n)
{
xor_node_t *real_next;
if (!n)
return ENOMEM;
xor_node_t *real_prev = &list->head;
xor_node_t *node = real_prev->cmp;
if (node == &list->tail)
real_next = MMMM;
else
real_next = node;
real_prev->cmp = n;
n->cmp = XOR_COMP(real_prev, real_next);
real_next->cmp = XOR_COMP(n, XOR_COMP(real_prev, PPPP));
list->count++;
return 0;
}
```
程式裡 `real_next` 從第 8 行可以知道它一直指向 head,而 node 指向 `head->cmp` 所指,也就是 list 的第一個元素,若 list 沒有任何元素 `head->cmp` 會指向 `tail`,而 `tail->cmp` 也會指向 `head`,因此 `MMMM` 為 list 沒有任何元素時,`real_next` 應指向 `&tail_next`
```graphviz
digraph list {
rankdir=LR;
node[shape=record, style=bold];
subgraph cluster_0 {
count [label="{count = 0}"];
{rank=same; count}
head [label="{head}|{<cmp>cmp}"];
{rank=same; head}
tail [label="{tail}|{<cmp>cmp}"];
{rank=same; tail}
label=list
}
head:cmp -> tail:w
tail:cmp -> head:e
}
```
接下來將 `n` 加入這個 list 的第一個位址,第 14、15 行先將 `real_prev->cmp` (`head` 的 `cmp`) 指向 `n`, `n->cmp` 設為 `head` $\oplus$ `prev_next`
第 16 行可以假設一下:
- 目前第一個元素(假設是 `a`)的 `cmp` 可能為 `head` $\oplus$ `b` ,今天我們要插入元素 `c`
- 已經把 `head->cmp = c` 、 `c->cmp = head ^ a`
- 最後要重設 `a->cmp`,原本的值會是 `head` $\oplus$ `b`,意思是 prev 為 `head`,next 為 `b`
- `c` 插進來後要將 `a->cmp` 改成 `c` $\oplus$ `b`,首先可以先 `real_prev` (`head`) $\oplus$ `a->cmp`,這樣做 `head` 就會消去只剩下 `b`
- 最後 `c` $\oplus$ `a->cmp` 就完成了
因此 `PPPP` 會是 `real_next->cmp`。
```graphviz
digraph list {
rankdir=LR;
node[shape=record, style=bold];
subgraph cluster_0 {
count [label="{count = 1}"];
{rank=same; count}
head [label="{head}|{<cmp>cmp}"];
{rank=same; head}
tail [label="{tail}|{<cmp>cmp}"];
{rank=same; tail}
label=list
}
cmp [label="{C}|{<cmp>A ⊕ B}"];
n[shape=plaintext]
node_[shape=plaintext]
real_prev[shape=plaintext]
real_next[shape=plaintext]
head:cmp -> cmp
tail:cmp -> cmp
real_prev -> head
node_ -> tail
real_next -> tail
n -> cmp
}
```