# 2023q1 Homework2 (quiz2)
contributed by < `YSRossi` >
## 測驗 `1`
```c
uint64_t next_pow2(uint64_t x)
{
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 1;
x |= x >> 8;
x |= x >> 16;
x |= x >> 32;
return ++x;
}
```
- 目標找出 `x` 最接近且大於等於 2 的冪的值,`x` 最高位的 1 的下一個高位位元即是答案。
- 想法是找出 `x` 最高位的 1 ,將比這個位元低位的位元都設成 1 ,最後加一進位後,得到答案。
- 前面做了 7 次 `x |= x >> 1`,代表最高位的 1 右側的 7 個位元都設成 1,後續分別右移 8 、 16 、 32 個位元,且每次都要與 x 做 `OR` 存到 `x` 當中,使 64 個位元都處理過,最後加一進位後,得到 `x` 最接近且大於等於 2 的冪的值。
---
## 測驗 `2`
```c
int concatenatedBinary(int n)
{
const int M = 1e9 + 7;
int len = 0; /* the bit length to be shifted */
/* use long here as it potentially could overflow for int */
long ans = 0;
for (int i = 1; i <= n; i++) {
/* removing the rightmost set bit
* e.g. 100100 -> 100000
* 000001 -> 000000
* 000000 -> 000000
* after removal, if it is 0, then it means it is power of 2
* as all power of 2 only contains 1 set bit
* if it is power of 2, we increase the bit length
*/
if (!(i & i - 1))
len++;
ans = (i | (ans << len)) % M;
}
return ans;
}
```
- 迴圈中會將 `i` 接在 `ans` 的最低位。
- 作法是將 `ans` 往左位移,將低位元清出足夠多的 0,與 `i` 做 `OR` ,完成合併。
- 要位移多少取決於 `i` 最高位的 1 的位置,藉由判斷是否為 2 的冪 `if (!(i & i - 1))` ,若是的話 `i & i - 1 == 0`,將 `len` 累加,得到最高位的 1 的位置。
---
## 測驗 `3`
```c=
size_t swar_count_utf8(const char *buf, size_t len)
{
const uint64_t *qword = (const uint64_t *) buf;
const uint64_t *end = qword + len >> 3;
size_t count = 0;
for (; qword != end; qword++) {
const uint64_t t0 = *qword;
const uint64_t t1 = ~t0;
const uint64_t t2 = t1 & 0x04040404040404040llu;
const uint64_t t3 = t2 + t2;
const uint64_t t4 = t0 & t3;
count += __builtin_popcountll(t4);
}
count = (1 << 3) * (len / 8) - count;
count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0;
return count;
}
```
- 從函式 `count_utf8` 的 `for (size_t i = 0; i < len; i++)` 得知, `len` 代表的是字串總長度,單位為 `byte`。
- 從第七行 for 迴圈 `for (; qword != end; qword++)` 得知,一次會處理一個 `qword` 的大小,而一個 `qword` 為 64 位元,所以第四行的 `len >> 3` 將 `len` 除以 8 個 bytes,計算總共要處理幾個完整的 8 bytes。
- for 迴圈中使用 `not bit6 and bit7` 的 `bitmask`,找出 `10xx.xxxx` 形式的 byte,使該 byte 經過 `bitmask` 的處理後的輸出為 `1000.0000`。搭配函式 `__builtin_popcountll` 計算 64 位元中有多少位元為 1 ,即可求得 `continuation bytes` 數。
```c
count = (1 << 3) * (len / 8) - count;
count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0;
```
- 原先題目應該是 `count = (len >> 3) << 3 - count` ,作用是將 `len` 的最右側 3 個位元設為 0 ,得到整除部分的 byte 數,再減去 `continuation bytes` 數。
- `count = (1 << 3) * (len / 8) - count` 中的乘以 8 與除以 8 也有相似的移位效果,能將 `len` 的最右側 3 個位元設為 0 ,再減去 `continuation bytes` 數。
- `len & 7` 相當於取 8 的餘數,處理剩餘無法被整除的部分,使用函式 `count_utf8` 求出 `continuation bytes` 數,最後將其加上 `count` 得到最後結果。
---
## 測驗 `4`
判定 16 位元無號整數 `x` 是否符合以下特定樣式 (pattern)
```
pattern: 8000 (32768)
pattern: c000 (49152)
pattern: e000 (57344)
pattern: f000 (61440)
pattern: f800 (63488)
pattern: fc00 (64512)
pattern: fe00 (65024)
pattern: ff00 (65280)
pattern: ff80 (65408)
pattern: ffc0 (65472)
pattern: ffe0 (65504)
pattern: fff0 (65520)
pattern: fff8 (65528)
pattern: fffc (65532)
pattern: fffe (65534)
pattern: ffff (65535)
```
```c
bool is_pattern(uint16_t x)
{
const uint16_t n = -x;
return (n ^ x) < x;
}
```
* 觀察題目所要求的 `pattern` ,可以發現所有符合要求的數字都是一個最高位為 1 的 16 位元無號整數,且最低位的1與最高位之間的位元皆為1。
* -x 與 x 做 `XOR` 後,會有以下兩種功能 :
1. `x` 最低位的 1 變成 0。
2. `x` 最低位的 1 與最高位之間為 0 的位元變成 1。
- 符合題目要求的 `pattern` ,最低位的 1 與最高位之間皆為 1 ,只會將 `x` 最低位的 1 變成 0 ,因此 `XOR` 後的結果會小於 `x`。
- 不符合題目要求的 `pattern` 最低位的 1 與最高位之間存在 0,不只會將 `x` 最低位的 1 變成 0 ,還會將 `x` 最低位的 1 與最高位之間為 0 的位元變成 1,因此 `XOR` 後的結果會大於 `x`。
| x | (1111 1**1**00)~2~ | (1111 1**01**0)~2~ |
| -------- | -------- | -------- |
| n = -x | (0000 0100)~2~ | (0000 0110)~2~ |
| n ^ x | (1111 1**0**00)~2~ | (1111 1**10**0)~2~ |