|
|
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 }
|