# BWT 程式碼片段解釋
### fmindex.py
#### 1. FMSimpleIndex._occ():
```python=
def _occ(self, qc):
""" get the first occurance of letter qc in left-column"""
occ = 0
#-------Your code should start here---------
occ = self.occ.get(qc)
#--------Your code should end here----------
return occ
```
程式碼解說:
```
line 1: 表示 _occ function 接收一參數 qc。
line 3: 先將 occ 歸零。
line 6: 利用 self.occ.get() 取得 qc 的 occurance value。
其中,self.occ 是由 bwt.calc_first_occ(self.data) 計算出。
self.occ 是 dictionary 型態,
因此透過 .get() 內建函示即可取得 qc 的 occ value。
line 8: 回傳 occ 值。
```
-----
#### 2. FMSimpleIndex._count():
```python=
def _count(self, idx, qc):
""" count the occurances of letter qc
(rank of qc) upto position idx """
if not qc in self.occ.keys(): return 0
count = 0
#-------Your code should start here---------
count = self.data[:idx].count(qc)
#--------Your code should end here----------
return count
```
程式碼解說:
```
line 1: 表示 _count function 接收兩參數 idx 與 qc。
line 5: 先檢查 qc 是否存在 occ dictionary 清單內,
若無,則 return 0。結束 _count()
line 9: 計算 count 值
self.data[:idx]: 將字串 transformedBWT 進行修剪。
讓字串只取到 idx 位置,後面接刪除。
接著使用 python 內建函示 .count() 計算剩餘字串中,有幾個 qc。
line 11: 回傳 count 值。
```
-----
#### 3. FMSimpleIndex._lf():
```python=
def _lf(self, idx, qc):
#-------Your code should start here---------
""" return the nearset lf mapping for letter qc at position idx """
lf_result = self._occ(qc) + self._count(idx, qc)
return lf_result
#--------Your code should end here----------
```
程式碼解說:
```
line 1: 表示 _lf function 接收兩參數 idx 與 qc。
line 5: 計算 lf 值。計算方法為呼叫兩 fmindex.py 內建函示 _occ() 與 _count()
分別計算出 occ 值與 count 值後,加總,即為 lf 值。
line 6: 回傳 lf 值。
```
-----
#### 4. FMSimpleIndex._bounds():
```python=
def bounds(self, q):
""" find the first and last suffix positions for query q """
"""These are positions in the BWT string"""
"""This is the meat of the FM search algorithm"""
top = 0
bot = len(self.data)
#-------Your code should start here---------
#iterate over letters in query string q in reverse
#reverse will change q, so use [::-1]
re_q = q[::-1]
for qc in re_q:
# calculate top (which maps the position
# in the last column to the position in the first column)
top = self._lf(top, qc)
# calculate bottom
bot = self._lf(bot, qc)
#--------Your code should end here----------
if top == bot: return (-1,-1)
#since bottom is non-inclusive,
#top==bot implies that the string was not found.
return (top,bot)
```
程式碼解說:
```
line 1: 表示 bounds function 接收一參數 q。
line 5: 先將 top bound 設定為 0。
line 6: 再將 bottom bound 設定為 transformedBWT 長度。
line 11: 將 q,即欲尋找之字串,反轉。
line 13: 用 for 迴圈,逐一取出字元。
line 16: 透過 fmindex.py 內建函示 _lf 進行 top bound 夾擊,產生新 top bound。
line 17: 透過 fmindex.py 內建函示 _lf 進行 bottom bound 夾擊,產生新 bottom bound。
line 21: 若 top bound 與 bottom bound 相等,便回傳 (-1, -1),亦即找不到字串。
line 22: 反之,若 top/bottom bound 不相等,則回傳 (top, bot)。
```
-----
### bwt.py
#### 5. BurrowsWheeler.transform()
```python=
def transform(self, s):
""" Simplest Burrows-Wheeler transform implementation,
O(n^2) respective to the length of the text. """
assert self.EOS not in s,
"Input string cannot contain null character (%s)" % self.EOS
# add end of text marker
# please note that although this code
# uses the null marker to mark the end of the string,
# i've replaced it with "$" just for the output
# to help you debug your code -BZ
s += self.EOS
bwtTransformedString=''
# s is the input string,
# with the end-of-string character added for you
#-------Your code should start here---------
# generate a list of rotated input strings
inputstring = s * 2
after_rotation = []
for i in range(0, len(s)):
do_rotation = inputstring[i:i+len(s)]
after_rotation.append(do_rotation)
# sort the table of rotations
sort_result = sorted(after_rotation)
# extract the last characters of each row
for line in sort_result:
result = line[-1]
'''convert the characters to a string,
and store them in the variable bwtTransformedString
'''
bwtTransformedString = bwtTransformedString + result
#--------Your code should end here----------
return bwtTransformedString
```
程式碼解說:
```
line 1: 表示 transform function 接收一參數 s。
line 4: 排除字串中有 null character。
line 12: 將 null character 塞到 s 最後面,當 end of the string。
line 13: 將 bwtTransformedString 設為空字串。
line 19: 為方便 rotation,字串位移,我們先將 s * 2。
line 20: 宣告 after_rotation 為空 list。
line 23: 透過 for 迴圈,搭配 python string slicing 功能,進行字串 rotation。
line 24: 將 rotation 結果,append 進 after_rotation list 中。
line 27: 使用 python 內建函式 sorted(),將 after_rotation 進行排序,
並將結果傳給 sort_result。
line 30: 使用 for 迴圈,將 sort_result list 逐一取出。
line 31: 將 line 30 取出之結果,透過 python string 操弄,取出最後一個字元,
並將結果傳給 result。
line 36: 將 line 31 結果逐一 append 到 bwtTransformedString 字串上。
line 40: 回傳 bwtTransformedString。
```
-----
#### 6. calc_checkpoints()
```python=
def calc_checkpoints(s, step):
#returns the list of checkpoints, seperated by distance "step", each of which is a dictionary of the
#number of occurences of each character up to that point (noninclusive) -BZ
""" count the number of letters for each step and
return list of the counts"""
A = {} # letter count
C = [] # checkpoints, a list of letter count dictionaries
#-------Your code should start here---------
for ckp_index, q in enumerate(s):
point = ckp_index % step
if point == 0:
C.append(A.copy())
if A.get(q):
A[q] = A[q] + 1
else:
A[q] = 1
#--------Your code should end here----------
return C
```
程式碼解說:
```
此函式之目的為加速 count 值之計算。
在 index () 挑選 FMCheckpointing() 才會啟用。
line 1: 表示 transform function 接收一參數 s。
line 6: 給定一空 dictionary,存放 letter count,該 dict 命名為 A。
line 7: 給定一空 list,存放,各 check point 當時的 letter count。
line 11: 利用 enumerate() 搭配 for 迴圈,將 string 逐一取出,並授與編號。
line 12: 計算 checkpoint 檢查條件。
line 13: 透過檢查條件 ,判斷是否為 checkpoint。
若 index number % step 之餘數為零則為 checkpoint。
line 14: 若為 checkpoint,則將當時的 letter count 狀態 A,存入 list C 中。
line 16: 若可在 letter count dictionary A 中,
尋找到 q 字元,則將 letter count + 1。
line 19: 若無法在 letter count dictionary A 中,尋找到 q 字元,
則表示該字元為第一次出現,該 letter count 值 = 1。
line 22: 回傳 C (各 checkpoint 的 letter count 狀態)。s
```
-----
# -*- coding: utf-8 -*-
print("讓事情變不可能的,是「人」,不是「事」。")