Solution (source code)

= Solution

Use the number of alignment columns, including gap columns, as the denominator. A column contributes to <sequence identity> when its two nongap letters are equal, to <sequence similarity> when its two nongap letters have a strictly positive <BLOSUM62> score, and to the <gap fraction> when either letter is a gap. A zero substitution score is not counted as similar.

The implementation in part (a) computes each numerator directly during its final pass through the aligned strings:

``
for name, a in alignments.items():
    print(name, a["alignment_length"], a["identities"],
          a["similarities"], a["gaps"])
    print(a["identity_percent"], a["similarity_percent"], a["gap_percent"])
``

|| Query
|| Columns
|| Identical
|| Similar
|| Gap columns

| baboon
| 66
| 63 (95.45%)
| 65 (98.48%)
| 1 (1.52%)

| squirrel monkey
| 58
| 49 (84.48%)
| 50 (86.21%)
| 4 (6.90%)

| dingo
| 71
| 57 (80.28%)
| 59 (83.10%)
| 10 (14.08%)

| goldfish
| 72
| 34 (47.22%)
| 50 (69.44%)
| 14 (19.44%)

Identical columns also count as similar for these data, so these percentages are overlapping summaries rather than three categories summing to 100%.