package explain import ( "bufio" "fmt" "io" "strconv" "strings" ) // parseClause parses a line representing a clause in the DIMACS CNF syntax. func parseClause(fields []string) ([]int, error) { clause := make([]int, 0, len(fields)-1) for _, rawLit := range fields { lit, err := strconv.Atoi(rawLit) if err != nil { return nil, fmt.Errorf("could not parse clause %v: %v", fields, err) } if lit != 0 { clause = append(clause, lit) } } return clause, nil } // ParseCNF parses a CNF and returns the associated problem. func ParseCNF(r io.Reader) (*Problem, error) { sc := bufio.NewScanner(r) var pb Problem for sc.Scan() { line := sc.Text() fields := strings.Fields(line) if len(fields) == 0 { continue } switch fields[0] { case "c": continue case "p": if err := pb.parseHeader(fields); err != nil { return nil, fmt.Errorf("could not parse header %q: %v", line, err) } default: if err := pb.parseClause(fields); err != nil { return nil, fmt.Errorf("could not parse clause %q: %v", line, err) } } } if err := sc.Err(); err != nil { return nil, fmt.Errorf("could not parse problem: %v", err) } return &pb, nil } func (pb *Problem) parseHeader(fields []string) error { if len(fields) != 4 { return fmt.Errorf("expected 4 fields, got %d", len(fields)) } strVars := fields[2] strClauses := fields[3] var err error pb.NbVars, err = strconv.Atoi(fields[2]) if err != nil { return fmt.Errorf("invalid number of vars %q: %v", strVars, err) } if pb.NbVars < 0 { return fmt.Errorf("negative number of vars %d", pb.NbVars) } pb.units = make([]int, pb.NbVars) pb.NbClauses, err = strconv.Atoi(fields[3]) if err != nil { return fmt.Errorf("invalid number of clauses %s: %v", strClauses, err) } if pb.NbClauses < 0 { return fmt.Errorf("negative number of clauses %d", pb.NbClauses) } pb.Clauses = make([][]int, 0, pb.NbClauses) return nil } func (pb *Problem) parseClause(fields []string) error { clause, err := parseClause(fields) if err != nil { return err } pb.Clauses = append(pb.Clauses, clause) if len(clause) == 1 { lit := clause[0] v := lit if lit < 0 { v = -v } if v > pb.NbVars { // There was an error in the header return fmt.Errorf("found lit %d but problem was supposed to hold only %d vars", lit, pb.NbVars) } if lit > 0 { pb.units[v-1] = 1 } else { pb.units[v-1] = -1 } } return nil }