1 module satd.dimacs;
2 import satd.cnf;
3 import satd.solvers.dpll : SolverResult, Null;
4 import std.stdio : readln, stdin, writeln;
5 import std.array : array, join;
6 import std.string : split;
7 import std.conv : to;
8 import std.math : abs;
9 import std.range : empty;
10 import std.stdio : stdin, File;
11 import std.typecons : Tuple;
12 
13 public:
14 
15 /*
16  * "p FORMAT VARIABLES CLAUSES"
17  * FORMAT must be "cnf"
18  * VARIABLES and CLAUSES must be positive integers
19 */
20 Preamble parsePreamble(File f = stdin)
21 {
22     string[] inp;
23 
24     // comments appear in the input is ignored
25     do
26     {
27         inp = f.readln.split;
28     }
29     while (inp.length < 1 || inp[0] == "c");
30 
31     if (inp[0] != "p")
32     {
33         error("Unknown command: %s", inp[0]);
34         assert(0);
35     }
36 
37     else
38     {
39         if (inp.length != 4)
40             error("Not enough arguments");
41 
42         if (inp[1] != "cnf")
43             error("Given format \"%s\" not supported", inp[1]);
44 
45         size_t variables, clauses;
46         try
47         {
48             variables = inp[2].to!size_t, clauses = inp[3].to!size_t;
49         }
50         catch (Exception e)
51         {
52             error("Numbers in preamble couldn't be parsed");
53         }
54         return Preamble(variables, clauses);
55     }
56 }
57 
58 struct Preamble
59 {
60     size_t variables;
61     size_t clauses;
62 }
63 
64 alias parseResult = Tuple!(Clause[], "clauses", Preamble, "preamble");
65 parseResult parseClauses(File f = stdin)
66 {
67     Preamble preamble = parsePreamble(f);
68     Clause[] clauses;
69     Literal[] literals;
70     Clause.ID id = 1;
71 
72     // read until EOF then ...
73     long[] tokens;
74     try
75     {
76         tokens = f.byLineCopy.array.join(' ').split.to!(long[]);
77     }
78     catch (Exception e)
79     {
80         error("Malformed input");
81     }
82 
83     foreach (token; tokens)
84     {
85         if (token == 0)
86         {
87             if (clauses.length >= preamble.clauses)
88                 error("Too many clauses");
89 
90             Clause clause = Clause(id, literals);
91             clauses ~= clause;
92             literals = null;
93             id++;
94             continue;
95         }
96         if (abs(token) > preamble.variables)
97             error("Given %d but variable bound is %d", abs(token), preamble.variables);
98 
99         Literal literal = token;
100         literals ~= literal;
101     }
102     if (!literals.empty)
103         error("Unexpected End of File");
104 
105     return parseResult(clauses, preamble);
106 
107 }
108 
109 string solverResultToString(SolverResult sr)
110 {
111     if (sr.peek!Null)
112         return "s UNSATISFIABLE";
113     return format("s SATISFIABLE\n%s", sr.get!0.toDIMACSFormat());
114 }
115 
116 private:
117 
118 import std.string : format;
119 
120 void error(A...)(string msg, A args)
121 {
122     class DIMACSReadException : Exception
123     {
124         this(string msg)
125         {
126             super(msg);
127         }
128     }
129 
130     throw new DIMACSReadException(format(msg, args));
131 }