1 /**
2     This is the `showMask` command of `dentist`.
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.commands.showMask;
10 
11 import dentist.common :
12     ReferenceInterval,
13     ReferenceRegion;
14 import dentist.util.log;
15 import dentist.dazzler : readMask;
16 import std.algorithm :
17     map,
18     max,
19     maxElement,
20     sum;
21 import std.math : log10, lrint, FloatingPointControl;
22 import std.stdio : writefln, writeln, stderr;
23 import vibe.data.json :
24     serializeToJsonString,
25     toJson = serializeToJson,
26     toJsonString = serializeToPrettyJson;
27 
28 /// Execute the `showMask` command with `options`.
29 void execute(Options)(in Options options)
30 {
31     auto masks = [options.repeatMask] ~ options.additionalMasks;
32     ReferenceRegion mergedMask;
33     Stats[] statsList;
34 
35     foreach (mask; masks)
36     {
37         auto maskRegion = ReferenceRegion(readMask!ReferenceInterval(
38             options.refDb,
39             mask,
40             options.workdir,
41         ));
42 
43         if (shouldLog(LogLevel.debug_))
44             stderr.writeln(serializeToJsonString(maskRegion.intervals));
45         statsList ~= statsFor(mask, maskRegion);
46 
47         if (masks.length > 1)
48             mergedMask |= maskRegion;
49     }
50 
51     if (masks.length > 1)
52     {
53         if (shouldLog(LogLevel.debug_))
54             stderr.writeln(serializeToJsonString(mergedMask.intervals));
55         statsList ~= statsFor("__merged__", mergedMask);
56     }
57 
58     if (options.useJson)
59         writeln(statsList.toJsonString);
60     else
61         writeTabular(statsList);
62 }
63 
64 struct Stats
65 {
66     string name;
67     size_t numIntervals;
68     size_t numMaskedBases;
69 
70     size_t columnWidth() const nothrow
71     {
72         FloatingPointControl fpCtrl;
73         fpCtrl.rounding = FloatingPointControl.roundUp;
74         auto numWidth = lrint(log10(max(
75             numIntervals,
76             numMaskedBases,
77         )));
78         numWidth = max(numWidth, name.length);
79 
80         return numWidth;
81     }
82 }
83 
84 Stats statsFor(string name, ReferenceRegion maskRegion)
85 {
86     return Stats(
87         name,
88         maskRegion.intervals.length,
89         maskRegion.intervals.map!"a.size".sum,
90     );
91 }
92 
93 void writeTabular(Stats[] statsList)
94 {
95     auto columnWidth = statsList.map!"a.columnWidth".maxElement;
96 
97     foreach (i, stats; statsList)
98     {
99         writefln!"name:           %*s"(columnWidth, stats.name);
100         writefln!"numIntervals:   %*d"(columnWidth, stats.numIntervals);
101         writefln!"numMaskedBases: %*d"(columnWidth, stats.numMaskedBases);
102 
103         if (i < statsList.length - 1)
104             writeln();
105     }
106 }