mirror of
https://github.com/mudler/luet.git
synced 2025-07-19 01:37:13 +00:00
105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
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
|
|
}
|