# 2023q1 Homework1 (quiz1)
[quiz1](https://hackmd.io/@sysprog/linux2023-quiz1)
## 測驗 1
### 解釋上述程式碼運作原理
初始狀態:
```graphviz
digraph {
rankdir=LR
head[shape=none]
head -> 4 -> 1 -> 3 -> 5 -> 2 -> 7
}
```
Step 0, `head` list 進入 `list_sort`,初始化 `list_less` 和 `list_greater`。
```c
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
```
```graphviz
digraph {
rankdir=LR
head, list_less, list_greater[shape=none]
head -> 4 -> 1 -> 3 -> 5 -> 2 -> 7
list_less
list_greater
}
```
Step 1, 從 `head` 移出第一個節點設為 `pivot`。
```c
struct item *pivot = list_first_entry(head, struct item, list);
list_del(&pivot->list);
```
```graphviz
digraph {
rankdir=LR
head, pivot, list_less, list_greater[shape=none]
head -> 1 -> 3 -> 5 -> 2 -> 7
pivot -> 4
list_less
list_greater
}
```
Step 2, 遍歷所有節點,並和 `pivot` 比較大小分別放到 `list_greater` 和 `list_less` list
```c
struct item *itm = NULL, *is = NULL;
list_for_each_entry_safe (itm, is, head, list) {
if (cmpint(&itm->i, &pivot->i) < 0)
list_move_tail (&itm->list, &list_less);
else
list_move_tail (&itm->list, &list_greater);
}
```
```graphviz
digraph {
rankdir=LR
head, pivot, list_less, list_greater[shape=none]
head
pivot -> 4
list_less -> 1 -> 3 -> 2
list_greater -> 5 -> 7
}
```
Step 3, 把 `list_greater` 和 `list_less` 作為 `list_sort` 的參數 recursive 執行 Step 0 至 2。返回的是已排序完成的 `list_greater` 和 `list_less` 。
```c
list_sort(&list_less);
list_sort(&list_greater);
```
```graphviz
digraph {
rankdir=LR
head, pivot, list_less, list_greater[shape=none]
head
pivot -> 4
list_less -> 1 -> 2 -> 3
list_greater -> 5 -> 7
}
```
Step 4, 把 `pivot` 接回 `head`;然後把 `list_less` 接回 `head` 的開頭;最後把 `list_greater` 接回 `head` 的尾部。排序完成。
```c
list_add(&pivot->list, head);
list_splice(&list_less, head);
list_splice_tail(&list_greater, head);
```
```graphviz
digraph {
rankdir=LR
head, pivot, list_less, list_greater[shape=none]
head -> 1 -> 2 -> 3 -> 4 -> 5 -> 7
pivot -> 4
list_less -> 1
list_greater -> 5
}
```
### 針對 Quick sort 提出上述程式碼的改進方案並實作
Quick sort 的 worst-case 是 $O(n^2)$。可以使用 Hybrid sort 採用截長補短的策略,融合兩項或更多排序演算法,這樣的案例包含 Timsort 和 Introsort。
## 測驗 2
### 解釋上方程式碼運作原理,指出設計和實作的缺失
程式碼用了 Divide and Conquer 策略進行排序;並且用一個 `stack` 陣列摸擬和取代了 recursive 的 function call stack。
初始狀態:
```graphviz
digraph {
rankdir=LR
head[shape=none]
head -> 4 -> 1 -> 3 -> 5 -> 2 -> 7
}
```
變數初始化:
```c
struct list_head stack[MAX_DEPTH];
for (int i = 0; i < MAX_DEPTH; i++)
INIT_LIST_HEAD(&stack[i]);
int top = -1;
list_splice_init(head, &stack[++top]);
struct list_head partition;
INIT_LIST_HEAD(&partition);
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
4 -> 1 -> 3 -> 5 -> 2 -> 7
stack:f0 -> 4
}
head[shape=none]
partition[shape=none]
top[shape=none label="top = 0"]
}
```
進入 while 迴圈,把 `stack` 把最後一個 list 指派給 `partition`:
```c
while (top >= 0) {
INIT_LIST_HEAD(&partition);
list_splice_init(&stack[top--], &partition);
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
}
head[shape=none]
partition[shape=none]
partition -> 4 -> 1 -> 3 -> 5 -> 2 -> 7
top[shape=none label="top = -1"]
}
```
`partition` 長度大於 1,進入 Divide 分支,分出 `list_less` 和 `list_greater` lists:
```c
if (!list_empty(&partition) && !list_is_singular(&partition)) {
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);
struct item *itm = NULL, *is = NULL;
list_for_each_entry_safe(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);
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
}
head[shape=none]
partition,pivot,list_less,list_greater[shape=none]
partition
pivot -> 4
list_less -> 1 -> 3 -> 2
list_greater -> 5 -> 7
top[shape=none label="top = -1"]
}
```
把 `list_less` 和 `list_greater` lists 放到 `stack` 陣列:
```c
list_move_tail (&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, &stack[++top]);
if (!list_empty(&list_less))
list_splice_tail(&list_less, &stack[++top]);
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
1 -> 3 -> 2 -> 4
5 -> 7
stack:f0 -> 5
stack:f1 -> 1
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 1"]
}
```
從 `stack` 取出最後一個 list 指派給 `partition` 再進行 Divide:
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
}
head[shape=none]
partition[shape=none]
partition -> 1 -> 3 -> 2 -> 4
top[shape=none, label="top = 0"]
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
}
head[shape=none]
partition,pivot,list_less,list_greater[shape=none]
partition
pivot -> 1
list_less
list_greater -> 3 -> 4 -> 2
top[shape=none, label="top = 0"]
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
1
3 -> 4 -> 2
stack:f0 -> 5
stack:f1 -> 3
stack:f2 -> 1
}
head[shape=none]
top[shape=none, label="top = 2"]
}
```
再從 `stack` 取出最後一個 list 指派給 `partition`:
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
3 -> 4 -> 2
stack:f0 -> 5
stack:f1 -> 3
}
head[shape=none]
partition[shape=none]
partition -> 1
top[shape=none, label="top = 1"]
}
```
當 `partition` 的長度少於等於 1 時,便進入 Conquer 分支。把 `partition` 放回 `stack`。再從 `stack` 的尾端把連續的長度少於等於 1 的 list 接回 `head`。
```c
} else {
top++;
list_splice_tail(&partition, &stack[top]);
while (top >= 0 && list_is_singular(&stack[top])) {
struct item *tmp =
list_first_entry(&stack[top], struct item, list);
list_del(&tmp->list);
INIT_LIST_HEAD(&stack[top--]);
list_add_tail(&tmp->list, head);
}
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
3 -> 4 -> 2
stack:f0 -> 5
stack:f1 -> 3
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 1"]
head -> 1
}
```
繼續從 `stack` 取出最後一個 list 指派給 `partition` 再進行 Divide:
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 0"]
partition -> 3 -> 4 -> 2
head -> 1
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
}
head[shape=none]
partition,pivot,list_less,list_greater[shape=none]
head -> 1
partition
pivot -> 3
list_less -> 2
list_greater -> 4
top[shape=none, label="top = 0"]
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
stack:f1 -> 4
stack:f2 -> 2 -> 3
}
head[shape=none]
top[shape=none, label="top = 2"]
head -> 1
}
```
繼續從 `stack` 取出最後一個 list 指派給 `partition` 再進行 Divide:
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
stack:f1 -> 4
stack:f2
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 1"]
partition -> 2 -> 3
head -> 1
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
stack:f1 -> 4
stack:f2
}
head[shape=none]
partition,pivot,list_less,list_greater[shape=none]
head -> 1
partition
pivot -> 2
list_less
list_greater -> 3
top[shape=none, label="top = 1"]
}
```
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f3>3|..."]
}
5 -> 7
stack:f0 -> 5
stack:f1 -> 4
stack:f2 -> 3
stack:f3 -> 2
}
head[shape=none]
top[shape=none, label="top = 3"]
head -> 1
}
```
繼續從 `stack` 取出最後一個 list 指派給 `partition`:
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
stack:f1 -> 4
stack:f2 -> 3
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 2"]
partition -> 2
head -> 1
}
```
當 `partition` 的長度少於等於 1 時,便進入 Conquer 分支。把 `partition` 放回 `stack`。再從 `stack` 的尾端把連續的長度少於等於 1 的 list 接回 `head`。
```graphviz
digraph {
rankdir=LR
subgraph cluster0 {
rankdir=LR
peripheries=0
subgraph cluster_stack {
label="stack"
peripheries=0
stack [shape="record"]
stack [label="<f0>0|<f1>1|<f2>2|<f2>..."]
}
5 -> 7
stack:f0 -> 5
}
head[shape=none]
partition[shape=none]
top[shape=none, label="top = 0"]
head -> 1 -> 2 -> 3 -> 4
}
```
繼續上述 Divide and Conquer 操作,直至 `stack` 為空 (top < 0) 時,便返回排序好的 `head` list。
### 提出效能改進方案,特別是避免依賴 MAX_DEPTH
採用 linked list 取代 stack 陣例,便可不需要 MAX_DEPTH。
## 測驗 3
### 解釋上述程式碼運作原理,指出其實作缺陷並改進
```graphviz
digraph {
subgraph cluster_p {
label="prev"
peripheries=0
p [shape="record"]
p [label="{<f0>value|<f1>*cmp}"]
}
subgraph cluster_c {
label="n"
peripheries=0
c [shape="record"]
c [label="{<f0>value|<f1>*cmp}"]
}
subgraph cluster_n {
label="next"
peripheries=0
n [shape="record"]
n [label="{<f0>value|<f1>*cmp}"]
}
}
```
注意看,節點的 `xor_node_t *cmp` 指標並沒有指向任何節點,只是用來記錄前後節點記憶體位址 XOR 後的值。
```c
n.xornode->cmp = XOR_COMP(&prev.xornode, &next.xornode);
````
根據 $(A \oplus B) \oplus B = A$ 特性。`next` 和 `prev` 便可透過以下方式取得。
```c
container_of(next, struct test_node, XOR_COMP(n.xornode->cmp, &prev.xornode));
container_of(prev, struct test_node, XOR_COMP(n.xornode->cmp, &next.xornode))
```
因為可以透過以上方式找到前和後的節點,所以 XOR linked list 可以實作出 doubly linked list。