package circuitcompiler import ( "fmt" "math/big" "sort" "strings" ) type factors []*factor type factor struct { typ Token name string invert, negate bool multiplicative [2]int } func (f factors) Len() int { return len(f) } func (f factors) Swap(i, j int) { f[i], f[j] = f[j], f[i] } func (f factors) Less(i, j int) bool { if strings.Compare(f[i].String(), f[j].String()) < 0 { return false } return true } func (f factor) String() string { if f.typ == CONST { return fmt.Sprintf("(const fac: %v)", f.multiplicative) } str := f.name if f.invert { str += "^-1" } if f.negate { str = "-" + str } return fmt.Sprintf("(\"%s\" fac: %v)", str, f.multiplicative) } func (f factors) clone() (res factors) { res = make(factors, len(f)) for k, v := range f { res[k] = &factor{multiplicative: v.multiplicative, typ: v.typ, name: v.name, invert: v.invert, negate: v.negate} } return } func (f factors) normalizeAll() { for i, _ := range f { f[i].multiplicative = normalizeFactor(f[i].multiplicative) } } // find Least Common Multiple (LCM) via GCD func LCMsmall(a, b int) int { result := a * b / GCD(a, b) return result } func extractFactor(f factors) (factors, [2]int) { lcm := f[0].multiplicative[1] for i := 1; i < len(f); i++ { lcm = LCMsmall(f[i].multiplicative[1], lcm) } for i := 0; i < len(f); i++ { f[i].multiplicative[0] = (lcm / f[i].multiplicative[1]) * f[i].multiplicative[0] } gcd := f[0].multiplicative[0] for i := 1; i < len(f); i++ { gcd = GCD(f[i].multiplicative[0], gcd) } for i := 0; i < len(f); i++ { f[i].multiplicative[0] = f[i].multiplicative[0] / gcd f[i].multiplicative[1] = 1 } return f, [2]int{gcd, lcm} } func factorsSignature(leftFactors, rightFactors factors) (sig MultiplicationGateSignature, extractedLeftFactors, extractedRightFactors factors) { leftFactors = leftFactors.clone() rightFactors = rightFactors.clone() leftFactors.normalizeAll() var extractedFacLeft [2]int leftFactors, extractedFacLeft = extractFactor(leftFactors) sort.Sort(leftFactors) hasher.Reset() for _, fac := range leftFactors { hasher.Write([]byte(fac.String())) } leftNum := new(big.Int).SetBytes(hasher.Sum(nil)) rightFactors.normalizeAll() var extractedFacRight [2]int rightFactors, extractedFacRight = extractFactor(rightFactors) sort.Sort(rightFactors) hasher.Reset() for _, fac := range rightFactors { hasher.Write([]byte(fac.String())) } rightNum := new(big.Int).SetBytes(hasher.Sum(nil)) //we did all this, because multiplication is commutativ, and we want the signature of a //mulitplication gate factorsSignature(a,b) == factorsSignature(b,a) leftNum.Add(leftNum, rightNum) res := normalizeFactor(mul2DVector(extractedFacLeft, extractedFacRight)) return MultiplicationGateSignature{identifier: leftNum.String()[:16], commonExtracted: res}, leftFactors, rightFactors } func lengthOfLongestSlice(a, b factors) int { if len(a) > len(b) { return len(a) } return len(b) } //multiplies factor elements and returns the result //in case the factors do not hold any constants and all inputs are distinct, the output will be the concatenation of left+right func mulFactors(leftFactors, rightFactors factors) (result factors) { if len(leftFactors) < len(rightFactors) { tmp := leftFactors leftFactors = rightFactors rightFactors = tmp } for i, left := range leftFactors { for _, right := range rightFactors { if left.typ == CONST && right.typ == IN { leftFactors[i] = &factor{typ: IN, name: right.name, negate: Xor(left.negate, right.negate), invert: right.invert, multiplicative: mul2DVector(right.multiplicative, left.multiplicative)} continue } if right.typ == CONST && left.typ == IN { leftFactors[i] = &factor{typ: IN, name: left.name, negate: Xor(left.negate, right.negate), invert: left.invert, multiplicative: mul2DVector(right.multiplicative, left.multiplicative)} continue } if right.typ&left.typ == CONST { leftFactors[i] = &factor{typ: CONST, negate: Xor(right.negate, left.negate), multiplicative: mul2DVector(right.multiplicative, left.multiplicative)} continue } //tricky part here //this one should only be reached, after a true mgate had its left and right braches computed. here we //a factor can appear at most in quadratic form. we reduce terms a*a^-1 here. if right.typ&left.typ == IN { if left.name == right.name { if right.invert != left.invert { leftFactors[i] = &factor{typ: CONST, negate: Xor(right.negate, left.negate), multiplicative: mul2DVector(right.multiplicative, left.multiplicative)} continue } } //rightFactors[i] = factor{typ: CONST, negate: Xor(facRight.negate, facLeft.negate), multiplicative: mul2DVector(facRight.multiplicative, facLeft.multiplicative)} //continue } panic("unexpected. If this errror is thrown, its probably brcause a true multiplication gate has been skipped and treated as on with constant multiplication or addition ") } } return leftFactors } //returns the absolute value of a signed int and a flag telling if the input was positive or not //this implementation is awesome and fast (see Henry S Warren, Hackers's Delight) func abs(n int) (val int, positive bool) { y := n >> 63 return (n ^ y) - y, y == 0 } //adds two factors to one iff they are both are constants or of the same variable func addFactor(facLeft, facRight factor) (couldAdd bool, sum factor) { if facLeft.typ&facRight.typ == CONST { var a0, b0 = facLeft.multiplicative[0], facRight.multiplicative[0] if facLeft.negate { a0 *= -1 } if facRight.negate { b0 *= -1 } absValue, positive := abs(a0*facRight.multiplicative[1] + facLeft.multiplicative[1]*b0) return true, factor{typ: CONST, negate: !positive, multiplicative: [2]int{absValue, facLeft.multiplicative[1] * facRight.multiplicative[1]}} } if facLeft.typ&facRight.typ == IN && facLeft.invert == facRight.invert && facLeft.name == facRight.name { var a0, b0 = facLeft.multiplicative[0], facRight.multiplicative[0] if facLeft.negate { a0 *= -1 } if facRight.negate { b0 *= -1 } absValue, positive := abs(a0*facRight.multiplicative[1] + facLeft.multiplicative[1]*b0) return true, factor{typ: IN, invert: facRight.invert, name: facRight.name, negate: !positive, multiplicative: [2]int{absValue, facLeft.multiplicative[1] * facRight.multiplicative[1]}} } return false, factor{} } //returns the reduced sum of two input factor arrays //if no reduction was done (worst case), it returns the concatenation of the input arrays func addFactors(leftFactors, rightFactors factors) factors { var found bool res := make(factors, 0, len(leftFactors)+len(rightFactors)) for _, facLeft := range leftFactors { found = false for i, facRight := range rightFactors { var sum factor found, sum = addFactor(*facLeft, *facRight) if found { rightFactors[i] = &sum break } } if !found { res = append(res, facLeft) } } for _, val := range rightFactors { if val.multiplicative[0] != 0 { res = append(res, val) } } return res } // -4/-5 -> 4/5 ; 5/-7 -> -5/7 ; 6 /2 -> 3 / 1 func normalizeFactor(b [2]int) [2]int { resa, signa := abs(b[0]) resb, signb := abs(b[1]) gcd := GCD(resa, resb) if Xor(signa, signb) { resa = -resa } return [2]int{resa / gcd, resb / gcd} } //naive component multiplication func mul2DVector(a, b [2]int) [2]int { return [2]int{a[0] * b[0], a[1] * b[1]} } // find Least Common Multiple (LCM) via GCD func LCM(a, b int, integers ...int) int { result := a * b / GCD(a, b) for i := 0; i < len(integers); i++ { result = LCM(result, integers[i]) } return result } //euclidean algo to determine greates common divisor func GCD(a, b int) int { for b != 0 { t := b b = a % b a = t } return a }