# 2017q1 Homework5 (matrix)
contributed by < `ryanwang522` >
## 開發環境
- OS: ubuntu 16.04 LTS
- L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 3072K
- Architecture:x86_64
- CPU op-mode(s): 32-bit, 64-bit
- Byte Order: Little Endian
- CPU(s): 4
- Model name: Intel® Core™ i5-4200H CPU @ 2.80GHz
## 開發紀錄
### matrix.h
* 看不太懂 macro 的寫法,參考 [Concatenation](https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html) 的說明,利用 `##` operator 連接字串,讓宣告更為方便。
```C=
struct command commands[] =
{
{ "quit", quit_command },
{ "help", help_command },
…
};
```
* 例如上面這段 code 可以替換成
```c=
#define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
…
};
```
所以原本 `matrix.h` 裡的寫法就可以替換成兩次的
```C=
typedef struct { float values[3][3]; } Mat3x3
typedef struct { float values[4][4]; } Mat4x4
```
---
### 嘗試支援不同的 data type 和 matrix size
* 剛看完 macro 的資料就想說來練習怎麼使用好了,雖然直接寫函式比較直觀,不過就試試看吧~
```clike
#define DECLARE_MATRIX_FLEXIBLE(type, cols, rows) \
type **mat ## cols ## rows = (type **) malloc(sizeof(type *) * rows); \
for (int i = 0; i < rows; i++) \
mat ## cols ## rows[i] = (type *) malloc(sizeof(type) * cols)
DECLARE_MATRIX_FLEXIBLE(float, 4, 4);
```
* 一開始的想法,後來弄懂 macro 後才發現這樣寫不太 ok
* 上面這段 code 會產生 `initializer element is not constant` 的編譯錯誤,參考網路上的資料應該是對全域變數進行了不合法的初始化
* 嘗試將要執行的程式碼放在函式,並藉由 define 宣告函式,在 define 一個額外的函式呼叫
```C=
#define DECLARE_MATRIX_TYPE(type) \
static type **matrix_##type(int cols, int rows) { \
type **mat ## cols ## rows = (type **) malloc(sizeof(type *) * rows); \
for (int i = 0; i < rows; i++) \
mat ## cols ## rows[i] = (type *) malloc(sizeof(type) * cols); \
return mat ## cols ## rows; }
DECLARE_MATRIX_TYPE(float);
#define CREATE_MATRIX(type, cols, rows) \
type **type##_mat##cols##x##rows = matrix_##type(cols, rows)
```
* 藉由 `DECLARE_MATRIX_TYPE(float)` 定義可以動態配置一個二維陣列的 function,接著只要在 `.c` 檔呼叫 `CREATE_MATRIX(float, 4, 4)` 就可以產生一個 `float **` 型態的變數 `float_mat`
* 不過有個嚴重的問題就是因為變數名稱寫死所以只能用一次...,雖然覺得做了不少白工,但沒關係就當作個經驗吧XD
#### 修改 `matrix_naive.c` 的 `assign` 函式
* 遇到問題!
* 如果同時要支援不同 data type ,函式的參數型態會無法區分
* 先實作支援不同大小的矩陣
* 直接傳 2D Array
---
### 實作支援不同大小的矩陣
* 把之前嘗試的 macro 拿掉,修改 `main()`
* 動態配置二維陣列作為 matrix
* 捨棄先前直接寫死的矩陣,產生亂數矩陣方便測試
* 增加一些方便的函式
```C=
float **alloc(int row, int col)
{
float **result;
if (!(result = (float **) malloc(row * sizeof(float *))))
return NULL;
for (int i = 0; i < col; i++)
if (!(result[i] = (float *) malloc(col * sizeof(float)))) {
free(result);
return NULL;
}
return result;
}
void free_space(float ***target, int col)
{
for (int i = 0; i < col; i++)
free((*target)[i]);
free(**target);
**target = NULL;
}
```
* 學到了如何亂數產生浮點數,記得要加上 `srand(time(NULL))`
```C=
float randFloat(float range)
{
return ((float)rand() / (float)(RAND_MAX)) * range;
}
```
* 接著修改 `assign` 函式如下
```C=
static void assign(Matrix *thiz, int rows, int cols, float **data)
{
thiz->row = rows;
thiz->col = cols;
thiz->priv = malloc(cols * rows * sizeof(float));
PRIV(thiz)->values = (float **) malloc(rows * sizeof(float *));
for (int i = 0; i < cols; i++)
PRIV(thiz)->values[i] = (float *) malloc(cols * sizeof(float));
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
PRIV(thiz)->values[i][j] = data[i][j];
}
```
* 這裡有個問題...
* `thiz->priv = malloc(cols * rows * sizeof(float));`
* 這裡配置給 `void *priv` 一塊記憶體,在將 `thiz->priv` cast 成 `struct naive_priv *` ,但不懂為什麼要給 cols * rows 的大小呢
* 先紀錄一下,再想想...
* 要注意 values 是宣告成 `float **` ,要記得 malloc 空間給她否則直接存取會造成 segmentation fault
---
## 參考資料
- [Concatenation](https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html)
- [ptt - 第一次使用亂數就上手](https://www.ptt.cc/man/C_and_CPP/DB9B/DE78/M.1198667412.A.173.html)