diff --git a/pkg/package/database.go b/pkg/package/database.go new file mode 100644 index 00000000..c3bc28f4 --- /dev/null +++ b/pkg/package/database.go @@ -0,0 +1,18 @@ +// Copyright © 2019 Ettore Di Giacinto +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, see . + +package pkg + +var Database map[string]string = map[string]string{} diff --git a/pkg/package/package.go b/pkg/package/package.go index f36e3c1b..1ff85da0 100644 --- a/pkg/package/package.go +++ b/pkg/package/package.go @@ -1,4 +1,4 @@ -// Copyright © 2018 Ettore Di Giacinto +// Copyright © 2019 Ettore Di Giacinto // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,6 +18,9 @@ package pkg import ( "encoding/base64" "encoding/json" + "errors" + "fmt" + "hash/crc32" "github.com/crillab/gophersat/bf" @@ -29,8 +32,10 @@ type Package interface { SetState(state State) Package BuildFormula() ([]bf.Formula, error) IsFlagged(bool) Package - Requires([]Package) Package - Conflicts([]Package) Package + Flagged() bool + + Requires([]*DefaultPackage) Package + Conflicts([]*DefaultPackage) Package } type DefaultPackage struct { @@ -38,15 +43,15 @@ type DefaultPackage struct { Version string UseFlags []string State State - PackageRequires []Package - PackageConflicts []Package + PackageRequires []*DefaultPackage + PackageConflicts []*DefaultPackage IsSet bool } type PackageUse []string type State string -func NewPackage(name, version string, requires []Package, conflicts []Package) Package { +func NewPackage(name, version string, requires []*DefaultPackage, conflicts []*DefaultPackage) *DefaultPackage { return &DefaultPackage{Name: name, Version: version, PackageRequires: requires, PackageConflicts: conflicts} } @@ -75,7 +80,12 @@ func (p *DefaultPackage) Encode() (string, error) { return "", err } - return base64.StdEncoding.EncodeToString(res), nil + enc := base64.StdEncoding.EncodeToString(res) + crc32q := crc32.MakeTable(0xD5828281) + ID := fmt.Sprintf("%08x\n", crc32.Checksum([]byte(enc), crc32q)) + Database[ID] = base64.StdEncoding.EncodeToString(res) + fmt.Println("Package Encoded:", p.Name+" => "+ID) + return ID, nil } func (p *DefaultPackage) WithState(state State) Package { @@ -85,17 +95,20 @@ func (p *DefaultPackage) IsFlagged(b bool) Package { p.IsSet = b return p } +func (p *DefaultPackage) Flagged() bool { + return p.IsSet +} func (p *DefaultPackage) SetState(state State) Package { p.State = state return p } -func (p *DefaultPackage) Requires(req []Package) Package { +func (p *DefaultPackage) Requires(req []*DefaultPackage) Package { p.PackageRequires = req return p } -func (p *DefaultPackage) Conflicts(req []Package) Package { +func (p *DefaultPackage) Conflicts(req []*DefaultPackage) Package { p.PackageConflicts = req return p } @@ -105,7 +118,13 @@ func (p *DefaultPackage) Clone() Package { return new } -func DecodePackage(pa string) (Package, error) { +func DecodePackage(ID string) (Package, error) { + + pa, ok := Database[ID] + if !ok { + return nil, errors.New("No package found with that id") + } + enc, err := base64.StdEncoding.DecodeString(pa) if err != nil { return nil, err @@ -119,7 +138,7 @@ func DecodePackage(pa string) (Package, error) { } func (p *DefaultPackage) BuildFormula() ([]bf.Formula, error) { - encodedA, err := p.Encode() + encodedA, err := p.IsFlagged(true).Encode() if err != nil { return nil, err } @@ -128,7 +147,7 @@ func (p *DefaultPackage) BuildFormula() ([]bf.Formula, error) { var formulas []bf.Formula if p.IsSet { - formulas = append(formulas, A) + //formulas = append(formulas, A) //f = bf.And(f, bf.Var(encodedA)) } else { //f = bf.And(f, bf.Not(bf.Var(encodedA))) @@ -137,11 +156,12 @@ func (p *DefaultPackage) BuildFormula() ([]bf.Formula, error) { //formulas = append(formulas, A) for _, required := range p.PackageRequires { - encodedB, err := required.Encode() + encodedB, err := required.IsFlagged(true).Encode() if err != nil { return nil, err } B := bf.Var(encodedB) + formulas = append(formulas, bf.Or(bf.Not(A), B)) f, err := required.BuildFormula() if err != nil { @@ -149,27 +169,33 @@ func (p *DefaultPackage) BuildFormula() ([]bf.Formula, error) { } formulas = append(formulas, f...) - formulas = append(formulas, bf.Or(bf.Not(A), bf.And(A, B))) - } for _, required := range p.PackageConflicts { - encodedB, err := required.Encode() + encodedB, err := required.IsFlagged(true).Encode() if err != nil { return nil, err } B := bf.Var(encodedB) + formulas = append(formulas, bf.Or(bf.Not(A), + bf.Not(B))) f, err := required.BuildFormula() if err != nil { return nil, err } - - formulas = append(formulas, bf.Or(bf.Not(A), - bf.And(A, bf.Not(B)))) formulas = append(formulas, f...) + //if required.Flagged() { + // f, err := required.BuildFormula() + // if err != nil { + // return nil, err + // } + // formulas = append(formulas, bf.Not(bf.And(f...))) + //} } - return []bf.Formula{bf.Implies(A, bf.And(formulas...))}, nil + //return []bf.Formula{bf.And(formulas...)}, nil + return formulas, nil + //return []bf.Formula{bf.Implies(A, bf.And(formulas...))}, nil } diff --git a/pkg/solver/decoder.go b/pkg/solver/decoder.go index 282c9647..7fb43784 100644 --- a/pkg/solver/decoder.go +++ b/pkg/solver/decoder.go @@ -16,24 +16,36 @@ package solver import ( - "fmt" - pkg "gitlab.com/mudler/luet/pkg/package" ) -func DecodeModel(model map[string]bool) []pkg.Package { - ass := make([]pkg.Package, 0) +type PackageAssert struct { + Package pkg.Package + Value bool +} + +func DecodeModel(model map[string]bool) ([]PackageAssert, error) { + ass := make([]PackageAssert, 0) for k, v := range model { if a, err := pkg.DecodePackage(k); err == nil { - fmt.Println(a) - a.IsFlagged(false) - if v { - a.IsFlagged(true) - } + + // fmt.Println("Flagged", v, a.Flagged()) + // if v { + // fmt.Println("To flag", a) + // } + // if a.Flagged() && !v { + // a.IsFlagged(false) + // } else if !a.Flagged() && v { + // fmt.Println("To flag ", a) + // a.IsFlagged(true) + // } + //if a.State == common.STATE_CURRENT { - ass = append(ass, a) + ass = append(ass, PackageAssert{Package: a, Value: v}) //} // Else, there was a state transition between Initial state and current run + } else { + return nil, err } } - return ass + return ass, nil } diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index e9048312..cb7af10d 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -27,32 +27,43 @@ type State interface{ Encode() string } type PackageSolver interface { BuildFormula() (bf.Formula, error) - Solve() ([]pkg.Package, error) + Solve() ([]PackageAssert, error) Apply() (map[string]bool, bf.Formula, error) + SetSteps(int) + SetWorld(p []pkg.Package) } type Solver struct { - PackageCollection []pkg.Package - InitialState []pkg.Package + Wanted []pkg.Package + Installed []pkg.Package + World []pkg.Package + Steps int } -func NewSolver(pcoll []pkg.Package, init []pkg.Package) PackageSolver { - return &Solver{PackageCollection: pcoll, InitialState: init} +func NewSolver(pcoll []pkg.Package, init []pkg.Package, w []pkg.Package) PackageSolver { + for _, v := range init { + v.IsFlagged(true) + } + for _, v := range pcoll { + v.IsFlagged(false) + } + for _, v := range w { + v.IsFlagged(true) + } + return &Solver{Wanted: pcoll, Installed: init, World: w} } -func (s *Solver) BuildFormula() (bf.Formula, error) { - //f := bf.True +func (s *Solver) SetSteps(st int) { + s.Steps = st +} + +func (s *Solver) SetWorld(p []pkg.Package) { + s.World = p +} + +func (s *Solver) BuildWorld() (bf.Formula, error) { var formulas []bf.Formula - for _, a := range s.InitialState { - init, err := a.BuildFormula() - if err != nil { - return nil, err - } - //f = bf.And(f, init) - formulas = append(formulas, init...) - } - - for _, p := range s.PackageCollection { + for _, p := range s.Wanted { solvable, err := p.BuildFormula() if err != nil { return nil, err @@ -61,7 +72,34 @@ func (s *Solver) BuildFormula() (bf.Formula, error) { formulas = append(formulas, solvable...) } + return bf.And(formulas...), nil +} +func (s *Solver) BuildFormula() (bf.Formula, error) { + //f := bf.True + var formulas []bf.Formula + r, err := s.BuildWorld() + if err != nil { + return nil, err + } + formulas = append(formulas, r) + for _, wanted := range s.Wanted { + encodedW, err := wanted.IsFlagged(true).Encode() + if err != nil { + return nil, err + } + W := bf.Var(encodedW) + for _, installed := range s.Installed { + encodedI, err := installed.IsFlagged(true).Encode() + if err != nil { + return nil, err + } + I := bf.Var(encodedI) + formulas = append(formulas, bf.And(W, I)) + } + + } + //return bf.And(r), nil return bf.And(formulas...), nil } @@ -70,23 +108,51 @@ func (s *Solver) solve(f bf.Formula) (map[string]bool, bf.Formula, error) { if model == nil { return model, f, errors.New("Unsolvable") } + return model, f, nil } func (s *Solver) Apply() (map[string]bool, bf.Formula, error) { f, err := s.BuildFormula() - fmt.Println(f) + fmt.Println("Steps", s.Steps, f) if err != nil { return map[string]bool{}, nil, err } + + // if s.Steps != 0 { + // for i := s.Steps; i >= 0; i-- { + // f, err = s.BuildFormula() + // if err != nil { + // return map[string]bool{}, nil, err + // } + // model, _, err := s.solve(f) + // if err != nil { + // return map[string]bool{}, nil, err + // } + // fmt.Println("Step ", i, model) + + // ass := DecodeModel(model) + // s.Installed = append(s.Installed, ass...) + // } + // } + return s.solve(f) } -func (s *Solver) Solve() ([]pkg.Package, error) { +func (s *Solver) Solve() ([]PackageAssert, error) { model, _, err := s.Apply() if err != nil { - return []pkg.Package{}, err + return nil, err } - ass := DecodeModel(model) - return ass, nil + + // for _, wanted := range s.Wanted { + // encodedW, err := wanted.Encode() + // if err != nil { + // return nil, err + // } + // model[encodedW] = true + // fmt.Println("adding wanted", model) + + // } + return DecodeModel(model) } diff --git a/pkg/solver/solver_test.go b/pkg/solver/solver_test.go index 8767bdde..2db790bf 100644 --- a/pkg/solver/solver_test.go +++ b/pkg/solver/solver_test.go @@ -28,33 +28,31 @@ var _ = Describe("Solver", func() { Context("Simple set", func() { It("Solves correctly", func() { - B := pkg.NewPackage("B", "", []pkg.Package{}, []pkg.Package{}) - A := pkg.NewPackage("A", "", []pkg.Package{B}, []pkg.Package{}) - C := pkg.NewPackage("C", "", []pkg.Package{}, []pkg.Package{}) - C.IsFlagged(true) // installed - - s := NewSolver([]pkg.Package{A.IsFlagged(true)}, []pkg.Package{C}) // XXX: goes fatal with odd numbers of cnf ? + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + s := NewSolver([]pkg.Package{A}, []pkg.Package{C}, []pkg.Package{A, B, C}) // XXX: goes fatal with odd numbers of cnf ? + s.SetSteps(1) solution, err := s.Solve() Expect(err).ToNot(HaveOccurred()) - Expect(solution).To(ContainElement(A.IsFlagged(true))) - Expect(solution).To(ContainElement(B.IsFlagged(true))) - Expect(solution).To(ContainElement(C.IsFlagged(true))) + Expect(solution).To(ContainElement(PackageAssert{Package: A.IsFlagged(true), Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: B.IsFlagged(true), Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: C.IsFlagged(true), Value: true})) }) It("Solves correctly more complex ones", func() { - C := pkg.NewPackage("C", "", []pkg.Package{}, []pkg.Package{}) - D := pkg.NewPackage("D", "", []pkg.Package{}, []pkg.Package{}) - B := pkg.NewPackage("B", "", []pkg.Package{D}, []pkg.Package{}) - A := pkg.NewPackage("A", "", []pkg.Package{B}, []pkg.Package{}) + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) C.IsFlagged(true) // installed - s := NewSolver([]pkg.Package{A.IsFlagged(true)}, []pkg.Package{C}) + s := NewSolver([]pkg.Package{A.IsFlagged(true)}, []pkg.Package{C}, []pkg.Package{A, B, C, D}) solution, err := s.Solve() - Expect(solution).To(ContainElement(C.IsFlagged(true))) - Expect(solution).To(ContainElement(D.IsFlagged(true))) - Expect(solution).To(ContainElement(B.IsFlagged(true))) - + Expect(solution).To(ContainElement(PackageAssert{Package: A.IsFlagged(true), Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: B.IsFlagged(true), Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: D.IsFlagged(true), Value: true})) Expect(err).ToNot(HaveOccurred()) }) @@ -62,19 +60,17 @@ var _ = Describe("Solver", func() { Context("Conflict set", func() { - It("Solves correctly", func() { - C := pkg.NewPackage("C", "", []pkg.Package{}, []pkg.Package{}) + It("is unsolvable - as we something we ask to install conflict with system stuff", func() { + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) // D := pkg.NewPackage("D", "", []pkg.Package{}, []pkg.Package{}) - B := pkg.NewPackage("B", "", []pkg.Package{}, []pkg.Package{C}) - A := pkg.NewPackage("A", "", []pkg.Package{B}, []pkg.Package{}) - C.IsFlagged(true) // installed + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) - s := NewSolver([]pkg.Package{A.IsFlagged(true)}, []pkg.Package{C}) + s := NewSolver([]pkg.Package{A}, []pkg.Package{C}, []pkg.Package{A, B, C}) solution, err := s.Solve() - Expect(solution).To(Equal([]pkg.Package{C})) - - Expect(err).ToNot(HaveOccurred()) + Expect(len(solution)).To(Equal(0)) + Expect(err).To(HaveOccurred()) }) })