github.com/cznic/mathutil@v0.0.0-20181122101859-297441e03548/poly.go (about)

     1  // Copyright (c) 2016 The mathutil Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mathutil
     6  
     7  import (
     8  	"fmt"
     9  	"math/big"
    10  )
    11  
    12  func abs(n int) uint64 {
    13  	if n >= 0 {
    14  		return uint64(n)
    15  	}
    16  
    17  	return uint64(-n)
    18  }
    19  
    20  // QuadPolyDiscriminant returns the discriminant of a quadratic polynomial in
    21  // one variable of the form a*x^2+b*x+c with integer coefficients a, b, c, or
    22  // an error on overflow.
    23  //
    24  // ds is the square of the discriminant. If |ds| is a square number, d is set
    25  // to sqrt(|ds|), otherwise d is < 0.
    26  func QuadPolyDiscriminant(a, b, c int) (ds, d int, _ error) {
    27  	if 2*BitLenUint64(abs(b)) > IntBits-1 ||
    28  		2+BitLenUint64(abs(a))+BitLenUint64(abs(c)) > IntBits-1 {
    29  		return 0, 0, fmt.Errorf("overflow")
    30  	}
    31  
    32  	ds = b*b - 4*a*c
    33  	s := ds
    34  	if s < 0 {
    35  		s = -s
    36  	}
    37  	d64 := SqrtUint64(uint64(s))
    38  	if d64*d64 != uint64(s) {
    39  		return ds, -1, nil
    40  	}
    41  
    42  	return ds, int(d64), nil
    43  }
    44  
    45  // PolyFactor describes an irreducible factor of a polynomial in one variable
    46  // with integer coefficients P, Q of the form P*x+Q.
    47  type PolyFactor struct {
    48  	P, Q int
    49  }
    50  
    51  // QuadPolyFactors returns the content and the irreducible factors of the
    52  // primitive part of a quadratic polynomial in one variable with integer
    53  // coefficients a, b, c of the form a*x^2+b*x+c in integers, or an error on
    54  // overflow.
    55  //
    56  // If the factorization in integers does not exists, the return value is (0,
    57  // nil, nil).
    58  //
    59  // See also:
    60  // https://en.wikipedia.org/wiki/Factorization_of_polynomials#Primitive_part.E2.80.93content_factorization
    61  func QuadPolyFactors(a, b, c int) (content int, primitivePart []PolyFactor, _ error) {
    62  	content = int(GCDUint64(abs(a), GCDUint64(abs(b), abs(c))))
    63  	switch {
    64  	case content == 0:
    65  		content = 1
    66  	case content > 0:
    67  		if a < 0 || a == 0 && b < 0 {
    68  			content = -content
    69  		}
    70  	}
    71  	a /= content
    72  	b /= content
    73  	c /= content
    74  	if a == 0 {
    75  		if b == 0 {
    76  			return content, []PolyFactor{{0, c}}, nil
    77  		}
    78  
    79  		if b < 0 && c < 0 {
    80  			b = -b
    81  			c = -c
    82  		}
    83  		if b < 0 {
    84  			b = -b
    85  			c = -c
    86  		}
    87  		return content, []PolyFactor{{b, c}}, nil
    88  	}
    89  
    90  	ds, d, err := QuadPolyDiscriminant(a, b, c)
    91  	if err != nil {
    92  		return 0, nil, err
    93  	}
    94  
    95  	if ds < 0 || d < 0 {
    96  		return 0, nil, nil
    97  	}
    98  
    99  	x1num := -b + d
   100  	x1denom := 2 * a
   101  	gcd := int(GCDUint64(abs(x1num), abs(x1denom)))
   102  	x1num /= gcd
   103  	x1denom /= gcd
   104  
   105  	x2num := -b - d
   106  	x2denom := 2 * a
   107  	gcd = int(GCDUint64(abs(x2num), abs(x2denom)))
   108  	x2num /= gcd
   109  	x2denom /= gcd
   110  
   111  	return content, []PolyFactor{{x1denom, -x1num}, {x2denom, -x2num}}, nil
   112  }
   113  
   114  // QuadPolyDiscriminantBig returns the discriminant of a quadratic polynomial
   115  // in one variable of the form a*x^2+b*x+c with integer coefficients a, b, c.
   116  //
   117  // ds is the square of the discriminant. If |ds| is a square number, d is set
   118  // to sqrt(|ds|), otherwise d is nil.
   119  func QuadPolyDiscriminantBig(a, b, c *big.Int) (ds, d *big.Int) {
   120  	ds = big.NewInt(0).Set(b)
   121  	ds.Mul(ds, b)
   122  	x := big.NewInt(4)
   123  	x.Mul(x, a)
   124  	x.Mul(x, c)
   125  	ds.Sub(ds, x)
   126  
   127  	s := big.NewInt(0).Set(ds)
   128  	if s.Sign() < 0 {
   129  		s.Neg(s)
   130  	}
   131  
   132  	if s.Bit(1) != 0 { // s is not a square number
   133  		return ds, nil
   134  	}
   135  
   136  	d = SqrtBig(s)
   137  	x.Set(d)
   138  	x.Mul(x, x)
   139  	if x.Cmp(s) != 0 { // s is not a square number
   140  		d = nil
   141  	}
   142  	return ds, d
   143  }
   144  
   145  // PolyFactorBig describes an irreducible factor of a polynomial in one
   146  // variable with integer coefficients P, Q of the form P*x+Q.
   147  type PolyFactorBig struct {
   148  	P, Q *big.Int
   149  }
   150  
   151  // QuadPolyFactorsBig returns the content and the irreducible factors of the
   152  // primitive part of a quadratic polynomial in one variable with integer
   153  // coefficients a, b, c of the form a*x^2+b*x+c in integers.
   154  //
   155  // If the factorization in integers does not exists, the return value is (nil,
   156  // nil).
   157  //
   158  // See also:
   159  // https://en.wikipedia.org/wiki/Factorization_of_polynomials#Primitive_part.E2.80.93content_factorization
   160  func QuadPolyFactorsBig(a, b, c *big.Int) (content *big.Int, primitivePart []PolyFactorBig) {
   161  	content = bigGCD(bigAbs(a), bigGCD(bigAbs(b), bigAbs(c)))
   162  	switch {
   163  	case content.Sign() == 0:
   164  		content.SetInt64(1)
   165  	case content.Sign() > 0:
   166  		if a.Sign() < 0 || a.Sign() == 0 && b.Sign() < 0 {
   167  			content = bigNeg(content)
   168  		}
   169  	}
   170  	a = bigDiv(a, content)
   171  	b = bigDiv(b, content)
   172  	c = bigDiv(c, content)
   173  
   174  	if a.Sign() == 0 {
   175  		if b.Sign() == 0 {
   176  			return content, []PolyFactorBig{{big.NewInt(0), c}}
   177  		}
   178  
   179  		if b.Sign() < 0 && c.Sign() < 0 {
   180  			b = bigNeg(b)
   181  			c = bigNeg(c)
   182  		}
   183  		if b.Sign() < 0 {
   184  			b = bigNeg(b)
   185  			c = bigNeg(c)
   186  		}
   187  		return content, []PolyFactorBig{{b, c}}
   188  	}
   189  
   190  	ds, d := QuadPolyDiscriminantBig(a, b, c)
   191  	if ds.Sign() < 0 || d == nil {
   192  		return nil, nil
   193  	}
   194  
   195  	x1num := bigAdd(bigNeg(b), d)
   196  	x1denom := bigMul(_2, a)
   197  	gcd := bigGCD(bigAbs(x1num), bigAbs(x1denom))
   198  	x1num = bigDiv(x1num, gcd)
   199  	x1denom = bigDiv(x1denom, gcd)
   200  
   201  	x2num := bigAdd(bigNeg(b), bigNeg(d))
   202  	x2denom := bigMul(_2, a)
   203  	gcd = bigGCD(bigAbs(x2num), bigAbs(x2denom))
   204  	x2num = bigDiv(x2num, gcd)
   205  	x2denom = bigDiv(x2denom, gcd)
   206  
   207  	return content, []PolyFactorBig{{x1denom, bigNeg(x1num)}, {x2denom, bigNeg(x2num)}}
   208  }
   209  
   210  func bigAbs(n *big.Int) *big.Int {
   211  	n = big.NewInt(0).Set(n)
   212  	if n.Sign() >= 0 {
   213  		return n
   214  	}
   215  
   216  	return n.Neg(n)
   217  }
   218  
   219  func bigDiv(a, b *big.Int) *big.Int {
   220  	a = big.NewInt(0).Set(a)
   221  	return a.Div(a, b)
   222  }
   223  
   224  func bigGCD(a, b *big.Int) *big.Int {
   225  	a = big.NewInt(0).Set(a)
   226  	b = big.NewInt(0).Set(b)
   227  	for b.Sign() != 0 {
   228  		c := big.NewInt(0)
   229  		c.Mod(a, b)
   230  		a, b = b, c
   231  	}
   232  	return a
   233  }
   234  
   235  func bigNeg(n *big.Int) *big.Int {
   236  	n = big.NewInt(0).Set(n)
   237  	return n.Neg(n)
   238  }
   239  
   240  func bigMul(a, b *big.Int) *big.Int {
   241  	r := big.NewInt(0).Set(a)
   242  	return r.Mul(r, b)
   243  }
   244  
   245  func bigAdd(a, b *big.Int) *big.Int {
   246  	r := big.NewInt(0).Set(a)
   247  	return r.Add(r, b)
   248  }