Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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}
}
4 changes: 2 additions & 2 deletions grid/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 6 additions & 6 deletions grid/samples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
63 changes: 26 additions & 37 deletions primary.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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

}
6 changes: 3 additions & 3 deletions primary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to adjust the error message

}
if !Lit(false).Is(false) {
if !Lit(false).isLiteral(false) {
t.Error("Lit(false).Is(false) must be true")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem

}
}
Expand Down Expand Up @@ -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)
}
}
Expand Down
36 changes: 18 additions & 18 deletions quine-mccluskey.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading