1 module bio.std.hts.bam.md.operation;
2 
3 import bio.core.base;
4 import bio.core.sequence;
5 
6 import std.conv;
7 import std.traits;
8 import std.bitmanip;
9 import std.algorithm;
10 
11 /// MD tag operation types
12 enum MdOperationType : ubyte {
13     Match,
14     Mismatch,
15     Deletion
16 }
17 
18 /// Single MD operation.
19 struct MdOperation {
20 
21     private {
22         MdOperationType _type;
23         union {
24             uint _match;
25             NucleotideSequence _deletion;
26             Base16 _mismatch;
27         }
28     }
29 
30     /// Operation type
31     MdOperationType type() @property const {
32         return _type;
33     }
34 
35     /// ditto
36     void type(MdOperationType t) @property {
37         _type = t;
38     }
39 
40     /// Convenience methods
41     bool is_deletion() @property const {
42         return _type == MdOperationType.Deletion;
43     }
44 
45     /// ditto
46     bool is_match() @property const {
47         return _type == MdOperationType.Match;
48     }
49 
50     /// ditto
51     bool is_mismatch() @property const {
52         return _type == MdOperationType.Mismatch;
53     }
54 
55     /// The number of matched bases
56     ref uint match() @property {
57         return _match;
58     }
59 
60     /// Mismatched reference base
61     Base16 mismatch() @property const {
62         return _mismatch;
63     }
64 
65     /// ditto
66     void mismatch(Base16 base) @property {
67         _mismatch = base;
68     }
69 
70     /// Deleted sequence
71     ref NucleotideSequence deletion() @property {
72         return _deletion;
73     }
74 
75     static MdOperation createMatch(uint match) {
76         MdOperation m = void;
77         m._type = MdOperationType.Match;
78         m._match = match;
79         return m;
80     }
81 
82     static MdOperation createDeletion(string deletion) {
83         MdOperation m = void;
84         m._type = MdOperationType.Deletion;
85         m._deletion = nucleotideSequence(sliceableString(deletion));
86         return m;
87     }
88 
89     static MdOperation createMismatch(char mismatch) {
90         MdOperation m = void;
91         m._type = MdOperationType.Mismatch;
92         m._mismatch = Base16(mismatch);
93         return m;
94     }
95 
96     static MdOperation createDeletion(NucleotideSequence seq) {
97         MdOperation m = void;
98         m._type = MdOperationType.Deletion;
99         m._deletion = seq;
100         return m;
101     }
102 
103     static MdOperation createMismatch(Base16 base) {
104         MdOperation m = void;
105         m._type = MdOperationType.Mismatch;
106         m._mismatch = base;
107         return m;
108     }
109 
110     bool opEquals(ref const(MdOperation) other) const {
111 
112         if (type != other.type) {
113             return false;
114         }
115 
116         final switch (type) {
117             case MdOperationType.Match:
118                 return _match == other._match;
119             case MdOperationType.Mismatch:
120                 return mismatch == other.mismatch;
121             case MdOperationType.Deletion:
122                 return equal(cast()_deletion, cast()other._deletion);
123         }
124     }
125 
126     string toString() const {
127         final switch (type) {
128             case MdOperationType.Match:
129                 return "Match(" ~ to!string(_match) ~ ")";
130             case MdOperationType.Mismatch:
131                 return "Mismatch(" ~ to!string(_mismatch) ~ ")";
132             case MdOperationType.Deletion:
133                 return "Deletion(" ~ to!string(_deletion) ~ ")";
134         }
135     }
136 }
137 
138 /// Returns MD operation with reverse-complemented data
139 MdOperation reverseMdOp(MdOperation op) {
140     if (op.is_deletion)
141         return MdOperation.createDeletion(op.deletion.reverse);
142 
143     if (op.is_mismatch)
144         return MdOperation.createMismatch(op.mismatch.complement);
145 
146     return op;
147 }