1 
2 module markov.json.decoder;
3 
4 import markov.chain;
5 import markov.counter;
6 import markov.serialize;
7 import markov.state;
8 
9 import std.algorithm;
10 import std.array;
11 import std.conv;
12 import std.json;
13 import std.range;
14 import std.stdio;
15 import std.typecons;
16 
17 struct JsonDecoder(T)
18 {
19     MarkovChain!T decode(string input)
20     {
21         return MarkovChain!T(decodeStates(input.parseJSON));
22     }
23 
24 private:
25     State!T[] decodeStates(JSONValue json)
26     {
27         return json.array.map!(state => decodeState(state)).array;
28     }
29 
30     State!T decodeState(JSONValue json)
31     {
32         State!T state = State!T(json["size"].str.to!uint);
33 
34         foreach(first, counter; json["counters"].object)
35         {
36             state.set(decodeKeys(first), decodeCounter(counter));
37         }
38 
39         return state;
40     }
41 
42     Counter!T decodeCounter(JSONValue json)
43     {
44         Counter!T counter;
45 
46         foreach(follow, count; json.object)
47         {
48             counter.set(decodeKey(follow), count.str.to!uint);
49         }
50 
51         return counter;
52     }
53 
54     T[] decodeKeys(string keys)
55     {
56         return keys.to!(string[]).map!(k => decodeKey(k)).array;
57     }
58 
59     T decodeKey(string key)
60     {
61         static if(hasDecodeProperty!(T, string))
62         {
63             return T.decode(key);
64         }
65         else
66         {
67             return key.to!T;
68         }
69     }
70 }
71 
72 MarkovChain!T decodeJSON(T)(string encoded)
73 {
74     JsonDecoder!T decoder;
75     return decoder.decode(encoded);
76 }
77 
78 MarkovChain!T decodeJSON(T)(File input, size_t chunkSize = 4096)
79 {
80     return input.byChunk(chunkSize).joiner.map!(to!char).text.decodeJSON!T;
81 }
82 
83 unittest
84 {
85     auto chain1 = MarkovChain!string(1, 2, 3);
86     chain1.train("a", "b", "c", "e", "b", "a", "b", "a", "c", "e", "d", "c", "b", "a");
87 
88     import markov.json.encoder;
89     auto chain2 = chain1.encodeJSON.decodeJSON!string;
90 
91     assert(chain1.sizes.length == chain2.sizes.length);
92 
93     foreach(state1, state2; chain1.states.sort!"a.size > b.size".lockstep(chain2.states.sort!"a.size > b.size"))
94     {
95         assert(state1.size == state2.size);
96         assert(state1.keys.length == state2.keys.length);
97 
98         foreach(first1, first2; sort(state1.keys).lockstep(sort(state1.keys)))
99         {
100             assert(first1 == first2);
101             auto counters1 = state1.get(first1);
102             auto counters2 = state2.get(first2);
103 
104             assert(counters1.total == counters2.total);
105             assert(sort(counters1.keys) == sort(counters2.keys));
106 
107             foreach(follow1, follow2; sort(counters1.keys).lockstep(sort(counters2.keys)))
108             {
109                 assert(follow1 == follow2);
110                 assert(counters1.get(follow1) == counters2.get(follow2));
111             }
112         }
113     }
114 }