# 2018q3 Homework2 (lab0)
contributed by < [Jyun-Neng](https://github.com/Jyun-Neng) >
>請補上實驗環境
>[name=課程助教][color=red]
* 修改 `Makefile`
* 刪除以下兩行則執行 `make` 指令後便不會產生 `handin.tar`
```shell
-tar -cf handin.tar queue.c queue.h
tar cf handin.tar queue.c queue.h
```
:::info
`handin.tar` 的存在是為了 CMU Autolab 使用,以我們的使用情境來說,的確多餘,歡迎送 pull request 過來
:notes: jserv
:::
# Implementation of Queue
## Queue Structure
* 新增記錄 queue 內 element 總數的 `size`
* 新增 pointer to tail node in queue。
```clike=
typedef struct {
list_ele_t *head; // pointer to head element
list_ele_t *tail; // pointer to tail element
int size; // queue size
}
```
## Create empty queue
* 需要判斷若 malloc 回傳 NULL 的情況。
```clike=
if (q) {
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
```
## Free all storage used by queue
* 釋放 queue 的空間
* 釋放 queue 內 element 的空間
::: warning
雖說若 q 是 NULL 則 `free(q)` 不會運作,
但 `pos = q->head` 在 q 是 NULL 的情況下會導致 segmantation fault。因此,仍需要判斷 q 是否為 NULL。
:::
```clike=
if (!q) // prevent to execute line 4
return;
list_ele_t *pos = q->head;
while (pos) {
list_ele_t *tmp = pos;
pos = pos->next_node;
free(tmp);
}
free(q);
```
## Insert element at head of queue
* 判斷 queue 是否為 NULL。
* 判斷 malloc 新的 element 是否回傳 NULL。
* queue size 要增加。
* 要考慮 tail pointer,當加入第一個 element 時 tail pointer 也須指向此 element。
```clike=
if (!q)
return false;
list_ele_t *newh;
newh = (list_ele_t *) malloc(sizeof(list_ele_t));
if (newh) {
newh->value = strdup(s);
newh->next = q->head;
q->head = newh;
if (++(q->size) == 1)
q->tail = q->head; // tail node should be asigned at first insertion
return true;
}
return false;
```
## Insert element at tail of queue
* 判斷 queue 是否為 NULL。
* 判斷 malloc 新的 element 是否回傳 NULL。
* queue size 要增加。
* 要考慮 head pointer,當加入第一個 element 時 head pointer 也須指向此 element。
```clike=
if (!q)
return false;
list_ele_t *newt;
newt = (list_ele_t *) malloc(sizeof(list_ele_t));
if (newt) {
newt->next = NULL;
newt->value = strdup(s);
if (++(q->size) == 1)
q->head = newt; // head node should be assigned at first insertion
else
q->tail->next = newt;
q->tail = newt;
return true;
}
return false;
```
## Remove element from head of queue
* 當 q 或者 q->head 是 NULL 時,回傳 false。
* queue size 要減少。
* 要釋放移除的 element 的記憶體空間。
* 要顯示移除的 element 內記錄的字串。
* 複製移除字串。
:::danger
若 `sp` 不是 NULL,被移除的 element 內儲存的字串需複製到 `sp` 且要比較字串長度及`bufsize`,若字串長度大於 `bufsize-1` ,僅複製 `bufsize-1` 的字串。反之,則複製整個字串大小,且最後都需加入 null character。
:::
```clike=
if (!q || !(q->head))
return false;
list_ele_t *tmp = q->head;
unsigned long size;
if (sp) { // copy the removed string
size = strlen(tmp->value);
size = (size > bufsize - 1) ? bufsize - 1 : size;
strncpy(sp, tmp->value, size);
sp[size] = '\0';
}
q->size--;
q->head = q->head->next;
free(tmp);
return true;
```
> free 字串會出現 double free error 是 strdup 惹的禍,因為 harness.h 有段 macro 會把 free 換成一個自己開發的函式,導致你用不到真正的 free ,變成在用 malloc 與 test_free ,因此直接用 malloc 讓 preprocessor 把它也轉成 test_malloc 就好了。
> [name=pjchiou] [time=2018 Sep 27 Thu 05:58]
## Return number of elements in queue
* 由於我們已經在 queue structure 新增記錄 size 的變數,因此只需回傳當前紀錄queue大小的 size 變數即可,運算時間及為 $O(1)$。
* 若queue已經被釋放記憶體空間或根本沒有queue存在,直接回傳 0。
```clike=
return (q) ? q->size : 0;
```
## Reverse elements in queue
* 當不存在 queue、queue 內是空的、只有一個 element,不需動作。
* 需要三個 pointers 來記錄當前、下一個、前一個的 element。
* Time complexity: $O(n)$
```clike=
next = q->head->next;
prev = q->head;
while (next) {
pos = next;
next = next->next;
pos->next = prev;
prev = pos;
}
```
# Test Results
```shell
$make test
.
.
.
TOTAL 100/100
```
:::info
分析測試的結果,剩餘的錯誤是因為尚未完成 reverse 所導致,其餘的部分皆已完成且通過測試。
:::
> 未完成 reverse 的測試結果: 81/100
> [name=Lawrence][time=Sat, Sep 22, 2018 11:35 PM]
:::danger
Reverse 完成後發現 remove 是有問題的,string copy 沒有分辨好要複製的長度及沒有加 null character。
> [name=Lawrence][time=Sun, Sep 23, 2018 3:34 PM]
:::
> 修改完 copy string 的問題後測試完成: 100/100
> [name=Lawrence][time=Sun, Sep 23, 2018 3:36 PM]
>
:::danger
請繼續更新!
-- 課程助教
:::
# 自動評分系統
## `driver.py`
### `getopt`
`optlist, args = getopt.getopt(args, 'hp:t:v:A')`
這段程式碼將所需的參數存到 `optlist`
[Python documentation -- getopt](https://docs.python.org/2/library/getopt.html)
* Parses command line options and parameter list. args is the argument list to be parsed, without the leading reference to the running program. Typically, this means `sys.argv[1:]`. options is the string of option letters that the script wants to recognize, with options that require an argument followed by a colon (`:` ; i.e., the same format that Unix getopt() uses).
```python
>> import getopt
>> args = ['-h', '-p', 'xx', '-t', 'xxx', '-v', 'xxxx', '-A']
>> optlist, args = getopt.getopt(args, 'hp:t:v:A')
>> optlist
[('-h', ''), ('-p', 'xx'), ('-t', 'xxx'), ('-v', 'xxxx'), ('-A'. '')]
>> args
[]
```
### `runTrace`
`runTrace` 為執行測試指令的函式
```python
fname = "%s/%s.cmd" % (self.traceDirectory, self.traceDict[tid])
vname = "%d" % self.verbLevel
clist = [self.qtest, "-v", vname, "-f", fname]
try:
retcode = subprocess.call(clist)
except Exception as e:
print "Call of '%s' failed: %s" % (" ".join(clist), e)
return False
```
`retcode = subprocess.call(clist)`
等同於執行 command line `./qtest -v -f ./traces/file.cmd`,`qtest` 便會執行 `file.cmd` 的指令來測試。
## `qtest.c`
測試程式的指令是由 `console.[ch]` 來管理,且是用 linked list 來儲存新增的指令。
```C
/* Information about each command */
/* Organized as linked list in alphabetical order */
typedef struct CELE cmd_ele, *cmd_ptr;
struct CELE {
char *name;
cmd_function operation;
char *documentation;
cmd_ptr next;
};
```