1 module plotcli.data;
2 
3 import std.typecons : Tuple;
4 
5 version( unittest )
6 {
7     import dunit.toolkit;
8 }
9 
10 
11 import plotcli.options : Options, OptionRange;
12 
13 /** 
14  Default values to use for many of the settings.
15 */
16 auto aesDefaults()
17 {
18     import ggplotd.aes : DefaultValues, merge;
19     return DefaultValues.merge(Tuple!(string, "legend", string, "plotID", string, "type", 
20         string, "plotname", string, "format", string, "xlabel", string, "ylabel",
21         string, "colourgradient", double, "x", double, "y",
22         string, "colour")
23         ( "", "", "point", "plotcli", 
24         "png", "x", "y", "default", double.init, double.init, "black") ); 
25 }
26 
27 private int safeToIndex( string str )
28 {
29     import std.conv : to;
30     import std.range : empty;
31     if (str.empty)
32         return -1;
33     else
34         return str.to!int;
35 }
36 
37 auto toTuples( string[] columns, Options options, int lineCount )
38 {
39     // TODO use generate?
40     struct Tuples
41     {
42         import std.regex : ctRegex, match;
43         this( string[] columns, Options options, int lineCount )
44         {
45             import std.range : empty, repeat;
46             import std.array : array;
47             _columns = columns;
48             _lineCount = lineCount;
49             _options = options;
50         }
51 
52         @property bool empty()
53         {
54             import std.conv : to;
55             import std.range : empty, front;
56             return ( 
57                     (!_options.values["x"].empty 
58                         && _options.values["x"].front.safeToIndex 
59                             >= _columns.length.to!int) 
60                 || (!_options.values["y"].empty 
61                         && _options.values["y"].front.safeToIndex 
62                             >= _columns.length.to!int) 
63                 || (_options.values["x"].empty && _options.values["y"].empty)
64                    );
65         }
66 
67         @property auto front()
68         {
69             import std.conv : to;
70             import std.range : empty, front;
71             import ggplotd.aes : DefaultValues, merge;
72             import plotcli.parse : isInteger;
73 
74             auto tuple = aesDefaults().merge(Tuple!(double, "x", double, "y",
75                 string, "colour" )
76                 ( lineCount.to!double, lineCount.to!double, columnID.to!string ) 
77             );
78 
79             foreach( i, field; tuple.fieldNames )
80             {
81                 if (field in _options.values && !_options.values[field].empty)
82                 {
83                     auto f = _options.values[field].front;
84                     if (f.isInteger)
85                     {
86                         tuple[i] = _columns[f.to!int].to!(typeof(tuple[i]));
87                     } else if (!f.empty) {
88                         tuple[i] = f.to!(typeof(tuple[i]));
89                     }
90                 }
91             }
92             // I hoped special cases for types would not have been needed
93             // but I do not see a better way/we want to group by column
94             if (tuple.type == "box")
95                 tuple.y = columnID.to!double;
96             return tuple;
97         }
98 
99         void popFront()
100         {
101             import std.range : empty, popFront;
102             foreach( k, ref v; _options.values )
103                 if (!v.empty)
104                     v.popFront;
105             ++columnID;
106         }
107 
108         string[] _columns;
109         Options _options;
110         int columnID = 0;
111         int _lineCount;
112     }
113 
114     return Tuples( columns, options, lineCount );
115 }
116 
117 unittest
118 {
119     Options options;
120     options.values["y"] = OptionRange!string("1");
121 
122     auto ts = ["1","2","3"].toTuples( options, -1 );
123     assertEqual( ts.front.x, -1 );
124     assertEqual( ts.front.y, 2 );
125 
126     options.values["x"] = OptionRange!string("2");
127 
128     ts = ["1","2","3"].toTuples( options, -1 );
129     assertEqual( ts.front.x, 3 );
130     assert(!ts.empty);
131     ts.popFront;
132     assert(ts.empty);
133 
134     options.values["x"] = OptionRange!string("0,1,..");
135     ts = ["1","2","3"].toTuples( options, -1 );
136     assertEqual( ts.front.x, 1 );
137     ts.popFront;
138     assertEqual( ts.front.x, 2 );
139     ts.popFront;
140     assertEqual( ts.front.x, 3 );
141     ts.popFront;
142     assert( ts.empty );
143 
144     import plotcli.options : defaultOptions;
145     options = defaultOptions();
146     options.values["x"] = OptionRange!string("0,0,1,0,1");
147     options.values["y"] = OptionRange!string(",2,,,");
148     ts = ["0.04", "3.22", "-0.27"].toTuples( options, -1 );
149     import std.stdio;
150     assertEqual( ts.front.x, 0.04 );
151     assertEqual( ts.front.y, -1 );
152     ts.popFront;
153     assertEqual( ts.front.x, 0.04 );
154     assertEqual( ts.front.y, -0.27 );
155 }
156 
157