# 2023q1 Homework2 (quiz2)
contributed by < siwon0619 >
## 測驗1
```cpp=
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;
}
```
### 解釋程式碼
目標為找到最接近且大於等於2的冪的值,因此這邊將所有最高位元下的所有位元都將其設為1最後透過將`x`加一使其進位便會獲得最接近2的冪的值
如同`x`為(0001 0100)~2~-->(0010 0000)~2~
可以觀察到最接近於2的冪的值為原最高位元左移一位且後面位元皆為0所組成,因此可以透過`++x`來達到進位
### 用 __builtin_clzl 改寫
* Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
--> 其功能為回傳最高位元以前0的個數,因此可以用來計算需要左移多少位元
將程式改寫為:
```cpp=
uint64_t next_pow2(uint64_t x)
{
if(!x)
return 1;
int count = __builtin_clzl(x);
return 1 << (64-count);
}
```
## 測驗2
```cpp=
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 | (i << len)) % M;
}
return ans;
}
```
### 解釋程式碼
給定一整數n,回傳將 1 到 n的二進位表示法依序串接在一起所得到的二進位字串,其所代表的十進位數字 mod $10^9 + 7$ 之值。
* 可以觀察到 `i & (i - 1)`為判斷是否為2的冪次,若成立`len++`,若不成立len長度不變
* 將`ans << len`移出空間使 `i`可與其進行合併
### 用 __builtin_{clz,ctz,ffs} 改寫
```cpp=
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
*/
int len = 32 - __builtin_clz(x);
ans = (i | (i << len)) % M;
}
return ans;
}
```
使用__builtin_clz計算leading zeros,可直接計算需要位移的長度
## 測驗3
```cpp=
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;
}
```
## 測驗4
以下程式碼可判定 16 位元無號整數是否符合特定樣式 (pattern):
```cpp=
#include <stdint.h>
#include <stdbool.h>
bool is_pattern(uint16_t x)
{
if (!x)
return 0;
for (; x > 0; x <<= 1) {
if (!(x & 0x8000))
return false;
}
return true;
}
```
### 解釋程式碼
我們可以觀察到` x & 0x8000(MSB) `為 0則不符合特定pattern會return false,符合上述程式碼的樣式,如下:
```
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)
```
可以知道上述以二進位表示為
1000 0000 0000 0000~2~
1100 0000 0000 0000~2~
1110 0000 0000 0000~2~
.
.
.
.
1111 1111 1111 1111~2~
#### 改寫上述程式碼
```cpp=
bool is_pattern(uint16_t x)
{
const uint16_t n = -x;
return (n ^ x) < x;
}
```
以`x`=(1110 0000 0000 0000~2~)為例,`n`為`x`之補數,因此可以得知`n`=(0010 0000 0000 0000~2~),便將兩者取 $xor$ 可以得到 (1100 0000 0000 0000~2~),而此值比當前`x`小,便會return true,為符合pattern