1 /**
2     This package holds common code for the `dentist` algorithm.
3 
4     Copyright: © 2018 Arne Ludwig <arne.ludwig@posteo.de>
5     License: Subject to the terms of the MIT license, as written in the
6              included LICENSE file.
7     Authors: Arne Ludwig <arne.ludwig@posteo.de>
8 */
9 module dentist.common;
10 
11 import dentist.util.log;
12 import dentist.util.region : Region;
13 import std.algorithm : count, fold, map, max, sum;
14 import std.array : array;
15 import std.conv : to;
16 import std.format : format;
17 import std.math : floor;
18 import std.traits : TemplateOf;
19 import std.typecons : Flag;
20 
21 public import dentist.common.alignments;
22 public import dentist.common.binio;
23 public import dentist.common.scaffold;
24 
25 
26 /// True iff building with testing commands.
27 version (DentistTesting)
28     enum isTesting = true;
29 else
30     enum isTesting = false;
31 
32 /// Evaluate to `value` if building with testing command;
33 /// otherwise to `typeof(value).init`.
34 template testingOnly(alias value)
35 {
36     static if (isTesting)
37         enum testingOnly = value;
38     else
39         enum testingOnly = typeof(value).init;
40 }
41 
42 /// Thrown if some runtime error in the `dentist` algorithm occurs.
43 class DentistException : Exception
44 {
45     pure nothrow @nogc @safe this(string msg, string file = __FILE__,
46             size_t line = __LINE__, Throwable nextInChain = null)
47     {
48         super(msg, file, line, nextInChain);
49     }
50 }
51 
52 /// A region of the reference aka. mask.
53 alias ReferenceRegion = Region!(size_t, size_t, "contigId");
54 /// An interval of a reference contig.
55 alias ReferenceInterval = ReferenceRegion.TaggedInterval;
56 /// A point on the reference.
57 alias ReferencePoint = ReferenceRegion.TaggedPoint;
58 /// A region of a read aka. mask.
59 alias ReadRegion = Region!(size_t, size_t, "readId");
60 /// An interval of a read contig.
61 alias ReadInterval = ReadRegion.TaggedInterval;
62 /// A point on a read.
63 alias ReadPoint = ReadRegion.TaggedPoint;
64 
65 /// A point on the output assembly.
66 struct OutputCoordinate
67 {
68     static enum OriginType : ubyte
69     {
70         global,
71         contig,
72         scaffold,
73         scaffoldContig,
74     }
75 
76     id_t scaffoldId;
77     id_t contigId;
78     coord_t coord;
79 
80     @property coord_t idx() const pure nothrow
81     {
82         return coord - 1;
83     }
84 
85     @property OriginType originType() const pure nothrow
86     {
87         if (scaffoldId == 0 && contigId == 0)
88             return OriginType.global;
89         else if (scaffoldId == 0 && contigId > 0)
90             return OriginType.contig;
91         else if (scaffoldId > 0 && contigId == 0)
92             return OriginType.scaffold;
93         else
94             return OriginType.scaffoldContig;
95     }
96 
97     string toString() const
98     {
99         final switch(originType)
100         {
101         case OriginType.global:
102             return format!`%d`(coord);
103         case OriginType.contig:
104             return format!`contig/%d/%d`(contigId, coord);
105         case OriginType.scaffold:
106             return format!`scaffold/%d/%d`(scaffoldId, coord);
107         case OriginType.scaffoldContig:
108             return format!`scaffold/%d/contig/%d/%d`(scaffoldId, contigId, coord);
109         }
110     }
111 }
112 
113 /// Returns the alignment region of alignmentChain.
114 R to(R, string contig = "contigA")(in AlignmentChain alignmentChain) pure
115         if (__traits(isSame, TemplateOf!R, Region))
116 {
117     auto contigId = mixin("alignmentChain." ~ contig ~ ".id");
118 
119     return R(alignmentChain
120         .localAlignments
121         .map!(la => R.TaggedInterval(
122             contigId,
123             mixin("la." ~ contig ~ ".begin"),
124             mixin("la." ~ contig ~ ".end"),
125         ))
126         .array
127     );
128 }