diff --git a/builders.go b/builders.go index 52280b0..d577ae5 100644 --- a/builders.go +++ b/builders.go @@ -3,7 +3,7 @@ package boolgebra // TermBuilder can be used to efficiently append ID ( or Not(ID)) into a big And type TermBuilder struct { isLitFalse bool - m minterm + m Term } // And append a variable to the current term @@ -12,7 +12,7 @@ func (t *TermBuilder) And(id string, val bool) { return } // nothing to do if t.m == nil { - t.m = make(minterm) + t.m = make(Term) } if prev, exists := t.m[id]; exists && prev != val { //attempt to do something like AND(x, !x) which is always false therefore the result will always be Lit(false) @@ -33,5 +33,5 @@ func (t *TermBuilder) Build() Expr { } res := t.m t.m = nil // destroy reference to m to avoid editing it anymore - return res + return Expr{res} } diff --git a/grid/rules.go b/grid/rules.go index 7dd7ea0..c337195 100644 --- a/grid/rules.go +++ b/grid/rules.go @@ -17,8 +17,8 @@ func P(name, value string) Expr { return ID(name + "=" + value) } // // Let's define 'R' an transitive and symetric relation in Values noted `\forall x,y \in Values xRy` // -// 1. `\forall g,h \in Groups² |g| = |h| \and g \inter h = \phi` -// 2. `\forall G \in Groups, \forall v \notin G \exists! w in G vRw` +// 1. `\forall g,h \in Groups² |g| = |h| \and g \inter h = \phi` +// 2. `\forall G \in Groups, \forall v \notin G \exists! w in G vRw` // // groups are defined by to position in the list func Rules(N int, values ...string) Expr { diff --git a/grid/samples_test.go b/grid/samples_test.go index f26e060..d414786 100644 --- a/grid/samples_test.go +++ b/grid/samples_test.go @@ -47,11 +47,11 @@ func ExampleSimplify_logic3x1() { result := Simplify(And(rules, Hint1, Hint2, Hint3)) - if result.Terms() > 1 { - fmt.Printf("There are %d solutions, that's too many\n", result.Terms()) + if len(result) > 1 { + fmt.Printf("There are %d solutions, that's too many\n", len(result)) fmt.Println(Factor(result)) } else { - fmt.Printf("There is %d solution.\n", result.Terms()) + fmt.Printf("There is %d solution.\n", len(result)) } //Output: // There is 1 solution. @@ -95,13 +95,13 @@ func ExampleSimplify_logic4x1() { P(Philippe, R6), ) - if result.Terms() > 1 { - fmt.Printf("There are %d solutions, that's too many\n", result.Terms()) + if len(result) > 1 { + fmt.Printf("There are %d solutions, that's too many\n", len(result)) deduction, rem := Factor(result) fmt.Println(deduction) fmt.Println(rem) } else { - fmt.Printf("There is %d solution.\n", result.Terms()) + fmt.Printf("There is %d solution.\n", len(result)) } //Output: // There is 1 solution. diff --git a/primary.go b/primary.go index 6f5811b..c1b73c3 100644 --- a/primary.go +++ b/primary.go @@ -6,35 +6,34 @@ package boolgebra // Lit returns an Expr equivalent to a boolean literal 'val' func Lit(val bool) Expr { if val { - return minterm{} // true is by definition an empty minterm ( neutral for product) + return Expr{Term{}} // true is by definition an empty minterm ( neutral for product) } else { - return expression{} // false is an empty expression (neutral for sum) + return Expr{} // false is an empty expression (neutral for sum) } } // ID returns an Expr equivalent to a single ID 'id' -func ID(id string) Expr { return minterm{id: true} } +func ID(id string) Expr { return Expr{Term{id: true}} } // Or return the conjunction of all the expression passed in parameter. // // By convention, if 'x' is empty it returns Lit(false). See https://en.wikipedia.org/wiki/Empty_sum func Or(x ...Expr) Expr { // start with the neutral of the Or i.e a false - res := make(expression, 0) + res := make(Expr, 0) // scan all terms, in all expr for _, exp := range x { - for i := 0; i < exp.Terms(); i++ { - t := exp.Term(i) - if t.Is(true) { - return t // + for _, t := range exp { + if t.isLiteral(true) { + return Expr{Term{}} } - if !t.Is(false) { // if this is the literal false, we can just skip it - res = append(res, t.(minterm)) + if !t.isLiteral(false) { // if this is the literal false, we can just skip it + res = append(res, t) } } } if len(res) == 1 { - return res[0] + return Expr{res[0]} } return res } @@ -62,26 +61,24 @@ func And(expressions ...Expr) Expr { // this is the only real case x, y := expressions[0], expressions[1] - if x.Is(false) || y.Is(false) { + if x.isLiteral(false) || y.isLiteral(false) { return Lit(false) } - if x.Is(true) { + if x.isLiteral(true) { return y } - if y.Is(true) { + if y.isLiteral(true) { return x } // general case - z := make(expression, 0, x.Terms()*y.Terms()) + z := make(Expr, 0, len(x)*len(y)) // this is the big one: all terms from x multiplied by terms from y - for i := 0; i < x.Terms(); i++ { - m := x.Term(i).(minterm) + for _, m := range x { product: - for j := 0; j < y.Terms(); j++ { - n := y.Term(j).(minterm) + for _, n := range y { // compute the real m && n , this is basically a merge of all IDs // there is one special case: A & A' = false @@ -93,7 +90,7 @@ func And(expressions ...Expr) Expr { } // basic merge - o := make(minterm) + o := make(Term) for k, v := range m { o[k] = v } @@ -113,12 +110,7 @@ func Not(x Expr) Expr { return x.Not() } // Simplify returns a simpler version of 'x' by applying simplification rules. func Simplify(x Expr) Expr { - switch e := x.(type) { - case expression: - return reduce(e) - default: - return x // unchanged - } + return reduce(x) } // Factor computes the greatest common factor between terms of x @@ -127,30 +119,27 @@ func Simplify(x Expr) Expr { // // x is currently a sum of terms, this function returns f and rem so that // -// x = And(f, rem) -// f.Terms() ==1 : it's a minterm -// +// x = And(f, rem) +// f.Terms() ==1 : it's a minterm func Factor(x Expr) (f, rem Expr) { - var res minterm - for i := 0; i < x.Terms(); i++ { - m := x.Term(i).(minterm) + var res Term + for i, m := range x { if i == 0 { // special case for the first one, need to init the thing res = m } res = inter(res, m) if len(res) == 0 { - return expression{res}, x // empty one + return Expr{res}, x // empty one } } // now for each minterm recompute the reminder - r := expression{} - for i := 0; i < x.Terms(); i++ { - m := x.Term(i).(minterm) + r := Expr{} + for _, m := range x { r = append(r, div(m, res)) } - return expression{res}, r + return Expr{res}, r } diff --git a/primary_test.go b/primary_test.go index 0f9945a..48128cf 100644 --- a/primary_test.go +++ b/primary_test.go @@ -13,10 +13,10 @@ func ExampleID() { // TesLit ensure that the basic true and false are working accordingly with Is(bool) func TestLit(t *testing.T) { - if !Lit(true).Is(true) { + if !Lit(true).isLiteral(true) { t.Error("Lit(true).Is(true) must be true") } - if !Lit(false).Is(false) { + if !Lit(false).isLiteral(false) { t.Error("Lit(false).Is(false) must be true") } } @@ -72,7 +72,7 @@ func ExampleOr() { } func truthTester(t *testing.T, label string, z Expr, expected bool) { - if !z.Is(expected) { + if !z.isLiteral(expected) { t.Errorf("%s: expected %v got %v", label, expected, z) } } diff --git a/quine-mccluskey.go b/quine-mccluskey.go index 0d0eb98..58160b6 100644 --- a/quine-mccluskey.go +++ b/quine-mccluskey.go @@ -2,15 +2,15 @@ package boolgebra // Quine-McCluskey is an algorithm to simplify a sum of prod. -//reduce combine together all minterms of x into prime implicants -func reduce(x expression) expression { +// reduce combine together all minterms of x into prime implicants +func reduce(x Expr) Expr { // to reduce we need to cluster minterms of x into number of non neg ID - var cluster [][]minterm // index minterm by their 1s. store them in a slice - var primes []minterm // also keep primes all together + var cluster [][]Term // index minterm by their 1s. store them in a slice + var primes []Term // also keep primes all together // fill the first cluster - cluster = make([][]minterm, 1+len(x.IDs())) + cluster = make([][]Term, 1+len(x.IDs())) for _, m := range x { ones := positives(m) cluster[ones] = append(cluster[ones], m) @@ -20,7 +20,7 @@ func reduce(x expression) expression { for !emptycluster { // the next cluster will become the current one soon, so we already set the bool to true, because we start with an empty one emptycluster = true // we start with an empty next one, let see if it get filled - next := make([][]minterm, len(cluster)) + next := make([][]Term, len(cluster)) // attempt all possible combinations. // a minterm with n Positive IDs, can only be combined with another one with n or n+1 ( or n-1 but combination is symetric so we don't care) @@ -94,12 +94,12 @@ func reduce(x expression) expression { cluster = next } //done, we now have all the prime implicant - return expression(primes) + return Expr(primes) } // appenunique behave like 'append' except for items in 'terms' that are present in 'set': they // are not appended in this case. -func appendunique(set []minterm, terms ...minterm) []minterm { +func appendunique(set []Term, terms ...Term) []Term { termsloop: for _, m := range terms { for _, x := range set { @@ -118,7 +118,7 @@ termsloop: // x and y must be identical but on exactly one identifier. // // the combined is then then intersection of x and y. -func combine(x, y minterm) (c minterm, ok bool) { +func combine(x, y Term) (c Term, ok bool) { // alg: find out the one and only one difference between x,y // so scan for differences and count. var d string // the identifier that is different (if diffs == 1)) @@ -154,7 +154,7 @@ func combine(x, y minterm) (c minterm, ok bool) { // build c accordingly then // x and y are guaranteed to be identical but on 'd' // so copy x but 'd' - c = make(minterm) + c = make(Term) for k, v := range x { if k != d { c[k] = v @@ -165,7 +165,7 @@ func combine(x, y minterm) (c minterm, ok bool) { } // equals return true if and only if m and n are both minterm, then they are semantically equals -func equals(m, n minterm) bool { +func equals(m, n Term) bool { if len(m) != len(n) { return false } @@ -178,7 +178,7 @@ func equals(m, n minterm) bool { } // positives returns the number of positive identifiers -func positives(m minterm) int { +func positives(m Term) int { count := 0 for _, v := range m { if v { @@ -188,9 +188,9 @@ func positives(m minterm) int { return count } -//inter computes the intersection of x inter y -func inter(x, y minterm) minterm { - res := make(minterm) +// inter computes the intersection of x inter y +func inter(x, y Term) Term { + res := make(Term) for k, v := range x { if w, exists := y[k]; exists && v == w { res[k] = v @@ -200,10 +200,10 @@ func inter(x, y minterm) minterm { } -//div computes x/y i.e z so that And(z,y) = x +// div computes x/y i.e z so that And(z,y) = x // can be seen as x removed from items in y -func div(x, y minterm) minterm { - res := make(minterm) +func div(x, y Term) Term { + res := make(Term) for k, v := range x { if w, exists := y[k]; !exists || v != w { res[k] = v diff --git a/quine-mccluskey_test.go b/quine-mccluskey_test.go index db023a9..34e257e 100644 --- a/quine-mccluskey_test.go +++ b/quine-mccluskey_test.go @@ -2,7 +2,7 @@ package boolgebra import "testing" -//TestReduce check that we get the prime correctly using the wikipedia examples +// TestReduce check that we get the prime correctly using the wikipedia examples func TestReduce(t *testing.T) { /* Number @@ -28,7 +28,7 @@ func TestReduce(t *testing.T) { m11 := m("ab'cd") m14 := m("abcd'") m15 := m("abcd") - x := expression{m4, m8, m9, m10, m12, m11, m14, m15} + x := Expr{m4, m8, m9, m10, m12, m11, m14, m15} //primes are //m4_12 := m("bc'd'") //m8_9_10_11 := m("ab'") @@ -39,9 +39,9 @@ func TestReduce(t *testing.T) { } -//newminterm creates a new minterm using ' at the end of the string to find out that its a neg -func m(x string) minterm { - res := make(minterm) +// newminterm creates a new minterm using ' at the end of the string to find out that its a neg +func m(x string) Term { + res := make(Term) for i, id := range x { nextisquote := i+1 < len(x) && x[i+1] == '\'' @@ -54,7 +54,7 @@ func m(x string) minterm { // TestPosLen make sure that we count the number of true correctly func TestPositives(t *testing.T) { - x := minterm{"A": true, "B": true, "C": false, "E": true} + x := Term{"A": true, "B": true, "C": false, "E": true} if positives(x) != 3 { t.Errorf("invalid minterm %v PosLen attribute got %v want 3", x, positives(x)) } @@ -63,13 +63,13 @@ func TestPositives(t *testing.T) { // TestMinterm_combine gold test some minterm combinations func TestCombine(t *testing.T) { - x := minterm{"A": true, "B": true, "C": false, "E": true} - var y, r, c minterm + x := Term{"A": true, "B": true, "C": false, "E": true} + var y, r, c Term var ok bool // 1,0 -> _ - y = minterm{"A": true, "B": true, "C": false, "E": false} - r = minterm{"A": true, "B": true, "C": false} + y = Term{"A": true, "B": true, "C": false, "E": false} + r = Term{"A": true, "B": true, "C": false} c, ok = combine(x, y) if !ok { t.Errorf("failed to combine(%v,%v)", x, y) @@ -78,8 +78,8 @@ func TestCombine(t *testing.T) { } // 1,_ -> _ - y = minterm{"A": true, "B": true, "C": false} - r = minterm{"A": true, "B": true, "C": false} + y = Term{"A": true, "B": true, "C": false} + r = Term{"A": true, "B": true, "C": false} c, ok = combine(x, y) if !ok { t.Errorf("failed to combine(%v,%v)", x, y) @@ -88,8 +88,8 @@ func TestCombine(t *testing.T) { } // 0,1 -> _ - y = minterm{"A": true, "B": true, "C": true, "E": true} - r = minterm{"A": true, "B": true, "E": true} + y = Term{"A": true, "B": true, "C": true, "E": true} + r = Term{"A": true, "B": true, "E": true} c, ok = combine(x, y) if !ok { t.Errorf("failed to combine(%v,%v)", x, y) @@ -98,8 +98,8 @@ func TestCombine(t *testing.T) { } // 0,_ -> _ - y = minterm{"A": true, "B": true, "E": true} - r = minterm{"A": true, "B": true, "E": true} + y = Term{"A": true, "B": true, "E": true} + r = Term{"A": true, "B": true, "E": true} c, ok = combine(x, y) if !ok { t.Errorf("failed to combine(%v,%v)", x, y) diff --git a/smullyan/counting.go b/smullyan/counting.go index d20e69b..ca423ad 100644 --- a/smullyan/counting.go +++ b/smullyan/counting.go @@ -1,7 +1,9 @@ package smullyan -import "github.com/etnz/permute" -import . "github.com/etnz/boolgebra" +import ( + . "github.com/etnz/boolgebra" + "github.com/etnz/permute" +) //counting.go holds the function relative to counting, like Exactly or AtMost diff --git a/types.go b/types.go index d18d743..830afa7 100644 --- a/types.go +++ b/types.go @@ -7,35 +7,23 @@ import ( ) type ( - // expression is boolean algebra expression as a sum of prod of minterms. + // Expr is boolean algebra Expr as a sum of prod of minterms. // As such it is a slice of minterms. It must be considered as a set // - // an empty expression is always false ( and this is the definition of false + // an empty Expr is always false ( and this is the definition of false // - expression []minterm + Expr []Term - //minterm is a product of identifier or their negation. For instance - // mintern "AB'D" <=> "A or Not(B) or D" is coded as minterm{ "A":true, "B":false, "D":true} + //Term is a product of identifiers or their negation. For instance + // mintern "AB'D" <=> "A or Not(B) or D" is coded as Term{ "A":true, "B":false, "D":true} // - // it is conventional that https://en.wikipedia.org/wiki/Empty_product the empty minterm is 1 the neutral for prod ( and for and too) + // it is conventional that https://en.wikipedia.org/wiki/Empty_product the empty Term is 1 the neutral for prod ( and for and too) // - minterm map[string]bool - - // Expr is the interface that all elements of a boolean algebra share - Expr interface { - String() string - // return the negation of receiver - Not() Expr - // Is return true if the Expr is literally equals to value - Is(val bool) bool - Terms() int - Term(i int) Expr - IDs() (ids map[string]struct{}) - } + Term map[string]bool ) // String return the literal representation (using primary functions) of the current expression. -func (x expression) String() string { +func (x Expr) String() string { if len(x) == 0 { return "Lit(false)" } @@ -54,8 +42,8 @@ func (x expression) String() string { return "Or(" + strings.Join(terms, ", ") + ")" } -//String return the literal representation (using primary functions) of the current minterm -func (m minterm) String() string { +// String return the literal representation (using primary functions) of the current minterm +func (m Term) String() string { if len(m) == 0 { return "Lit(true)" } @@ -81,7 +69,7 @@ func (m minterm) String() string { // NOT -func (x expression) Not() Expr { +func (x Expr) Not() Expr { factors := make([]Expr, 0, len(x)) for _, e := range x { factors = append(factors, e.Not()) @@ -89,16 +77,16 @@ func (x expression) Not() Expr { return And(factors...) } -func (m minterm) Not() Expr { - res := make(expression, 0, len(m)) +func (m Term) Not() Expr { + res := make(Expr, 0, len(m)) for k, v := range m { - res = append(res, minterm{string(k): !v}) + res = append(res, Term{string(k): !v}) } return res } -//Is return true if this expression is equals to val -func (x expression) Is(val bool) bool { +// Is return true if this expression is equals to val +func (x Expr) isLiteral(val bool) bool { if val { return len(x) == 1 && len(x[0]) == 0 } else { @@ -106,35 +94,13 @@ func (x expression) Is(val bool) bool { } } -//Is return true if this expression is equals to val -func (m minterm) Is(val bool) bool { +// Is return true if this expression is equals to val +func (m Term) isLiteral(val bool) bool { return val && len(m) == 0 } -//Terms retuns the number of terms in this expression -func (x expression) Terms() int { return len(x) } - -//Terms retuns the number of terms in this expression -func (m minterm) Terms() int { return 1 } - -//Term retuns the ith terms. Panic if out of bounds ( negative, or >= Terms()) -func (x expression) Term(i int) Expr { - if i < 0 || i >= x.Terms() { - panic("Term is not defined for this index value") - } - return x[i] -} - -//Term retuns the ith terms. Panic if out of bounds ( negative, or >= Terms()) -func (m minterm) Term(i int) Expr { - if i != 0 { - panic("Term is not defined for this index value") - } - return m -} - // IDs return the set of ID in this expression -func (x expression) IDs() (ids map[string]struct{}) { +func (x Expr) IDs() (ids map[string]struct{}) { ids = make(map[string]struct{}) for _, m := range x { for k := range m { @@ -145,7 +111,7 @@ func (x expression) IDs() (ids map[string]struct{}) { } // IDs return the set of ID in this expression -func (m minterm) IDs() (ids map[string]struct{}) { +func (m Term) IDs() (ids map[string]struct{}) { ids = make(map[string]struct{}) for k := range m { ids[k] = struct{}{}