├── .travis.yml ├── CPimport.txt ├── ECDSA.py ├── LICENSE ├── NumbThy.pdf ├── Readme.md ├── ellipticcurve.py ├── finitefield.py ├── gaussint.py ├── numbthy.py └── test_numbthy_unittest.py /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | matrix: 3 | include: 4 | - python: 2.6 # Still in use, but not avail on xenial (ubuntu 16.04, now used by Travis CI) 5 | dist: trusty 6 | - python: 2.7 # Most recent (and final) Python 2.x 7 | dist: xenial 8 | - python: 3.3 9 | dist: trusty 10 | - python: 3.7 # Latest python 11 | dist: xenial 12 | script: python test_numbthy_unittest.py 13 | -------------------------------------------------------------------------------- /ECDSA.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # ECDSA Signature 3 | # Python implementation (needs Python EllipticCurve module) 4 | # Author: Robert Campbell, 5 | # Date: 1 June, 2014 6 | # Version 0.3 7 | # License: Simplified BSD (see details at bottom) 8 | ###################################################################################### 9 | # Refs: 10 | # NIST FIPS 186-4, March 2013 (and earlier versions to 186-2) 11 | # ECDSA Test Vectors [http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/ECDSA_Prime.pdf] 12 | ###################################################################################### 13 | 14 | import hashlib, binascii, random 15 | 16 | class ECDSA(object): 17 | """ECDSA Signatures 18 | Usage: 19 | >>> from ellipticcurve import * 20 | >>> from ellipticcurve import * 21 | >>> theECDSA = ECDSA(256,d=0xDEADBEEF) # d = 0xDEADBEEF private key 22 | Q = d*G = [0xB487...8394, 0x2A12...CE5E] 23 | >>> themsg = "Blah, blah" 24 | >>> [r,s] = theECDSA.sign(themsg,k=0xF00F00D00F) # Explicit signing nonce k 25 | (r,s) = [0xFF99...4ECB1, 0xDA3F...FB81] 26 | >>> theECDSA.verify(themsg,[r,s]) 27 | """ 28 | 29 | def __init__(self,curve=384,d=None,verbose=True): # Generate signing key 30 | self.verbose = verbose 31 | if (curve == 384): 32 | [E,G,n,thehash,numbits] = self.define384() 33 | elif (curve == 256): 34 | [E,G,n,thehash,numbits] = self.define256() 35 | elif (curve == 192): 36 | [E,G,n,thehash,numbits] = self.define192() 37 | else: 38 | raise ValueError("***** Error *****: curve parameter must be 192, 256 or 384, not {0}".format(curve)) 39 | self.numbits = numbits # For verbose output formatting 40 | self.E = E # The elliptic curve - E:y^2 = x^3 - 3x + b 41 | # SAGE: self.p = E.base().characteristic() # Mod p (SAGE specific) 42 | # SAGE: self.b = self.p - E.defining_polynomial().coefficients()[-1] # (SAGE specific) 43 | # SAGE: self.n = E.cardinality() # Order of the cyclic elliptic curve group (a prime) 44 | self.p = E.prime # Mod p (Python class) 45 | self.b = self.p - E.b # (Python class specific) 46 | self.n = E.order # Order of the cyclic elliptic curve group (a prime) (Python class specific) 47 | self.G = G # Basepoint on E with order n 48 | self.thehash = thehash # Hash function used 49 | if (d is None): # Generate a random secret key if none is provided 50 | d = random.randint(1,n) 51 | self.d = d 52 | if (verbose): print " Private Key: d = 0x{0:0{1}X}".format(self.d,numbits/4) 53 | self.Q = (self.d)*(self.G) 54 | if (verbose): print " Public Key: Q = d*G = [0x{0:0{1}X}, 0x{2:0{3}X}]".format( 55 | Integer(self.Q[0]),numbits/4,Integer(self.Q[1]),numbits/4) 56 | 57 | def sign(self,msg,k=None): 58 | """Generate an ECDSA signature for the message provided. 59 | Usage: 60 | sage: themsg = "Example of ECDSA with P-384" 61 | sage: [r,s] = theECDSA.sign(themsg) # ECDSA signature 62 | sage: [r,s] = theECDSA.sign(themsg,r=0xA9876543210) # ECDSA signature w/ explicit nonce 63 | """ 64 | verbose = self.verbose 65 | if (k is None): 66 | k = random.randint(1,self.n) 67 | if (verbose): print " k = 0x{0:0{1}X}".format(k,self.numbits/4) 68 | r = int((k*(self.G))[0]) # x-coord of point k*G 69 | if (verbose): print " r = (k*G)_x = 0x{0:0{1}X}".format(r,self.numbits/4) 70 | z = int(self.thehash(msg).hexdigest(),16) 71 | if (verbose): print " z = hash(msg) = 0x{0:0{1}X}".format(z,self.numbits/4) 72 | kinv = int(inverse_mod(k,self.n)) 73 | if (verbose): print " kinv = 1/k (mod n) = 0x{0:0{1}X}".format(kinv,self.numbits/4) 74 | s = int(mod((z+r*self.d)*kinv,self.n)) 75 | if (verbose): print " s = kinv*(z+r*d) (mod n) = 0x{0:0{1}X}".format(s,self.numbits/4) 76 | if (verbose): print " Signature: (r,s) = [0x{0:0{1}X}, 0x{2:0{3}X}]".format(r,self.numbits/4,s,self.numbits/4) 77 | return [r,s] 78 | 79 | def verify(self,msg,thesign): 80 | """Verify an ECDSA signature for the message. 81 | Usage: 82 | sage: themsg = "" 83 | sage: theECDSA.verify(themsg,[r,s]) 84 | """ 85 | verbose = self.verbose 86 | [r,s] = thesign 87 | if (verbose): print " Verify Signature: (r,s) = [0x{0:0{1}X}, 0x{2:0{3}X}]".format(r,self.numbits/4,s,self.numbits/4) 88 | z = int(self.thehash(msg).hexdigest(),16) 89 | if (verbose): print " z = hash(msg) = 0x{0:0{1}X}".format(z,self.numbits/4) 90 | w = int(inverse_mod(s,self.n)) 91 | if (verbose): print " w = 1/s (mod n) = 0x{0:0{1}X}".format(w,self.numbits/4) 92 | u1 = int(mod(z*w,self.n)) 93 | if (verbose): print " u1 = z*w (mod n) = 0x{0:0{1}X}".format(u1,self.numbits/4) 94 | u2 = int(mod(r*w,self.n)) 95 | if (verbose): print " u2 = r*w (mod n) = 0x{0:0{1}X}".format(u2,self.numbits/4) 96 | # SAGE: rprime = int(mod((u1*(self.G)+u2*(self.Q))[0],self.n)) # x-coord of point (u1*G+u2*Q) 97 | rprime = int(mod((u1*(self.G)+u2*(self.Q)).x,self.n)) # x-coord of point (u1*G+u2*Q) 98 | if (verbose): print " r' = (u1*G + u2*Q)_x = 0x{0:0{1}X}".format(rprime,self.numbits/4) 99 | if (r == rprime): 100 | if (verbose): print " Signature verifies" 101 | return True 102 | else: 103 | if (verbose): print " Signature FAILS to verify" 104 | return False 105 | 106 | ############################################################################################## 107 | # NIST P-192/256/384 Elliptic Curves 108 | # [http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf] 109 | # [http://www.secg.org/collateral/sec2_final.pdf] 110 | # FIPS 186-2, pg 29 [http://csrc.nist.gov/publications/fips/archive/fips186-2/fips186-2.pdf] 111 | # FIPS 186-4, Sect D.1.2.1, pg 90 [http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf] 112 | ############################################################################################## 113 | def define192(self): 114 | p = 2**192 - 2**64 - 1 # 6277101735386680763835789423207666416083908700390324961279 115 | b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 116 | n = 6277101735386680763835789423176059013767194773182842284081 117 | E = EllipticCurve(GF(p),[-3,b]) 118 | #SAGE: E.set_order(n) # Set the pre-computed curve order 119 | E.order = n # Set the pre-computed curve order 120 | gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 121 | gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 122 | g = E.point([gx,gy]) # The basepoint 123 | return [E,g,n,hashlib.sha1,192] 124 | 125 | def define256(self): 126 | p = 2**256 - 2**224 + 2**192 + 2**96 - 1 # 115792089210356248762697446949407573530086143415290314195533631308867097853951 127 | b = 41058363725152142129326129780047268409114441015993725554835256314039467401291 128 | n = 115792089210356248762697446949407573529996955224135760342422259061068512044369 129 | E = EllipticCurve(GF(p),[-3,b]) 130 | E.set_order(n) # Set the pre-computed curve order 131 | gx = 48439561293906451759052585252797914202762949526041747995844080717082404635286 132 | gy = 36134250956749795798585127919587881956611106672985015071877198253568414405109 133 | g = E.point([gx,gy]) # The basepoint 134 | return [E,g,n,hashlib.sha256,256] 135 | 136 | def define384(self): 137 | p = 2**384 - 2**128 - 2**96 + 2**32 - 1 # 3940...2319 138 | b = 27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 139 | n = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 140 | E = EllipticCurve(GF(p),[-3,b]) 141 | E.set_order(n) # Set the pre-computed curve order 142 | gx = 26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087 143 | gy = 8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871 144 | g = E.point([gx,gy]) # The basepoint 145 | return [E,g,n,hashlib.sha384,384] 146 | 147 | ############################################################################ 148 | # License: Freely available for use, abuse and modification 149 | # (this is the Simplified BSD License, aka FreeBSD license) 150 | # Copyright 2014 Robert Campbell. All rights reserved. 151 | # 152 | # Redistribution and use in source and binary forms, with or without 153 | # modification, are permitted provided that the following conditions are met: 154 | # 155 | # 1. Redistributions of source code must retain the above copyright notice, 156 | # this list of conditions and the following disclaimer. 157 | # 158 | # 2. Redistributions in binary form must reproduce the above copyright 159 | # notice, this list of conditions and the following disclaimer in 160 | # the documentation and/or other materials provided with the distribution. 161 | ############################################################################ 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2001-2018, Robert Campbell 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /NumbThy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robert-Campbell-256/Number-Theory-Python/638f41a4d6c32c8e610536e030a2e8be2a259ac2/NumbThy.pdf -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Number Theory Python 2 | A collection of Python modules implementing number theory stuff. 3 | 4 | The Number-Theory-Python package currently includes: 5 | * NumbThy.pdf: A short course in number theory 6 | * numbthy.py: Basic number theory functions 7 | * gaussint.py: Basic operations over the Gaussian integers 8 | * finitefield.py: Finite fields of prime power order 9 | * CPimport.txt: Data file of finite field defining polynomials 10 | * ellipticcurve.py: Elliptic curves in affine reduced Weierstrass form over prime order fields 11 | * ECDSA.py: Elliptic curve signatures 12 | 13 | Functions implemented in numbthy.py are: 14 | * gcd(a,b) - Compute the greatest common divisor of a and b. 15 | * xgcd(a,b) - Find [g,x,y] such that g=gcd(a,b) and g = ax + by. 16 | * power_mod(b,e,n) - Compute b^e mod n efficiently. 17 | * inverse_mod(b,n) - Compute 1/b mod n. 18 | * is_prime(n) - Test whether n is prime using a variety of pseudoprime tests. 19 | * euler_criterion(a, p) - Test whether a is a quadratic residue mod p. 20 | * euler_phi(n) - Compute Euler's Phi function of n - the number of integers strictly less than n which are coprime to n. 21 | * carmichael_lambda(n) - Compute Carmichael's Lambda function of n - the smallest exponent e such that b\*\*e = 1 for all b coprime to n. 22 | * factor(n) - Return a sorted list of the prime factors of n with exponents. 23 | * prime_divisors(n) - Returns a sorted list of the prime divisors of n. 24 | * is_primitive_root(g,n) - Test whether g is primitive - generates the group of units mod n. 25 | * sqrtmod(a,n) - Compute sqrt(a) mod n using various algorithms. 26 | * TSRsqrtmod(a,grpord,n) - Compute sqrt(a) mod n using Tonelli-Shanks-RESSOL algorithm. 27 | 28 | Classes implemented in finitefield.py are: 29 | * FiniteField(p,polycoeffs) is the finite field of characteristic p and given defining polynomial. The methods defined for this class are: 30 | * str(ff), format(ff) and repr(ff) - also implicitly used by print and display functions 31 | * Coerce integer or array of integers to element 32 | * Iterator over all elements of the finite field 33 | * ff.random() - Random element of the finite field 34 | * If a defining polynomial is not specified, the Conway polynomial is used 35 | * FiniteFieldElt(ff,polycoeffs) is the element in the specified finite field. The methods defined for this class are: 36 | * str(elt), format(elt) and repr(elt) - also implicitly used by print and display functions 37 | * elt1.add(elt2) - Add two elements - also overloads the + operator, so elt1 + elt2 (and elt1 += elt2) 38 | * elt1.mul(elt2) - Multiply two elements - also overloads the * operator, so elt1 * elt2 (and elt1 *= elt2) 39 | * elt1.neg() - Additive inverse (aka negative) of an element - also overloads the unary - operator, so -elt1 40 | * elt1.inv() - Multiplicative inverse of an element 41 | * elt1.div(elt2) - Divide two elements - also overloads the / operator, so elt1 / elt2 (and elt1 /= elt2) 42 | * elt1.pow(n) - The nth power of an element - also overloads the ** operator, so elt1**n 43 | * elt1.is_primitive() - Does elt1 generate the multiplicative group 44 | * elt1.order() - Compute the multiplicative order of elt1 45 | 46 | Classes implemented in ellipticcurve.py are: 47 | * EllipticCurve(p,[a,b]) is the elliptic curve in Weierstrass form y^2 = x^3 +ax + b (mod p) over the finite field of prime order p. The methods defined for this class are: 48 | * str(ec), format(ec) and repr(ec) - also implicitly used by print and display functions 49 | * EllipticCurveElt(ec,[x,y]) is a point on the specified elliptic curve. The methods defined for this class are: 50 | * str(pt), format(pt) and repr(pt) - also implicitly used by print and display functions 51 | * pt1.add(pt2) - Add two points - also overloads the + operator, so pt1 + pt2 (and pt1 += pt2) 52 | * pt1.neg() - Additive inverse (aka negative) of a point - also overloads the unary - operator, so -pt1 53 | * pt1.mult(n) - The nth multiple of a point - also overloads the * operator, so n*pt1 54 | 55 | Classes implemented in ECDSA.py are: 56 | * ECDSA(size,d=key) defines signatures over the NIST curve of size bits, using the optional value d as a private key (otherwise it is randomly generated). The methods defined for this class are: 57 | * ecdsa.sign(msg,k=nonce) - Signs the message string, using the optional value k as a signing nonce (othewise it is randomly generated). 58 | * ecdsa.verify(msg,thesign) - Verifies that thesign is a valid signature for the string msg. 59 | 60 | Classes implemented in gaussint.py are: 61 | * GaussInt(a,b) is the Gaussian integer a + bI, where a and b are integers. The methods defined for this class are: 62 | * str(ff) and repr(ff) - also implicitly used by print and display functions 63 | * Coerce integer or complex to element 64 | * elt1.add(elt2) - Add two elements - also overloads the + operator, so elt1 + elt2 (and elt1 += elt2) 65 | * elt1.mult(elt2) - Multiply two elements - also overloads the * operator, so elt1 * elt2 (and elt1 *= elt2) 66 | * elt1.neg() - Additive inverse (aka negative) of an element - also overloads the unary - operator, so -elt1 67 | * elt1.div(elt2) - Divide two elements - also overloads the / operator, so elt1 / elt2 (and elt1 /= elt2) 68 | * elt1.mod(elt2) - Reduce one element mod another - also overloads the % operator, so elt1 % elt2 (and elt1 %= elt2) 69 | * elt1.divmod(elt2) - Divide two elements, returning both quotient and remainder 70 | * elt1.gcd(elt2) - The gcd of two elements 71 | * elt1.xgcd(elt2) - The extended gcd of two elements 72 | * elt1.pow(n) - The nth power of an element - also overloads the ** operator, so elt1**n 73 | * elt1.isprime() - Tests whether elt1 is prime (as a Gaussian integer) 74 | * elt1.factors() - Return a list of the prime factors of elt1 75 | * elt1.factor() - Return a single prime factor of elt1 76 | 77 | -------------------------------------------------------------------------------- /ellipticcurve.py: -------------------------------------------------------------------------------- 1 | ###################################################################################### 2 | # Elliptic Curve 3 | # Elliptic curves in reduced Weierstrass form over prime order fields 4 | # Author: Robert Campbell, 5 | # Date: 17 Feb, 2018 6 | # Version 0.26 7 | # License: Simplified BSD (see details at bottom) 8 | ###################################################################################### 9 | """Elliptic Curve 10 | EllipticCurve(p,[a,b]) is the elliptic curve in affine restricted Weierstrass form 11 | y^2 = x^3 +ax + b (mod p) 12 | Usage: 13 | >>> from ellipticcurve import * 14 | >>> ec29 = EllipticCurve(29,[4,20]); ec29 15 | y^2 = x^3 + 4x + 20 (mod 29) 16 | >>> pt = EllipticCurveElt(ec29,[2,6]); pt 17 | (2, 6) 18 | >>> 7*pt 19 | (3, 28) 20 | >>> 37*pt 21 | (Infinity, Infinity) 22 | >>> pt1 = 9*pt; pt1 - pt 23 | (15, 27) 24 | >>> '{0:f}'.format(ec29) # Full format 25 | 'EllipticCurve(29, (4,20))' 26 | """ 27 | 28 | __version__ = '0.26' # Format specified in Python PEP 396 29 | Version = 'ELLIPTICCURVE.PY, version ' + __version__ + ', 17 Feb, 2018, by Robert Campbell, ' 30 | 31 | import numbthy # For xgcd (for modinv) and sqrtmod 32 | import random # Generate random elements 33 | import sys # Check Python2 or Python3 34 | import math # For sqrt 35 | 36 | # Assumptions: Affine (later Projective?) Reduced Weierstrass form 37 | # over prime field. Thus identity is point at infinity, 38 | # and -P is gotten from P by negating the y value. 39 | # y^2 = x^3 + ax + b (mod p) 40 | # Refs: 41 | # [HMV04] Guide to Elliptic Curve Cryptography by Hankerson, Menezes & Vanstone, 2004 42 | # (see in particular, Sects 3.1.1 & 3.1.2) 43 | # [Wash03] Elliptic Curves, L. Washington, 2003 (see Sect 2.2) 44 | # [Many, many other decent references] 45 | # Addition Rules: 46 | # i) 0 + P = P 47 | # ii) P + (-P) = 0 [i.e. x1==x2 but y1==-y2] 48 | # iii) P + P [i.e. x1==x2 and y1==y2] 49 | # lambda = (3x^2+a)/2y # "tangent slope" 50 | # x3 = lambda^2 - 2x 51 | # y3 = lambda*(x-x3) - y 52 | # iv) P1 + P2 [i.e. x1!=x2] 53 | # lambda = (y1-y2)/(x1-x2) # "slope" 54 | # x3 = lambda^2 - x1 - x2 55 | # y3 = lambda*(x1-x3) - y1 56 | # Zero point (aka point at infinity) represented as ["Infinity","Infinity"] 57 | 58 | class EllipticCurve(object): 59 | """Elliptic Curve 60 | EllipticCurve(p,[a,b]) is the elliptic curve in affine restricted Weierstrass form 61 | y^2 = x^3 +ax + b (mod p) 62 | Usage: 63 | >>> from ellipticcurve import * 64 | >>> ec29 = EllipticCurve(29,[4,20]); ec29 65 | y^2 = x^3 + 4x + 20 (mod 29) 66 | >>> pt = EllipticCurveElt(ec29,[2,6]); pt 67 | (2, 6) 68 | >>> 7*pt 69 | (3, 28) 70 | >>> 37*pt 71 | (Infinity, Infinity) 72 | >>> pt1 = 9*pt; pt1 - pt 73 | (15, 27) 74 | >>> '{0:f}'.format(ec29) # Full format 75 | 'EllipticCurve(29, (4,20))' 76 | """ 77 | def __init__(self,prime,coeffs,fmtspec="s"): 78 | self.prime = prime 79 | if(not(numbthy.isprime(self.prime))): raise ValueError("***** Error *****: Characteristic of base field {0} must be prime".format(self.prime)) 80 | self.a = coeffs[0] 81 | self.b = coeffs[1] 82 | self.discriminant = -16*(4*(self.a**3)+27*(self.b**2)) % self.prime 83 | if(self.discriminant == 0): raise ValueError("***** Error *****: Not an elliptic curve - Zero discriminant (-16*(4*({0}^3)+27*({1}^2)))".format(self.a,self.b)) 84 | self.fmtspec = fmtspec 85 | 86 | def isIntType(self,x): 87 | if sys.version_info < (3,): return isinstance(x,(int, long,)) 88 | else: return isinstance(x,(int,)) 89 | 90 | def __call__(self,pt): # Coerce constant or array of coeffs as elt of field 91 | """Create a point on the curve from a tuple or list of integers. (not [Infinity,Infinity])""" 92 | if not (isinstance(pt,(list,tuple,)) and len(pt)==2 and self.isIntType(pt[0]) and self.isIntType(pt[1])): 93 | raise ValueError('{0} should be a list or tuple of two integers'.format(pt)) 94 | if not ((pow(pt[0],3,self.prime) + self.a*pt[0] + self.b - pt[1]*pt[1]) % self.prime == 0): 95 | raise ValueError('{0} is not a point on the curve {1}'.format(pt,self)) 96 | return EllipticCurveElt(self,pt) 97 | 98 | def __iter__(self): 99 | """Generator producing all points on the elliptic curve.""" 100 | yield EllipticCurveElt(self, ("Infinity","Infinity")) 101 | x = 0 102 | for x in range(self.prime): 103 | ysq = (pow(x,3,self.prime) + self.a*x + self.b) % self.prime 104 | if((ysq == 0) or (pow(ysq,(self.prime-1)//2,self.prime)==1)): 105 | if (ysq == 0): y = 0 106 | else: y = numbthy.sqrtmod(ysq,self.prime) 107 | if((y % 2)==1): y = self.prime - y # Always even y first (consistent order) 108 | yield EllipticCurveElt(self, (x,y)) 109 | if (y != 0): yield EllipticCurveElt(self, (x,self.prime - y)) # Distinct unless y==0 110 | raise StopIteration 111 | 112 | def random_element(self): 113 | """A random element of the elliptic curve.""" 114 | # Currently, choosing point at infinity (group identity) and point 115 | # with y=0 is twice as likely as any other point. 116 | # Find a random x such that y^2 = x^3 + ax + b has a solution (mod p) 117 | xrand = random.randint(-1,self.prime-1) 118 | if(xrand == -1): return EllipticCurveElt(self, ("Infinity","Infinity")) 119 | ysq = (pow(xrand,3,self.prime) + self.a*xrand + self.b) % self.prime 120 | while((ysq != 0) and (pow(ysq,(self.prime-1)//2,self.prime)!=1)): 121 | xrand = random.randint(-1,self.prime-1) 122 | if(xrand == -1): return EllipticCurveElt(self, ("Infinity","Infinity")) 123 | ysq = (pow(xrand,3,self.prime) + self.a*xrand + self.b) % self.prime 124 | # Given x, find a y solving y^2 = x^3 + ax + b (mod p) 125 | if (ysq == 0): yrand = 0 126 | else: yrand = numbthy.sqrtmod(ysq,self.prime) 127 | if(random.randint(0,1)==1): yrand = self.prime - yrand # Choose between pt and -pt 128 | return EllipticCurveElt(self,(xrand,yrand)) 129 | 130 | def __format__(self,fmtspec): # Over-ride format conversion 131 | """Override the format when outputting an elliptic curve. 132 | A default can be set when the curve is defined or it can be specified for each output. 133 | Possible formats are: 134 | s - short format (default) 135 | f - full format, can be used as input 136 | t - LaTeX format""" 137 | if(fmtspec == ''): fmtspec = self.fmtspec 138 | if(fmtspec == 's'): # Short format 139 | return "y^2 = x^3 + {0}x + {1} (mod {2})".format(self.a,self.b,self.prime) 140 | if(fmtspec == 'f'): # Full format 141 | return "EllipticCurve({0},({1},{2}))".format(self.prime,self.a,self.b) 142 | if(fmtspec == 't'): # LaTeX format 143 | return "\mathbb{{E}}_{{y^2 = x^3 + {0}x + {1} \pmod{{{2}}}}}".format(self.a,self.b,self.prime) 144 | 145 | def __str__(self): # Over-ride string conversion used by print (?maybe?) and str() 146 | return format(self) 147 | def __repr__(self): # Over-ride string conversion for output 148 | return format(self) 149 | 150 | 151 | class EllipticCurveElt(object): 152 | """EllipticCurveElt(ec,[x,y]) is an element of the elliptic curve ec, with coordinates (x,y) in 153 | affine Weierstrass form. 154 | Usage: 155 | >>> from ellipticcurve import * 156 | >>> ec29 = EllipticCurve(29,[4,20]); ec29 157 | y^2 = x^3 + 4x + 20 (mod 29) 158 | >>> pt = EllipticCurveElt(ec29,[2,6]); pt 159 | (2, 6) 160 | >>> 7*pt 161 | (3, 28) 162 | >>> 37*pt 163 | (Infinity, Infinity) 164 | >>> pt1 = 9*pt; pt1 - pt 165 | (15, 27) 166 | >>> '{0:f}'.format(pt) # Full format 167 | 'EllipticCurveElt(EllipticCurve(29,(4,20)), (2,6))' 168 | """ 169 | def __init__(self, ellipticcurve, coords): 170 | self.ec = ellipticcurve 171 | self.x = coords[0] 172 | self.y = coords[1] 173 | def __format__(self,fmtspec): # Over-ride format conversion 174 | """Override the format when outputting a point on an elliptic curve. 175 | A default can be set when the curve is defined or it can be specified for each output. 176 | Possible formats are: 177 | s - short format (default) 178 | f - full format, can be used as input 179 | t - LaTeX format""" 180 | if(fmtspec == 'f'): 181 | if(self.x == "Infinity"): 182 | return "EllipticCurveElt("+'{0:f}'.format(self.ec)+", (Infinity,Infinity))" 183 | return "(Infinity,Infinity)" 184 | else: 185 | return "EllipticCurveElt("+'{0:f}'.format(self.ec)+", ("+format(self.x)+","+format(self.y)+"))" 186 | else: # Both short and LaTeX formats 187 | if(self.x == "Infinity"): 188 | if(fmtspec == 't'): # LaTeX format 189 | return "(\infty,\infty)" 190 | else: # short format 191 | return "(Infinity,Infinity)" 192 | else: 193 | return "({0}, {1})".format(self.x,self.y) 194 | def __str__(self): # Over-ride string conversion used by str() 195 | return format(self) 196 | def __repr__(self): # Over-ride string conversion for output 197 | return format(self) 198 | def __cmpec__(self,other): # Implement cmp for both Python2 and Python3 199 | """compare two points for equality and (possibly in future allow sorting) 200 | overloaded to allow comparisons to lists of integers""" 201 | # Coerce if comparing list (x,y) and point 202 | if (isinstance(other,(list,tuple,)) and len(other)==2 and self.ec.isIntType(pt[0]) and self.ec.isIntType(pt[1])): 203 | if (other[0]==self.x) and (other[1]==self.y): return 0 204 | else: return 1 205 | elif(self.ec != other.ec): 206 | raise ValueError("Cannot compare elements of different elliptic curves: <{0}> and <{1}>".format(self.ec,other.ec)) 207 | else: 208 | if (other.x==self.x) and (other.y==self.y): return 0 209 | else: return 1 210 | def __eq__(self,other): return (self.__cmpec__(other) == 0) 211 | def __ne__(self,other): return (self.__cmpec__(other) != 0) 212 | def add(self,summand): 213 | """add elements of elliptic curves""" 214 | if (self.x == "Infinity"): # Add to zero (i.e. point at infinity) 215 | return summand 216 | elif (summand.x == "Infinity"): # Add zero (i.e. point at infinity) 217 | return self 218 | elif ((summand.x == self.x) and ((summand.y + self.y) % self.ec.prime == 0)): # P + (-P) = infty 219 | return EllipticCurveElt(self.ec, ("Infinity","Infinity")) 220 | else: # Usual addition and doubling (what a nuisance: lambda is a keyword - shorten to lamb) 221 | if (self.x == summand.x): # Point doubling 222 | lamb = (3*(self.x**2)+self.ec.a)*numbthy.xgcd(2*self.y,self.ec.prime)[1] % self.ec.prime 223 | else: # Point addition 224 | lamb = (self.y - summand.y) * numbthy.xgcd((self.x - summand.x), self.ec.prime)[1] % self.ec.prime 225 | x3 = (lamb*lamb - self.x - summand.x) % self.ec.prime 226 | y3 = (lamb*(self.x-x3) - self.y) % self.ec.prime 227 | return EllipticCurveElt(self.ec, (x3,y3)) 228 | def __add__(self,summand): # Overload the "+" operator 229 | return self.add(summand) 230 | def __iadd__(self,summand): # Overload the "+=" operator 231 | self = self + summand 232 | return self 233 | def __neg__(self): # Overload the "-" unary operator 234 | return EllipticCurveElt(self.ec, (self.x, ((-self.y) % self.ec.prime))) 235 | def __sub__(self,summand): # Overload the "-" binary operator 236 | return self.__add__(-summand) 237 | def __isub__(self,summand): # Overload the "-=" operator 238 | self = self - summand 239 | return self 240 | def mult(self,multand): # Multiply EC point by integer (repeated addition in EC) 241 | """multiply elliptic curve point by integer (repeated addition in the elliptic curve)""" 242 | accum = EllipticCurveElt(self.ec, ("Infinity","Infinity")) # start with identity 243 | i = 0 244 | bpow2 = self 245 | while ((multand>>i) > 0): 246 | if((multand>>i) & 1): 247 | accum = (accum + bpow2) 248 | bpow2 = (bpow2 + bpow2) 249 | i+=1 250 | return accum 251 | def __rmul__(self,multip): # Overload the "*" operator 252 | return self.mult(multip) 253 | def __imul__(self,multip): # Overload the "*=" operator 254 | self = self.mult(multip) 255 | return self 256 | 257 | ############################################################################ 258 | # License: Freely available for use, abuse and modification 259 | # (this is the Simplified BSD License, aka FreeBSD license) 260 | # Copyright 2001-2018 Robert Campbell. All rights reserved. 261 | # 262 | # Redistribution and use in source and binary forms, with or without 263 | # modification, are permitted provided that the following conditions are met: 264 | # 265 | # 1. Redistributions of source code must retain the above copyright notice, 266 | # this list of conditions and the following disclaimer. 267 | # 268 | # 2. Redistributions in binary form must reproduce the above copyright 269 | # notice, this list of conditions and the following disclaimer in 270 | # the documentation and/or other materials provided with the distribution. 271 | # 272 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 273 | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 274 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 275 | # SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 276 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 277 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 278 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 279 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 280 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 281 | ############################################################################ 282 | # 4 Feb 2018: ver 0.25 283 | # Formatting - for EllipticCurve and EllipticCurveElt 284 | # Added document strings 285 | # Remove verbose mode 286 | # Fixed support for Python 3 287 | # 17 Feb 2018: ver 0.26 288 | # Added random_element 289 | # Added iterator 290 | # Added call (coerce list as point on curve) 291 | # Changed list to tuple for (x,y) - fix comparison bug 292 | -------------------------------------------------------------------------------- /finitefield.py: -------------------------------------------------------------------------------- 1 | ###################################################################################### 2 | # FINITEFIELD.PY 3 | # A finite field of prime power order 4 | # Note: Version 0.9 - major changes to improve SAGE compatibility 5 | # Version 0.8 is compatible with both Python 2.5+ and 3.x 6 | # and changes some names to improve SAGE compatibility 7 | # Author: Robert Campbell, 8 | # Date: 23 Sept, 2018 9 | # Version 0.971 10 | # License: Simplified BSD (see details at bottom)1 11 | ###################################################################################### 12 | """Finite fields. 13 | Functions implemented are: 14 | +, *, /, **, norm, trace, gen, random, enumerate 15 | Simple Examples: 16 | >>> from finitefield import * # names do not need path 17 | >>> GF81 = GF(3**4) # create finite field 18 | >>> a = GF81([0,1]) # create element [0,1] = (0 + 1*x) = x 19 | >>> '{0:l}^10 has value {1:l}'.format(a,a**10) # format as coeff list 20 | >>> GF81.random_element().verbstr() # a random value, formatted as polynomial 21 | >>> a+2*(a**12)+2*(a**50) # some arithmetic 22 | >>> for i,x in enumerate(GF81): print i,x.verbstr() 23 | >>> GF13 = GF(13); GF13(10)+GF13(10) # Compute mod 13 24 | 25 | Examples: 26 | >>> from finitefield import * # names do not need path 27 | >>> GF9 = FiniteField(3,[2,1]) # Define GF(3^2), polys w/ GF3 coeffs, mod 2+x+x^2 (x^2 is assumed) 28 | >>> a = FiniteFieldElt(GF9,[1,2]) # Define 1+2x in GF(9) 29 | >>> a**12 # Compute (2x+1)**12 in GF(9) 30 | Define GF(5^8), defined mod z^8+3z^5+z^4+z^2+3z+4 31 | Providing the factored order as (5^8-1) = (2^5)(3)(13)(313) 32 | Output is coefficients only ('c'), not full polynomials ('p') 33 | >>> GF5e8 = FiniteField(5,[4,3,1,0,1,3,0,0],'z',[[2,5],[3,1],[13,1],[313,1]],'c') 34 | >>> a = FiniteFieldElt(GF5e8,[0,1]) 35 | >>> format(a**20) # Default format, set when defining FiniteField 36 | '20340110' 37 | >>> '{0:p}'.format(a**20) # Force polynomial format 38 | '(2 + 3*z**2 + 4*z**3 + z**5 + z**6)' 39 | >>> '{0:l}'.format(a**20) # Force coeff list format 40 | '[2, 0, 3, 4, 0, 1, 1, 0]' 41 | >>> '{0:c}'.format(a**20) # Force packed coeffs format 42 | '20340110' 43 | >>> print a**20 44 | [2, 0, 3, 4, 0, 1, 1, 0] 45 | >>> a**20 46 | FiniteFieldElt(FiniteField(5, [4, 3, 1, 0, 1, 3, 0, 0]),[2, 0, 3, 4, 0, 1, 1, 0])""" 47 | 48 | 49 | __version__ = '0.971' # Format specified in Python PEP 396 50 | Version = 'finitefield.py, version ' + __version__ + ', 23 Sept, 2018, by Robert Campbell, ' 51 | 52 | import numbthy # Use factor 53 | import random # Generate random elements 54 | import sys # Check Python2 or Python3 55 | from operator import add,mul,mod # To allow reduce(add,list) construct for sum 56 | 57 | class FiniteField(object): 58 | """Finite fields of prime power order. 59 | Driving polynomial must be monic and top coeff (i.e. 1) is implicit. 60 | Usage: 61 | >>> from finitefield import * 62 | >>> GF9 = FiniteField(3,[2,1]) # Define GF(3^2), polys w/ GF3 coeffs, mod x^2+x+2 63 | >>> a = FiniteFieldElt(GF9,[1,2]) # Define 2x+1 in GF(9) 64 | >>> a**12 # Compute (2x+1)**12 in GF(9)""" 65 | 66 | def __init__(self, prime, poly, var='x', orderfacts=None, fmtspec="p"): 67 | """FiniteField(prime, poly, var='x', orderfacts=None, fmtspec="p") 68 | Create a finite field of order p**d, where d is the degree of the polynomial. 69 | Driving polynomial must be monic and top coeff (i.e. 1) is implicit. 70 | Example: 71 | >>> from finitefield import * 72 | >>> GF9 = FiniteField(3,[2,1]) # Define GF(3^2), polys w/ GF3 coeffs, mod x^2+x+2 73 | >>> a = FiniteFieldElt(GF9,[1,2]) # Define 2x+1 in GF(9) 74 | Define GF(5^8), defined mod z^8+3z^5+z^4+z^2+3z+4 75 | Providing the factored order as (5^8-1) = (2^5)(3)(13)(313) 76 | Default output is coefficients only ('c'), not full polynomials ('z') 77 | >>> GF5e8 = FiniteField(5,[4,3,1,0,1,3,0,0],'z',((2,5),(3,1),(13,1),(313,1)),'c') 78 | >>> '{0}'.format(c**20) # Default format 79 | '20340110' 80 | >>> '{0:p}'.format(c**20) # Force polynomial format 81 | '(2 + 3*z**2 + 4*z**3 + z**5 + z**6)'""" 82 | 83 | self.char = prime 84 | self.degree = len(poly) 85 | self.order = self.char**self.degree 86 | self.modpoly = poly 87 | self.var = var 88 | self.fmtspec = fmtspec 89 | if(orderfacts == None): 90 | self.facts_order_gpunits = numbthy.factor(self.order - 1) 91 | else: 92 | self.facts_order_gpunits = orderfacts 93 | if((self.char**self.degree-1) != reduce(lambda theprod,primepow:theprod*primepow, [prime**thepow for [prime,thepow] in orderfacts])): 94 | raise ValueError('{0} is not a factorization of ({1}^{2}-1)'.format(orderfacts,self.char,self.degree)) 95 | self.reduc_table = [[0 for j in range(self.degree)] for i in range(2*self.degree-1)] 96 | for i in range(self.degree): self.reduc_table[i][i] = 1 97 | if(self.degree > 1): 98 | self.reduc_table[self.degree] = [(-self.modpoly[j])%self.char for j in range(self.degree)] 99 | for i in range(self.degree+1,2*self.degree-1): 100 | for j in range(self.degree): 101 | self.reduc_table[i][j] = sum(map(lambda k: (-self.modpoly[k]*self.reduc_table[i-self.degree+k][j]), range(self.degree))) % self.char 102 | 103 | def verbstr(self): # Requires feature from python 2.5.2 or better 104 | if(self.degree > 1): 105 | return "Z_"+str(self.char)+"["+self.var+"]/<"+self.polyprint(self.modpoly+[1],var=self.var)+">" 106 | else: 107 | return "Z_"+str(self.char) 108 | 109 | def __format__(self,fmtspec): # Over-ride format conversion 110 | """Override the format when outputting a finite field. 111 | A default can be set when the field is defined or it can be specified for each output. 112 | Possible formats are: 113 | p - polynomial format 114 | l - list of coefficients 115 | c - coefficients in packed format 116 | f - full format, can be used as input 117 | s - short format 118 | t - LaTeX format 119 | tl - LaTeX format (long) 120 | ts - LaTeX format (short) 121 | """ 122 | if(fmtspec == ''): fmtspec = self.fmtspec 123 | if(fmtspec == 'p'): # Polynomial format 124 | return "GF("+str(self.char)+"**"+str(self.degree)+","+self.polyprint(self.modpoly+[1],var=self.var)+")" 125 | elif(fmtspec == 'l'): # Coefficient list format 126 | return "GF("+str(self.char)+"**"+str(self.degree)+","+format(self.modpoly+[1])+")" 127 | elif(fmtspec == 'c'): # Coeffs only format 128 | return "GF("+str(self.char)+"**"+str(self.degree)+","+''.join([str(self.modpoly[i]) for i in range(len(self.modpoly))])+"1"+")" 129 | elif(fmtspec == 'f'): # Full form format - can be input 130 | return "FiniteField("+str(self.char)+","+str(self.modpoly)+")" 131 | if(fmtspec == 't' or fmtspec == 'tl'): # long LaTeX format 132 | return "{GF("+str(self.char)+")["+str(self.var)+r']/\left\langle{'+self.polyprint(self.modpoly+[1],var=self.var,fmtspec='t')+r'}\right\rangle}' 133 | if(fmtspec == 'ts'): # short LaTeX format 134 | return "{GF("+str(self.char)+"^{"+str(self.degree)+"})}" 135 | elif(fmtspec == 's'): # Short format - field size only (can be input though) 136 | return "GF("+str(self.char)+"**"+str(self.degree)+")" 137 | else: raise ValueError("***** Error *****: FiniteField has valid fmtspec values p, l, c, f, s, t, tl and ts, not <{0}>".format(fmtspec)) 138 | return str(self) # Get to this later 139 | 140 | def __str__(self): # Over-ride string conversion used by print 141 | if(self.degree > 1): 142 | return "GF("+str(self.char)+"^"+str(self.degree)+")" 143 | else: 144 | return "GF("+str(self.char)+")" 145 | 146 | def __repr__(self): # Over-ride format conversion 147 | return '{0:f}'.format(self) 148 | 149 | def polyprint(self,coeffs,var='X',fmtspec='p'): # Coefficients and polynomial variable, e.g. [1,2,2,0,3], "x" yields "1 + 2x + 2x^2 + 3x^4" 150 | """polyprint(coeffs,var=thevar,fmtspec=thefmtspec) prints the coefficients in polynomial form, 151 | using the variable thevar. Formats can be Python/SAGE, ie 1 - 2*x + 2*x**2 + 3*x**4, 152 | or LaTeX, ie 1 - 2x + 2x^{2} + 3x^{4} 153 | Possible formats are: 154 | p - polynomial format 155 | f - full format, can be used as input 156 | t - LaTeX format""" 157 | thestr = "" 158 | firstnon = -1 159 | for i in range(len(coeffs)): # Find first non-zero coeff 160 | if(coeffs[i] != 0): 161 | firstnon = i 162 | break 163 | if(firstnon == -1): return "0" # All coeffs are zero 164 | for i in range(firstnon,len(coeffs)): 165 | if coeffs[i] == 0: continue 166 | if(i == 0): # First non-zero coeff is the constant term 167 | thestr = "{0}".format(coeffs[0]) 168 | else: # First non-zero coeff is not the constant term 169 | if (coeffs[i] < 0): # Negative coeff 170 | if(i == firstnon): # Different spacing for first 171 | thestr += "-" 172 | else: 173 | thestr += " - " 174 | elif(i != firstnon): # Positive coeff (not the first non-zero coeff) 175 | thestr += " + " 176 | if(abs(coeffs[i]) != 1): # Suppress printing a coeff of '1' 177 | if (fmtspec=='t'): 178 | thestr += str(abs(coeffs[i])) 179 | else: 180 | thestr += (str(abs(coeffs[i])) + "*") 181 | if(i == 1): # x^1 is just 'x' 182 | thestr += var 183 | else: 184 | if (fmtspec=='t'): 185 | thestr += var+"^{"+str(i)+"}" 186 | else: 187 | thestr += var+"**"+str(i) 188 | return thestr 189 | 190 | def __call__(self,elts=0): # Coerce constant or array of coeffs as elt of field 191 | return FiniteFieldElt(self,elts) 192 | 193 | def __iter__(self): 194 | """Generator producing all elements of the finite field.""" 195 | digits = [0]*self.degree 196 | while True: 197 | yield FiniteFieldElt(self,digits) 198 | digits[0] += 1 199 | i = 0 200 | while digits[i] >= self.char: 201 | if ((i+1)>= self.degree): raise StopIteration 202 | digits[i] = 0 203 | digits[i+1] += 1 204 | i += 1 205 | 206 | def random_element(self): 207 | """A random element of the finite field.""" 208 | therand = random.randint(0,(self.char)**(self.degree)-1) 209 | return FiniteFieldElt(self,[(therand//(self.char)**i)%(self.char) for i in range(self.degree)]) 210 | 211 | def random(self): 212 | """A random element of the finite field. (Renamed random_element in ver 0.96)""" 213 | return self.random_element() 214 | 215 | def gen(self): # Generator element of the field 216 | """Usual generating element of the finite field.""" 217 | return FiniteFieldElt(self,[0,1]) 218 | 219 | class FiniteFieldElt(object): 220 | """An element of a prime power order finite fields" 221 | Driving polynomial must be monic and top coeff (i.e. 1) is implicit. 222 | Usage: 223 | >>> from finitefield import * 224 | >>> GF9 = FiniteField(3,[2,1]) # Define GF(3^2), polys w/ GF3 coeffs, mod x^2+x+2 225 | >>> a = FiniteFieldElt(GF9,[1,2]) # Define 2x+1 in GF(9) 226 | >>> a**12 # Compute (2x+1)**12 in GF(9)""" 227 | 228 | def isIntType(self,x): 229 | if sys.version_info < (3,): return isinstance(x,(int, long,)) 230 | else: return isinstance(x,(int,)) 231 | 232 | def __init__(self, field, elts=0): 233 | self.field = field 234 | if self.isIntType(elts): # Allow coercion from integer 235 | self.coeffs = [mod(elts,self.field.char)] + [0 for i in range(self.field.degree-1)] 236 | else: 237 | self.coeffs = [mod(theelt,self.field.char) for theelt in elts] + [0 for i in range(self.field.degree - len(elts))] 238 | 239 | def verbstr(self): # Requires feature from python 2.5.2 or better 240 | return "("+self.field.polyprint(self.coeffs,var=self.field.var)+")" 241 | 242 | def __format__(self,fmtspec): # Over-ride format conversion 243 | """Override the format when outputting a finite field element. 244 | A default can be set when the field is defined or it can be specified for each output. 245 | Possible formats are: 246 | p - polynomial format 247 | l - list of coefficients 248 | c - coefficients in packed format 249 | t - LaTeX format 250 | f - full format - can be used as input 251 | Example: 252 | >>> GF64 = GF(2**6,var='z',fmtspec='c') 253 | >>> a = GF64([1,0,1,1,0,1]) 254 | >>> format(a) 255 | '101101' 256 | >>> '{:p}'.format(a) 257 | '(1 + z^2 + z^3 + z^5)' 258 | >>> '{:l}'.format(a) 259 | '[1, 0, 1, 1, 0, 1]' 260 | >>> '{:c}'.format(a) 261 | '101101' """ 262 | if(fmtspec == ''): fmtspec = self.field.fmtspec 263 | if(fmtspec == 'p' or fmtspec == 's'): # Polynomial format 264 | return "("+self.field.polyprint(self.coeffs,var=self.field.var)+")" 265 | elif(fmtspec == 'l'): # Coefficient list format 266 | return format(self.coeffs) 267 | elif(fmtspec == 'c'): # Coeffs only format 268 | return ''.join([str(self.coeffs[i]) for i in range(len(self.coeffs))]) 269 | elif(fmtspec == 't'): # LaTeX format 270 | return "("+self.field.polyprint(self.coeffs,var=self.field.var,fmtspec='t')+")" 271 | elif(fmtspec == 'f'): # Full form format - can be input 272 | return "FiniteFieldElt("+'{0:f}'.format(self.field)+","+str(self.coeffs)+")" 273 | else: raise ValueError("***** Error *****: FiniteFieldElt has valid fmtspec values p, l, c and f, not <{0}>".format(fmtspec)) 274 | 275 | def __str__(self): 276 | """over-ride string conversion used by print""" 277 | return '{0:p}'.format(self) 278 | 279 | def __repr__(self): 280 | """over-ride format conversion""" 281 | return '{0:f}'.format(self) 282 | 283 | def __cmpfinfld__(self,other): # Implement cmp for both Python2 and Python3 284 | """compare two elements for equality and allow sorting 285 | overloaded to allow comparisons to integers and lists of integers""" 286 | if self.isIntType(other): # Coerce if comparing int and and finfld 287 | return self.listcmp(tuple(reversed(self.coeffs)),tuple(reversed([other]+[0 for i in range(self.field.degree-1)]))) 288 | elif isinstance(other,(list,tuple,)): # Coerce if comparing list (of coeffs) and finfld 289 | return self.listcmp(tuple(reversed(self.coeffs)),tuple(reversed(other+[0 for i in range(self.field.degree-len(other))]))) 290 | elif(self.field != other.field): 291 | raise ValueError("Cannot compare elements of different FiniteFields: <{0}> and <{1}>".format(self.field,other.field)) 292 | else: 293 | return self.listcmp(tuple(reversed(self.coeffs)),tuple(reversed(other.coeffs))) 294 | 295 | def listcmp(self,list1,list2): # Implement list cmp for Python3 296 | for ptr in range(len(list1)): 297 | if (list1[ptr] < list2[ptr]): return -1 298 | if (list1[ptr] > list2[ptr]): return 1 299 | else: return 0 300 | 301 | def __cmp__(self,other): return self.__cmpfinfld__(other) # Used by Python2 for sorting 302 | def __lt__(self,other): return (self.__cmpfinfld__(other) < 0) 303 | def __gt__(self,other): return (self.__cmpfinfld__(other) > 0) 304 | def __eq__(self,other): return (self.__cmpfinfld__(other) == 0) 305 | def __le__(self,other): return (self.__cmpfinfld__(other) <= 0) 306 | def __ge__(self,other): return (self.__cmpfinfld__(other) >= 0) 307 | def __ne__(self,other): return (self.__cmpfinfld__(other) != 0) 308 | 309 | def norm(self): 310 | """The norm of an element over the base field is the product of its conjugates, 311 | i.e. norm(a) = a * (a**p) * (a**(p**2)) * ... * (a**(p**k)) 312 | where p is the characteristic of the field and k is the extension degree.""" 313 | return reduce(mul,[self**(self.field.char**k) for k in range(self.field.degree)],1) 314 | 315 | def trace(self): 316 | """The trace of an element over the base field is the sum of its conjugates, 317 | i.e. trace(a) = a + (a**p) + (a**(p**2)) + ... + (a**(p**k)) 318 | where p is the characteristic of the field and k is the extension degree.""" 319 | return sum([self**(self.field.char**k) for k in range(self.field.degree)]) 320 | 321 | def add(self,summand): 322 | """add elements of finite fields (overloaded to allow adding integers and lists of integers)""" 323 | return FiniteFieldElt(self.field, tuple(map(lambda x,y: (x+y)%self.field.char, self.coeffs, summand.coeffs))) 324 | 325 | def __add__(self,summand): # Overload the "+" operator 326 | if self.isIntType(summand): # Coerce if adding integer and FiniteFieldElt 327 | return self.add(FiniteFieldElt(self.field,[summand])) 328 | elif isinstance(summand,(list,tuple,)): # Coerce if adding list (of coeffs) and FiniteFieldElt 329 | return self.add(FiniteFieldElt(self.field,summand)) 330 | else: 331 | return self.add(summand) 332 | 333 | def __radd__(self,summand): # Overload the "+" operator when first addend can be coerced to finfld 334 | if self.isIntType(summand): # Coerce if adding int and finfld 335 | return self.add(FiniteFieldElt(self.field,[summand])) 336 | elif isinstance(summand,(list,tuple,)): # Coerce if adding list and finfld 337 | return self.add(FiniteFieldElt(self.field,summand)) 338 | else: 339 | return self.add(summand) 340 | 341 | def __iadd__(self,summand): # Overload the "+=" operator 342 | self = self + summand 343 | return self 344 | 345 | def __neg__(self): # Overload the "-" unary operator 346 | return FiniteFieldElt(self.field, tuple(map(lambda x: self.field.char-x, self.coeffs))) 347 | 348 | def __sub__(self,summand): # Overload the "-" binary operator 349 | return self.__add__(-summand) 350 | 351 | def __isub__(self,summand): # Overload the "-=" operator 352 | self = self - summand 353 | return self 354 | 355 | def mult(self,multand): # Elementary multiplication in finite fields 356 | """multiply elements of finite fields (overloaded to allow integers and lists of integers)""" 357 | thelist = [0 for i in range(self.field.degree)] 358 | for d in range(2*self.field.degree-1): 359 | for j in range(max(0,d-(self.field.degree-1)),min(d+1,self.field.degree)): 360 | list2 = [(self.coeffs[j]*multand.coeffs[d-j])*i for i in self.field.reduc_table[d]] 361 | thelist = map(add, thelist, list2) 362 | return FiniteFieldElt(self.field, tuple(map(lambda x: x%self.field.char, thelist))) 363 | 364 | def __mul__(self,multip): # Overload the "*" operator 365 | if self.isIntType(multip): # Coerce if multiply int and finfld 366 | return self.mult(FiniteFieldElt(self.field,[multip])) 367 | elif isinstance(multip,(list,tuple,)): # Coerce if multiply list and finfld 368 | return self.mult(FiniteFieldElt(self.field,multip)) 369 | else: 370 | return self.mult(multip) 371 | 372 | def __rmul__(self,multip): # Overload the "*" operator 373 | if self.isIntType(multip): # Coerce if mult int and and finfld 374 | return self.mult(FiniteFieldElt(self.field,[multip])) 375 | elif isinstance(multip,(list,tuple,)): # Coerce if mult list and and finfld 376 | return self.mult(FiniteFieldElt(self.field,multip)) 377 | return self.mult(multip) 378 | 379 | def __imul__(self,multip): # Overload the "*=" operator 380 | self = self * multip 381 | return self 382 | 383 | def inv(self): 384 | """inverse of element in a finite field""" 385 | # A better implementation would be xgcd over polynomials 386 | return self.pow(self.field.order-2) 387 | 388 | def div(self,divisor): 389 | """divide elements of a finite field""" 390 | return self * divisor.inv() 391 | 392 | def __div__(self,divisor): 393 | if self.isIntType(divisor): 394 | divisor = FiniteFieldElt(self.field,[divisor]) # Coerce if dividing integer and FiniteFieldElt 395 | return self * divisor.inv() 396 | 397 | def __rdiv__(self,dividend): 398 | if self.isIntType(dividend): 399 | dividend = FiniteFieldElt(self.field,[dividend]) # Coerce if dividing integer and FiniteFieldElt 400 | return dividend * self.inv() 401 | 402 | def pow(self,exponent): 403 | """pow(b,e) computes the eth power of finite field element b.""" 404 | exponent = (exponent % (self.field.order-1)) # Handle negative and large exponents 405 | accum = FiniteFieldElt(self.field,[1]) 406 | i = 0 407 | bpow2 = self 408 | while ((exponent>>i)>0): 409 | if((exponent>>i) & 1): 410 | accum = (accum*bpow2) 411 | bpow2 = (bpow2*bpow2) 412 | i+=1 413 | return accum 414 | 415 | def __pow__(self,exponent): # Overload the "**" operator 416 | return self.pow(exponent) 417 | 418 | def is_primitive(self): 419 | """is_primitive(e) returns True if the finite field element e has full order, 420 | i.e. the powers of e exhaust all non-zero elements of the field.""" 421 | if (self**(self.field.order-1) != 1): return False # Not a unit 422 | for [theprime,thepow] in self.field.facts_order_gpunits: 423 | if (self**((self.field.order-1)//theprime) == 1): return False 424 | return True 425 | 426 | def multiplicative_order(self): 427 | """multiplicative_order(b) returns the smallest positive non-zero exponent e such that b**e == 1.""" 428 | if (self**(self.field.order-1) != 1): 429 | raise ValueError('{0} is not a unit in GF({1}^{2})'.format(self,self.field.char,self.field.degree)) 430 | orderaccum = 1 431 | for [theprime,maxpow] in self.field.facts_order_gpunits: 432 | theval = self**((self.field.order-1)//(theprime**maxpow)) 433 | for thepow in range(maxpow+1): 434 | if (theval == 1): 435 | orderaccum *= (theprime**thepow) 436 | break 437 | theval = theval**theprime 438 | return orderaccum 439 | 440 | def order(self): 441 | """order(b) returns the smallest positive non-zero exponent e such that b**e == 1. (Renamed multiplicative_order in ver 0.96)""" 442 | return self.multiplicative_order() 443 | 444 | def minimal_polynomial(self): # Find first element of nullspace in matrix (1, a, a^2, a^3, ..., a^deg) 445 | """minimal_polynomial(a) is the least degree monic polynomial which has 446 | a value of zero when a is substituted. (Currently returns a list of 447 | coefficients - will revisit when a polynomial package has been written.) 448 | To see in conventional polynomial format use 449 | FiniteField.polyprint(a.minimal_polynomial(),var='X')""" 450 | thedegree = self.field.degree 451 | themod = self.field.char 452 | numrows = self.field.degree+1 453 | numcols = 2*self.field.degree+1 454 | themat = [(self**i).coeffs + [(0 if (i!=j) else 1) for j in range(numrows)] for i in range(numrows)] 455 | ###### Nested function 456 | def reducemat(submat): 457 | pivotrow=0 458 | for rj in range(thedegree): 459 | # Find the pivot element in this column 460 | for ri in range(pivotrow,len(submat)): 461 | if (submat[ri][rj] != 0): 462 | # Swap rows 463 | temp = submat[pivotrow]; submat[pivotrow] = submat[ri]; submat[ri] = temp 464 | break 465 | else: 466 | continue # No pivot in this column - move to next column (for rj) 467 | # Subtract multiples of pivot row from lower rows (zeroing out column elts) 468 | invpivot = numbthy.inverse_mod(submat[pivotrow][rj],themod) 469 | for ri in range(pivotrow+1,len(submat)): 470 | rowmult = mod(invpivot*submat[ri][rj],themod) 471 | for rjj in range(rj,numcols): 472 | submat[ri][rjj] = mod(submat[ri][rjj]-rowmult*submat[pivotrow][rjj],themod) 473 | #if not any(themat[i][:thedegree]): return themat[i][thedegree:] 474 | pivotrow += 1 # Move down to next row 475 | ###### end Nested function reducemat 476 | for i in range(2,numrows+1): # Reduce each submatrix - look for first null 477 | reducemat(themat[:i]) 478 | if not any(map(lambda x:x!=0, themat[i-1][:thedegree])): return themat[i-1][thedegree:] 479 | 480 | # Read a file of Conway Polynomials, formatted as per Frank Luebeck 481 | # [http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CPimport.txt] 482 | def readconway(filepath,p,e): 483 | import sys 484 | try: 485 | cpfile = open(filepath) 486 | except IOError as e: 487 | print("I/O error({0}): {1}\n Probably couldn't find file 'CPimport.txt' of Conway Polynomials\n Try [http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CPimport.txt]".format(e.errno, e.strerror)) 488 | pass # Pass the exception up the calling stack 489 | cpfile.readline() # Discard the first line 490 | for theline in cpfile: 491 | if (theline[0] == '0'): 492 | cpfile.close() 493 | raise ValueError('GF({0}^{1}) is beyond the table of Conway Polynomials in {2}'.format(p,e,filepath)) 494 | polyspec = eval(theline[:-2]) # Strip off comma and line end char 495 | theprime = polyspec[0]; thepow = polyspec[1] 496 | if (p == polyspec[0] and e == polyspec[1]): 497 | cpfile.close() 498 | return polyspec[2] 499 | 500 | def findprimpoly(p,e): 501 | """A brute-force search for a primitive polynomomial mod p of degree e. 502 | Needs to be fixed, as it relies on a bug in the FiniteField code, allowing 503 | a 'field' to be defined mod a reducible polynomial. Also needs better check 504 | for reducibility - often fails for non-prime degrees. Need to implement Rabin test in polynomial class.""" 505 | raise NotImplementedError('findpripoly() is not implemented. Previous implementation was buggy and removed.') 506 | 507 | def GF(n, poly=[], var='x', fmtspec="p"): 508 | """A shorthand for generating finite fields. If poly is not specified then one will be chosen from a list of Conway polynomials.""" 509 | if numbthy.is_prime(n): 510 | if len(poly)<2: 511 | return FiniteField(n,[1],var=var,fmtspec=fmtspec) 512 | else: 513 | return FiniteField(n,poly,var=var,fmtspec=fmtspec) # Explicit characteristic and polynomial modulus 514 | else: # n is not prime - hope it's a prime power 515 | nfactors = numbthy.factor(n) 516 | if (len(nfactors) != 1): raise ValueError('GF({0}) only makes sense for {0} a prime power'.format(n)) 517 | p = nfactors[0][0]; e = nfactors[0][1] # Prime p, exponent e 518 | if (len(poly) == 0): 519 | try: 520 | poly = readconway('CPimport.txt',p,e)[:-1] # Assume monic 521 | except(IOError,ValueError): 522 | print(" Look for non-Conway primitive polynomial") 523 | poly = findprimpoly(p,e) 524 | else: 525 | if nfactors[0][1] != len(poly): 526 | raise ValueError('Polynomial {0} does not have degree {1}'.format(poly+[1],nfactors[0][1])) 527 | return FiniteField(p,poly,var=var,fmtspec=fmtspec) 528 | 529 | ################################################################################## 530 | # More Examples: 531 | # (ref: Primitive Polynomials over Finite Fields, T. Hansen & G. L. Mullins, Math Comp, Oct 1992) 532 | # GF32 = FiniteField(2,[1,0,1,0,0]) # Define GF(2^5) = Z_2[x]/ 533 | # GF256 = FiniteField(2,[1,0,1,1,1,0,0,0]) # Define GF(2^8) = Z_2[x]/ 534 | # GF27 = FiniteField(3,[1,2,0]) # Define GF(3^3) = Z_3[x]/ 535 | # GF6561 = FiniteField(3,[2,0,0,1,0,0,0,0])# Define GF(3^8) = Z_3[x]/ 536 | # GF25 = FiniteField(5,[2,1]) # Define GF(5^2) = Z_5[x]/ 537 | # GF125 = FiniteField(5,[2,3,0]) # Define GF(5^3) = Z_5[x]/ 538 | # GF2197 = FiniteField(13,[6,1,0]) # Define GF(13^3) = Z_13[x]/ 539 | # Irreducible Polynomials: 540 | # [http://theory.cs.uvic.ca/gen/poly.html] 541 | # [http://en.wikipedia.org/wiki/Conway_polynomial_(finite_fields)] 542 | # Factorization of Group Orders: (Cunningham Tables) 543 | # [http://mersennewiki.org/index.php/Cunningham_Tables] 544 | ################################################################################## 545 | 546 | ############################################################################ 547 | # License: Freely available for use, abuse and modification 548 | # (this is the Simplified BSD License, aka FreeBSD license) 549 | # Copyright 2001-2017 Robert Campbell. All rights reserved. 550 | # 551 | # Redistribution and use in source and binary forms, with or without 552 | # modification, are permitted provided that the following conditions are met: 553 | # 554 | # 1. Redistributions of source code must retain the above copyright notice, 555 | # this list of conditions and the following disclaimer. 556 | # 557 | # 2. Redistributions in binary form must reproduce the above copyright 558 | # notice, this list of conditions and the following disclaimer in 559 | # the documentation and/or other materials provided with the distribution. 560 | # 561 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 562 | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 563 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 564 | # SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 565 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 566 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 567 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 568 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 569 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 570 | ############################################################################ 571 | 572 | # 1 June 2014: ver 0.6 573 | # norm, trace 574 | # minpoly (difficult - requires linear algebra) 575 | # 19 July 2014: ver 0.7 576 | # allow direct input of factored group order 577 | # changed from factors to factor (changed in numbthy.py) 578 | # 21 July 2014: ver 0.71 579 | # is_primitive() 580 | # order() 581 | # 18 Oct 2014: ver 0.8 582 | # [http://www.sagemath.org/doc/reference/rings_standard/sage/rings/finite_rings/constructor.html] 583 | # [http://www.sagemath.org/doc/reference/rings_standard/sage/rings/finite_rings/element_base.html] 584 | # [http://www.sagemath.org/doc/reference/rings_standard/sage/rings/finite_rings/integer_mod.html] 585 | # 13 Dec 2014: ver 0.9 586 | # GF() constructor 587 | # Conway polynomials 588 | # (still need to address simple case of prime field) 589 | # Bug: 1/eltfinfld - fixed (inv and __div__ each referenced the other - need polynomial xgcd) 590 | # 15 Dec 2014: ver 0.91 591 | # Conway polynomials - fixed bug in readconway routine 592 | # findprimpoly() - added for brute force primitive polynomial search 593 | # 7 Feb 2017: ver 0.92 594 | # fix to findprimpoly() 595 | # 16 Sept 2017: ver 0.93 596 | # Prime field 597 | # Fixed bug in order() - always returned 1 598 | # Need to improve formatted output 599 | # 22 Jan 2018: ver 0.94 600 | # Formatting - both for FiniteField and FiniteFieldElt 601 | # Added document strings 602 | # 28 Jan 2018: ver 0.95 603 | # Fixed support for Python 3 604 | # Removed buggy findprimpoly (often returned reducible poly if degree composite) 605 | # 10 Feb 2018: ver 0.96 606 | # Add minimal_polynomial() method 607 | # Minor renaming for SAGE compatibility (multiplicative_order and random_element) 608 | # Minor formatting changes 609 | # 11 Mar 2018: ver 0.97 610 | # Fix various bugs in GF 611 | # polyprint: Let default var be 'X' 612 | # 11 Mar 2018: ver 0.971 613 | # Minor format fixes to repr and str 614 | 615 | -------------------------------------------------------------------------------- /gaussint.py: -------------------------------------------------------------------------------- 1 | ###################################################################################### 2 | # GAUSSINT.PY 3 | # Basic Number Theory functions implemented in Python 4 | # Note: Currently requires Python 3.x (uses floordiv, changes to the "types" module…) 5 | # Author: Robert Campbell, 6 | # Modified: Hubert Holin, 7 | # Date: 17 March, 2019 8 | # Version 1.2 9 | # License: Simplified BSD (see details at bottom) 10 | # Requirements: 11 | # Requires at least Python 3.x (runs fine on Python 3.6) 12 | # Bugs: 13 | # None currently known. 14 | ###################################################################################### 15 | __version__ = '1.2' # Format specified in Python PEP 396 16 | Version = 'GAUSSINT.PY, version ' + __version__ +\ 17 | ', 8 June, 2013, by Robert Campbell, '+\ 18 | ', modified 17 March 2019 by Hubert Holin, ' 19 | 20 | 21 | import math # For tools used in primality testing 22 | 23 | 24 | class GaussInt: 25 | """Gaussian Integer functions. 26 | Functions implemented are: 27 | Arithmetic functions: +,*,//,%,**(exponentiation) 28 | a.gcd(b) - Compute the greatest common divisor of a and b. 29 | a.xgcd(b) - Extended gcd - return gcd(a,b) and x,y such that gcd(a,b)=xa+yb. 30 | n.isprime() - Is n prime (pseudoprime tests) 31 | n.factor() - Return a factor of n. 32 | n.factors() - Return list of the factors of n. 33 | Gaussian Integers can be created by: 34 | n = GaussInt(5,7) # Create (5 + 7i) 35 | n = GaussInt(13) # Create (5 + 0i) 36 | z = complex(2,3); n = GaussInt(z) # Round the complex number to integer form 37 | A list of the functions implemented in GaussInt is printed by the command help(GaussInt). 38 | Usage: from gaussint import * """ 39 | 40 | 41 | def __init__(self, a = 0, b = 0): 42 | 43 | if (type(a) is complex): 44 | 45 | if b != 0: 46 | 47 | raise TypeError("Attempting to ceate a Gauss Integer from a complex "+ 48 | "number and another input ({0:s} and {1:s})!".format(a, b)) 49 | 50 | self.r = round(a.real) 51 | self.i = round(a.imag) 52 | 53 | else: 54 | 55 | self.r = int(a) 56 | self.i = int(b) 57 | 58 | 59 | def __str__(self): # Overload string conversion used by print 60 | 61 | return "(" + str(self.r) + ((" + "+str(self.i)) if (self.i >= 0) else (" - "+str(-self.i))) + " i)" 62 | 63 | 64 | def __format__(self, spec):# Overload string conversion used by format 65 | 66 | return "(" + str(self.r) + ((" + "+str(self.i)) if (self.i >= 0) else (" - "+str(-self.i))) + " i)" 67 | 68 | 69 | def __repr__(self): # Overload conversion used for output 70 | 71 | return "GaussInt(" + str(self.r) + ", " + str(self.i) + ")" 72 | 73 | 74 | def __complex__(self): # Allow conversion to complex type 75 | return complex(self.r, self.i) 76 | 77 | 78 | def __eq__(self,other): # Overload the "==" test operator - NOTE: differs from version 1.1 79 | 80 | if (type(other) is not GaussInt): 81 | 82 | return False 83 | 84 | else: 85 | 86 | return (self.r == other.r) and (self.i == other.i) 87 | 88 | 89 | def __ne__(self,other): # Overload the "!=" test operator 90 | 91 | return not (self == other) 92 | 93 | 94 | def neutral_element_for_multiplication(): 95 | 96 | return __class__(1) 97 | 98 | 99 | def conjugate(self): 100 | 101 | return GaussInt(self.r, -self.i) 102 | 103 | 104 | def norm(self): 105 | 106 | return self.r*self.r + self.i*self.i 107 | 108 | 109 | def __pos__(self): # Overload the "+" unary operator 110 | 111 | return self 112 | 113 | 114 | def add(self,summand): 115 | 116 | sum_r = self.r + summand.r 117 | sum_i = self.i + summand.i 118 | 119 | return GaussInt(sum_r, sum_i) 120 | 121 | 122 | def __add__(self,summand): # Overload the "+" binary operator 123 | 124 | if type(summand) is int: 125 | 126 | return GaussInt(self.r+summand, self.i) 127 | 128 | else: 129 | 130 | return self.add(summand) 131 | 132 | 133 | def __radd__(self,summand): # Overload the "+" binary operator 134 | 135 | if type(summand) is int: 136 | 137 | return GaussInt(self.r+summand, self.i) 138 | 139 | else: 140 | 141 | return self.add(summand) 142 | 143 | 144 | def __iadd__(self,summand): # Overload the "+=" operator 145 | 146 | self = self + summand 147 | 148 | return self 149 | 150 | 151 | def __neg__(self): # Overload the "-" unary operator 152 | 153 | return GaussInt(-self.r,-self.i) 154 | 155 | 156 | def __sub__(self,summand): # Overload the "-" binary operator 157 | 158 | return self.__add__(-summand) 159 | 160 | 161 | def __rsub__(self,summand): # Overload the "-" binary operator 162 | 163 | if type(summand) is int: 164 | 165 | return GaussInt(summand-self.r, -self.i) 166 | 167 | else: 168 | 169 | return summand-self 170 | 171 | 172 | def __isub__(self,summand): # Overload the "-=" operator 173 | 174 | self = self - summand 175 | 176 | return self 177 | 178 | 179 | def mult(self,multip): 180 | 181 | prod_r = (self.r * multip.r) - (self.i * multip.i) 182 | prod_i = (self.i * multip.r) + (self.r * multip.i) 183 | 184 | return GaussInt(prod_r, prod_i) 185 | 186 | 187 | def __mul__(self,multip): # Overload the "*" operator 188 | 189 | if type(multip) is int: 190 | 191 | return GaussInt(self.r*multip, self.i*multip) 192 | 193 | else: 194 | 195 | return self.mult(multip) 196 | 197 | 198 | def __rmul__(self,multip): # Overload the "*" operator 199 | 200 | if type(multip) is int: 201 | 202 | return GaussInt(self.r*multip, self.i*multip) 203 | 204 | else: 205 | 206 | return self.mult(multip) 207 | 208 | 209 | def __imul__(self,multip): # Overload the "*=" operator 210 | 211 | self = self * multip 212 | 213 | return self 214 | 215 | 216 | def floordiv(self,divisor): 217 | 218 | if type(divisor) is int: 219 | 220 | numerator = (-self if (divisor < 0) else self) 221 | 222 | denominator = (-divisor if (divisor < 0) else divisor) 223 | 224 | if denominator == 0: 225 | 226 | raise ZeroDivisionError("{0:s} is null!".format(divisor)) 227 | 228 | else: 229 | 230 | numerator = self*divisor.conjugate() 231 | 232 | denominator = divisor.norm() # Recall that denominator >= 0 233 | 234 | if denominator == 0: 235 | 236 | raise ZeroDivisionError("{0:s} is null!".format(divisor)) 237 | 238 | candidate_r = numerator.r//denominator 239 | candidate_i = numerator.i//denominator 240 | 241 | # i.e. (candidate_r+1)*denominator-numerator.r < numerator.r-candidate_r*denominator 242 | if (2*candidate_r+1)*denominator < 2*numerator.r: 243 | 244 | candidate_r += 1 245 | 246 | # i.e. (candidate_i+1)*denominator-numerator.i < numerator.i-candidate_i*denominator 247 | if (2*candidate_i+1)*denominator < 2*numerator.i: 248 | 249 | candidate_i += 1 250 | 251 | return GaussInt(candidate_r,candidate_i) 252 | 253 | 254 | def __floordiv__(self,divisor): # Overload the "//" operator 255 | 256 | return self.floordiv(divisor) 257 | 258 | 259 | def __ifloordiv__(self,divisor): # Overload the "//=" operator 260 | 261 | self = self//divisor 262 | 263 | return self 264 | 265 | 266 | def mod(self,divisor): 267 | 268 | return self - divisor * (self//divisor) 269 | 270 | 271 | def __mod__(self,divisor): # Overload the "%" operator 272 | 273 | return self.mod(divisor) 274 | 275 | 276 | def __imod__(self,divisor): # Overload the "%=" operator 277 | 278 | self = self % divisor 279 | 280 | return self 281 | 282 | 283 | def divmod(self,divisor): 284 | 285 | q = self//divisor 286 | 287 | return q, self - divisor * q 288 | 289 | 290 | def xgcd(self,other): 291 | 292 | quot = GaussInt() 293 | 294 | a1 = GaussInt(1,0) 295 | b1 = GaussInt(0,0) 296 | 297 | a2 = GaussInt(0,0) 298 | b2 = GaussInt(1,0) 299 | 300 | a = self 301 | b = other 302 | 303 | if(b.norm() > a.norm()): # Need to start with a>b 304 | 305 | a,b = b,a # Swap a and b 306 | a1,b1,a2,b2 = a2,b2,a1,b1 # Swap (a1,b1) with (a2,b2) 307 | 308 | while (True): 309 | 310 | quot = a // b 311 | 312 | a %= b 313 | 314 | a1 -= quot*a2 315 | b1 -= quot*b2 316 | 317 | if (a == GaussInt(0,0)): 318 | 319 | return b, a2, b2 320 | 321 | quot = b // a 322 | 323 | b %= a 324 | 325 | a2 -= quot*a1 326 | b2 -= quot*b1 327 | 328 | if (b == GaussInt()): 329 | 330 | return a, a1, b1 331 | 332 | 333 | def Bézout(self, other): 334 | 335 | a = self 336 | b = other 337 | 338 | if a.norm() < b.norm(): 339 | 340 | (u, v, pgcd) = b.Bézout(a) 341 | 342 | return (v, u, pgcd) 343 | 344 | if b == 0: 345 | 346 | return (1, 0, a) 347 | 348 | u_n, u_n_moins_1, v_n, v_n_moins_1 = 0, 1, 1, 0 349 | 350 | while b.norm() > 0: 351 | 352 | q,r = a.divmod(b) 353 | 354 | u_n_plus_1 = u_n_moins_1 - q*u_n 355 | v_n_plus_1 = v_n_moins_1 - q*v_n 356 | 357 | a, b = b, r 358 | u_n_moins_1, u_n, v_n_moins_1, v_n = u_n, u_n_plus_1, v_n, v_n_plus_1 359 | 360 | return (u_n_moins_1, v_n_moins_1, a) 361 | 362 | 363 | def gcd(self,other): 364 | 365 | a = self 366 | b = other 367 | 368 | if a.norm() < b.norm(): 369 | 370 | return b.gcd(a) 371 | 372 | while b.norm() > 0: 373 | 374 | q,r = a.divmod(b) 375 | a,b = b,r 376 | 377 | return a 378 | 379 | 380 | def powmod(self, a_power, a_modulus): 381 | # We adapt the Binary Exponentiation algorithm with modulo 382 | 383 | result = GaussInt(1) 384 | 385 | auxilliary = GaussInt(self.r, self.i) 386 | 387 | while a_power: 388 | 389 | if a_power % 2: # If power is odd 390 | 391 | result = (result * auxilliary) % a_modulus 392 | 393 | # Divide the power by 2 394 | a_power >>= 1 395 | 396 | # Multiply base to itself 397 | auxilliary = (auxilliary * auxilliary) % a_modulus 398 | 399 | return result 400 | 401 | 402 | def __pow__(self, a_power): # Overload the "**" operator 403 | # We adapt the Binary Exponentiation algorithm (without modulo!) 404 | 405 | result = GaussInt(1) 406 | 407 | auxilliary = GaussInt(self.r, self.i) 408 | 409 | while a_power: 410 | 411 | if a_power % 2: # If power is odd 412 | 413 | result = result * auxilliary 414 | 415 | # Divide the power by 2 416 | a_power >>= 1 417 | 418 | # Multiply base to itself 419 | auxilliary = auxilliary * auxilliary 420 | 421 | return result 422 | 423 | 424 | def isprime(self): 425 | """n.isprime() - Test whether the GaussInt n is prime using a variety of pseudoprime tests.""" 426 | # Multiply by (1,i,-1,-i) to rotate to first quadrant (similar to abs) 427 | if (self.r < 0): self *= (-1) 428 | if (self.i < 0): self *= GaussInt(0,1) 429 | # Check some small non-primes 430 | if (self in [GaussInt(0,0), GaussInt(1,0), GaussInt(0,1)]): return False 431 | # Check some small primes 432 | if (self in [GaussInt(1,1), GaussInt(2,1), GaussInt(1,2), GaussInt(3,0), GaussInt(0,3), GaussInt(3,2), GaussInt(2,3)]): 433 | return True 434 | return self.isprimeF(2) and self.isprimeF(3) and self.isprimeF(5) 435 | 436 | 437 | def isprimeF(self,base): 438 | """n.isprimeF(base) - Test whether the GaussInt n is prime using the 439 | Gaussian Integer analogue of the Fermat pseudoprime test.""" 440 | if type(base) is not GaussInt: 441 | base = GaussInt(base) # Coerce if base not GaussInt (works for int or complex) 442 | return base.powmod(self.norm()-1,self) == GaussInt(1,0) 443 | # Note: Possibly more effective would be to use the characterization of primes 444 | # in the Gaussian Integers based on the primality of their norm and reducing mod 4. 445 | # This depends on the characterization of the ideal class group, and only works for 446 | # simple rings of algebraic integers. 447 | 448 | 449 | def factor(self): 450 | """n.factor() - Find a prime factor of Gaussian Integer n using a variety of methods.""" 451 | if (self.isprime()): return n 452 | for fact in [GaussInt(1,1), GaussInt(2,1), GaussInt(1,2), 453 | GaussInt(3,0), GaussInt(3,2), GaussInt(2,3)]: 454 | if self%fact == 0: return fact 455 | return self.factorPR() # Needs work - no guarantee that a prime factor will be returned 456 | 457 | 458 | def factors(self): 459 | """n.factors() - Return a sorted list of the prime factors of Gaussian Integer n.""" 460 | if (self.isprime()): 461 | return [self] 462 | fact = self.factor() 463 | if (fact == 1): return "Unable to factor "+str(n) 464 | facts = (self/fact).factors() + fact.factors() 465 | return facts 466 | 467 | 468 | def factorPR(self): # TODO: learn and test 469 | """n.factorPR() - Find a factor of Gaussian Integer n using the analogue of the Pollard Rho method. 470 | Note: This method will occasionally fail.""" 471 | for slow in [2,3,4,6]: 472 | numsteps=2*math.floor(math.sqrt(math.sqrt(self.norm()))); fast=slow; i=1 473 | while i>> from GaussInt import * 492 | # >>> a = GaussInt(1,0) 493 | 494 | ############################################################################ 495 | # License: Freely available for use, abuse and modification 496 | # (this is the Simplified BSD License, aka FreeBSD license) 497 | # Copyright 2001-2013 Robert Campbell. All rights reserved. 498 | # 499 | # Redistribution and use in source and binary forms, with or without 500 | # modification, are permitted provided that the following conditions are met: 501 | # 502 | # 1. Redistributions of source code must retain the above copyright notice, 503 | # this list of conditions and the following disclaimer. 504 | # 505 | # 2. Redistributions in binary form must reproduce the above copyright 506 | # notice, this list of conditions and the following disclaimer in 507 | # the documentation and/or other materials provided with the distribution. 508 | ############################################################################ 509 | 510 | -------------------------------------------------------------------------------- /numbthy.py: -------------------------------------------------------------------------------- 1 | ###################################################################################### 2 | # NUMBTHY.PY 3 | # Basic Number Theory functions implemented in Python 4 | # Note: Version 0.7 changes some function names to align with SAGE 5 | # Note: Version 0.8 is compatible with both Python 2.5+ and 3.x 6 | # Author: Robert Campbell, 7 | # Contributors: Ege Alpay; ZhiHang Fan 8 | # Date: 13 Oct, 2019 9 | # Version 0.84 10 | # License: Simplified BSD (see details at bottom) 11 | ###################################################################################### 12 | """Basic number theory functions. 13 | Functions implemented are: 14 | gcd(a,b) - Compute the greatest common divisor of a and b. 15 | xgcd(a,b) - Find [g,x,y] such that g=gcd(a,b) and g = ax + by. 16 | power_mod(b,e,n) - Compute b^e mod n efficiently. 17 | inverse_mod(b,n) - Compute 1/b mod n. 18 | is_prime(n) - Test whether n is prime using a variety of pseudoprime tests. 19 | euler_criterion(a, p) - Test whether a is a quadratic residue mod p 20 | euler_phi(n) - Compute Euler's Phi function of n - the number of integers strictly less than n which are coprime to n. 21 | carmichael_lambda(n) - Compute Carmichael's Lambda function of n - the smallest exponent e such that b**e = 1 for all b coprime to n. 22 | factor(n) - Return a sorted list of the prime factors of n with exponents. 23 | prime_divisors(n) - Returns a sorted list of the prime divisors of n. 24 | is_primitive_root(g,n) - Test whether g is primitive - generates the group of units mod n. 25 | sqrtmod(a,n) - Compute sqrt(a) mod n using various algorithms. 26 | TSRsqrtmod(a,grpord,n) - Compute sqrt(a) mod n using Tonelli-Shanks-RESSOL algorithm. 27 | Usage and help for the module is printed with the command help(numbthy) and a list of functions in the module with the command dir(numbthy). 28 | Some functions which are used internally and names used prior to ver 0.7 of existing functions are: 29 | isprime(n) - Test whether n is prime using a variety of pseudoprime tests. (Renamed is_prime(b,n) in ver 0.7) 30 | isprimeF(n,b) - Test whether n is prime or a Fermat pseudoprime to base b. 31 | isprimeE(n,b) - Test whether n is prime or an Euler pseudoprime to base b. 32 | factorone(n) - Find a factor of n using a variety of methods. 33 | factors(n) - Return a sorted list of the prime factors of n. (Prior to ver 0.7 named factor(n)) 34 | factorPR(n) - Find a factor of n using the Pollard Rho method. 35 | invmod(b,n) - Compute 1/b mod n. (Renamed inverse_mod(b,n) in ver 0.7) 36 | powmod(b,e,n) - Compute b^e mod n efficiently. (Renamed power_mod(b,e,n) in ver 0.7) 37 | eulerphi(n) - Compute Euler's Phi function of n - the number of integers strictly less than n which are coprime to n. (Renamed euler_phi(n) in ver 0.7) 38 | carmichaellambda(n) - Compute Carmichael's Lambda function of n - the smallest exponent e such that b**e = 1 for all b coprime to n. (Renamed carmichael_lambda(n) in ver 0.7) 39 | isprimitive(g,n) - Test whether g is primitive mod n. (Renamed is_primitive_root(g,n) in ver 0.8) 40 | """ 41 | 42 | __version__ = '0.84' # Format specified in Python PEP 396 43 | Version = 'NUMBTHY.PY, version ' + __version__ + ', 13 Oct, 2019, by Robert Campbell, ' 44 | 45 | import math # Use sqrt, floor 46 | import functools # Use reduce (Python 2.5+ and 3.x) 47 | 48 | def euler_criterion(a, p): 49 | """p is odd prime, a is positive integer. Euler's Criterion will check if 50 | a is a quadratic residue mod p. If yes, returns True. If a is a non-residue 51 | mod p, then False""" 52 | return pow(a, (p - 1) // 2, p) == 1 53 | 54 | def gcd(a,b): 55 | """gcd(a,b) returns the greatest common divisor of the integers a and b.""" 56 | a = abs(a); b = abs(b) 57 | while (a > 0): 58 | b = b % a 59 | tmp=a; a=b; b=tmp 60 | return b 61 | 62 | def xgcd(a,b): 63 | """xgcd(a,b) returns a tuple of form (g,x,y), where g is gcd(a,b) and 64 | x,y satisfy the equation g = ax + by.""" 65 | a1=1; b1=0; a2=0; b2=1; aneg=1; bneg=1 66 | if(a < 0): 67 | a = -a; aneg=-1 68 | if(b < 0): 69 | b = -b; bneg=-1 70 | while (1): 71 | quot = -(a // b) 72 | a = a % b 73 | a1 = a1 + quot*a2; b1 = b1 + quot*b2 74 | if(a == 0): 75 | return (b, a2*aneg, b2*bneg) 76 | quot = -(b // a) 77 | b = b % a; 78 | a2 = a2 + quot*a1; b2 = b2 + quot*b1 79 | if(b == 0): 80 | return (a, a1*aneg, b1*bneg) 81 | 82 | def power_mod(b,e,n): 83 | """power_mod(b,e,n) computes the eth power of b mod n. 84 | (Actually, this is not needed, as pow(b,e,n) does the same thing for positive integers. 85 | This will be useful in future for non-integers or inverses.)""" 86 | if e<0: # Negative powers can be computed if gcd(b,n)=1 87 | e = -e 88 | b = inverse_mod(b,n) 89 | accum = 1; i = 0; bpow2 = b 90 | while ((e>>i)>0): 91 | if((e>>i) & 1): 92 | accum = (accum*bpow2) % n 93 | bpow2 = (bpow2*bpow2) % n 94 | i+=1 95 | return accum 96 | 97 | def inverse_mod(a,n): 98 | """inverse_mod(b,n) - Compute 1/b mod n.""" 99 | (g,xa,xb) = xgcd(a,n) 100 | if(g != 1): raise ValueError("***** Error *****: {0} has no inverse (mod {1}) as their gcd is {2}, not 1.".format(a,n,g)) 101 | return xa % n 102 | 103 | def is_prime(n): 104 | """is_prime(n) - Test whether n is prime using a variety of pseudoprime tests.""" 105 | if n<0: n=-n # Only deal with positive integers 106 | if n<2: return False # 0 and 1 are not prime 107 | if (n in (2,3,5,7,11,13,17,19,23,29)): return True 108 | return isprimeE(n,2) and isprimeE(n,3) and isprimeE(n,5) 109 | 110 | def factor(n): 111 | """factor(n) - Return a sorted list of the prime factors of n with exponents.""" 112 | # Rewritten to align with SAGE. Previous semantics available as factors(n). 113 | if ((abs(n) == 1) or (n == 0)): raise ValueError('Unable to factor {0}'.format(n)) 114 | factspow = [] 115 | currfact = None 116 | thecount = 1 117 | for thefact in factors(n): 118 | if thefact != currfact: 119 | if currfact != None: 120 | factspow += [(currfact,thecount)] 121 | currfact = thefact 122 | thecount = 1 123 | else: 124 | thecount += 1 125 | factspow += [(thefact,thecount)] 126 | return tuple(factspow) 127 | 128 | def prime_divisors(n): 129 | """prime_divisors(n) - Returns a sorted list of the prime divisors of n.""" 130 | return tuple(set(factors(n))) 131 | 132 | def euler_phi(n): 133 | """euler_phi(n) - Computer Euler's Phi function of n - the number of integers 134 | strictly less than n which are coprime to n. Otherwise defined as the order 135 | of the group of integers mod n.""" 136 | if n == 1: return 1 137 | if n <= 0: return 0 138 | # For each prime factor p with multiplicity n, a factor of (p**(n-1))*(p-1) 139 | return functools.reduce(lambda a,x:a*(x[0]**(x[1]-1))*(x[0]-1),factor(n),1) 140 | 141 | def carmichael_lambda(n): 142 | """carmichael_lambda(n) - Compute Carmichael's Lambda function 143 | of n - the smallest exponent e such that b**e = 1 for all b coprime to n. 144 | Otherwise defined as the exponent of the group of integers mod n.""" 145 | # SAGE equivalent is sage.crypto.util.carmichael_lambda(n) 146 | if n == 1: return 1 147 | if n <= 0: raise ValueError("*** Error ***: Input n for carmichael_lambda(n) must be a positive integer.") 148 | # The gcd of (p**(e-1))*(p-1) for each prime factor p with multiplicity e (exception is p=2). 149 | def _carmichael_lambda_primepow(theprime,thepow): 150 | if ((theprime == 2) and (thepow >= 3)): 151 | return (2**(thepow-2)) # Z_(2**e) is not cyclic for e>=3 152 | else: 153 | return (theprime-1)*(theprime**(thepow-1)) 154 | return functools.reduce(lambda accum,x:(accum*x)//gcd(accum,x),tuple(_carmichael_lambda_primepow(*primepow) for primepow in factor(n)),1) 155 | 156 | def is_primitive_root(g,n): 157 | """is_primitive_root(g,n) - Test whether g is primitive - generates the group of units mod n.""" 158 | # SAGE equivalent is mod(g,n).is_primitive_root() in IntegerMod class 159 | if gcd(g,n) != 1: return False # Not in the group of units 160 | order = euler_phi(n) 161 | if carmichael_lambda(n) != order: return False # Group of units isn't cyclic 162 | orderfacts = prime_divisors(order) 163 | for fact in orderfacts: 164 | if pow(g,order//fact,n) == 1: return False 165 | return True 166 | 167 | def sqrtmod(a,n): 168 | """sqrtmod(a,n) - Compute sqrt(a) mod n using various algorithms. 169 | Currently n must be prime, but will be extended to general n (when I get the time).""" 170 | # SAGE equivalent is mod(g,n).sqrt() in IntegerMod class 171 | if(not isprime(n)): raise ValueError("*** Error ***: Currently can only compute sqrtmod(a,n) for prime n.") 172 | if(pow(a,(n-1)//2,n)!=1): raise ValueError("*** Error ***: a is not quadratic residue, so sqrtmod(a,n) has no answer.") 173 | return TSRsqrtmod(a,n-1,n) 174 | 175 | def TSRsqrtmod(a,grpord,p): 176 | """TSRsqrtmod(a,grpord,p) - Compute sqrt(a) mod n using Tonelli-Shanks-RESSOL algorithm. 177 | Here integers mod n must form a cyclic group of order grpord.""" 178 | # Rewrite group order as non2*(2^pow2) 179 | ordpow2=0; non2=grpord 180 | while(not ((non2&0x01)==1)): 181 | ordpow2+=1; non2//=2 182 | # Find 2-primitive g (i.e. non-QR) 183 | for g in range(2,grpord-1): 184 | if (pow(g,grpord//2,p)!=1): 185 | break 186 | g = pow(g,non2,p) 187 | # Tweak a by appropriate power of g, so result is (2^ordpow2)-residue 188 | gpow=0; atweak=a 189 | for pow2 in range(0,ordpow2+1): 190 | if(pow(atweak,non2*2**(ordpow2-pow2),p)!=1): 191 | gpow+=2**(pow2-1) 192 | atweak = (atweak * pow(g,2**(pow2-1),p)) % p 193 | # Assert: atweak now is (2**pow2)-residue 194 | # Now a*(g**powg) is in cyclic group of odd order non2 - can sqrt directly 195 | d = invmod(2,non2) 196 | tmp = pow(a*pow(g,gpow,p),d,p) # sqrt(a*(g**gpow)) 197 | return (tmp*inverse_mod(pow(g,gpow//2,p),p)) % p # sqrt(a*(g**gpow))//g**(gpow//2) 198 | 199 | ################ Internally used functions ######################################### 200 | 201 | def isprimeF(n,b): 202 | """isprimeF(n) - Test whether n is prime or a Fermat pseudoprime to base b.""" 203 | return (pow(b,n-1,n) == 1) 204 | 205 | def isprimeE(n,b): 206 | """isprimeE(n) - Test whether n is prime or an Euler pseudoprime to base b.""" 207 | if (not isprimeF(n,b)): return False 208 | r = n-1 209 | while (r % 2 == 0): r //= 2 210 | c = pow(b,r,n) 211 | if (c == 1): return True 212 | while (1): 213 | if (c == 1): return False 214 | if (c == n-1): return True 215 | c = pow(c,2,n) 216 | 217 | def factorone(n): 218 | """factorone(n) - Find a prime factor of n using a variety of methods.""" 219 | if (is_prime(n)): return n 220 | for fact in (2,3,5,7,11,13,17,19,23,29): 221 | if n%fact == 0: return fact 222 | return factorPR(n) # Needs work - no guarantee that a prime factor will be returned 223 | 224 | def factors(n): 225 | """factors(n) - Return a sorted list of the prime factors of n. (Prior to ver 0.7 named factor(n))""" 226 | if n<0: n=-n # Only deal with positive integers 227 | if (is_prime(n)): 228 | return [n] 229 | fact = factorone(n) 230 | if ((abs(n) == 1) or (n == 0)): raise ValueError('Unable to factor \"{0}\"'.format(n)) 231 | facts = factors(n//fact) + factors(fact) 232 | facts.sort() 233 | return facts 234 | 235 | def factorPR(n): 236 | """factorPR(n) - Find a factor of n using the Pollard Rho method. 237 | Note: This method will occasionally fail.""" 238 | numsteps=2*math.floor(math.sqrt(math.sqrt(n))) 239 | for additive in range(1,5): 240 | fast=slow=1; i=1 241 | while i 5 | # Date: 14 Oct 2014 6 | # Version 0.8 7 | ###################################################################################### 8 | 9 | import unittest 10 | import numbthy 11 | import random 12 | 13 | class Test_numbthy(unittest.TestCase): 14 | 15 | def test_gcd(self): 16 | for testcase in ((1,1,1),(-1,1,1),(6,0,6),(0,6,6),(6,6,6),(1234,5678,2),(12345675,34567890,2469135)): 17 | self.assertEqual(numbthy.gcd(testcase[0],testcase[1]),testcase[2]) 18 | 19 | def test_xgcd(self): 20 | for testcase in ((1,-1,1,0,-1),(6,8,2,-1,1),(12345,2345,5,87,-458),(98760,76540,20,1898,-2449)): 21 | self.assertEqual(numbthy.xgcd(testcase[0],testcase[1]),(testcase[2],testcase[3],testcase[4])) 22 | 23 | def test_xgcd_random(self): 24 | for testnum in range(10): 25 | a = random.randint(0,10**20); b = random.randint(0,10**20); 26 | (g,x,y) = numbthy.xgcd(a,b) 27 | try: 28 | self.assertEqual(g,a*x+b*y) 29 | break 30 | except AssertionError: 31 | raise AssertionError("***** Error in xgcd *****: {2} != ({0})*({3}) + ({1})*({4})".format(a,b,g,x,y)) 32 | 33 | def test_power_mod(self): 34 | for testcase in ((2,5,13,6),(2,-21,31,16),(-2,-21,31,15),(0,5,31,0),(5,0,31,1)): 35 | self.assertEqual(numbthy.power_mod(testcase[0],testcase[1],testcase[2]),testcase[3]) 36 | 37 | def test_power_mod_raise_ValueError(self): # Error for power_mod(2,-11,26) 38 | self.assertRaises(ValueError,numbthy.power_mod,2,-11,26) 39 | 40 | def test_power_mod_raise_ZeroDivisionError(self): # Error for power_mod(2,5,0) 41 | self.assertRaises(ZeroDivisionError,numbthy.power_mod,2,5,0) 42 | 43 | def test_is_prime(self): 44 | for testcase in ((1,False),(0,False),(-2,True),(19,True),(1236,False),(1237,True),(321197185,False)): 45 | self.assertEqual(numbthy.is_prime(testcase[0]),testcase[1]) 46 | 47 | def test_euler_phi(self): 48 | for testcase in ((-5,0),(0,0),(1,1),(2,1),(4,2),(8*7,24),(2**4*3**3*7**2*19,108864)): 49 | self.assertEqual(numbthy.euler_phi(testcase[0]),testcase[1]) 50 | 51 | def test_carmichael_lambda(self): 52 | for testcase in ((1,1),(2,1),(4,2),(8*7,6),(2**4*3**3*7**2*19,252)): 53 | self.assertEqual(numbthy.carmichael_lambda(testcase[0]),testcase[1]) 54 | 55 | def test_carmichael_lambda_raise_ValueError(self): # Error for carmichael_lambda(0) 56 | self.assertRaises(ValueError,numbthy.carmichael_lambda,0) 57 | 58 | def test_factor(self): 59 | for testcase in ((-15,((3, 1), (5, 1))),(1234561000,((2, 3), (5, 3), (211, 1), (5851, 1)))): 60 | self.assertEqual(numbthy.factor(testcase[0]),testcase[1]) 61 | 62 | if __name__ == '__main__': 63 | unittest.main() 64 | 65 | --------------------------------------------------------------------------------