Source code for arithmetics

#!/Usr/bin/env python3
"""
Python3+ implementation of arithmetics tools

Copyright (C) 2016-2025 Raymond Bisdorff

Tools gathered for doing arithmetics.
Mainly inspired from G.A. Jones & J.M. Jones,
Elementary Number Theroy,
Springer Verlag London 1998.

    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""
#######################

__version__ = "$Revision: Python 3.13.2 $"

from arithmetics import *
from digraphs import Digraph
from decimal import Decimal
from collections import OrderedDict

#---------------
[docs] class QuadraticResiduesDigraph(Digraph): """ The **Legendre** symbol *(a/p)* of any pair of non null integers *a* and *p* is: - **0** if *a* = 0 (mod p); - **1** if *a* is a quadratic residue in *Zp*, ie *a* in *Qp*; - **-1** if *a* is a non quadratic residue unit in *Zp*, ie *a* in *Up* - *Qp*. The Legendre symbol hence defines a bipolar valuation on pairs of non null integers. The **reciprocity theorem** of the Legendre symbol states that, for *p* being an odd prime, *(a/p)* = *(p/a)*, apart from those pairs *(a/p)*, where *a* = *p* = 3 (mod 4). In this case, *(a/p)* = -*(p/a)*. We may graphically illustrate the reciprocity theorem as follows:: >>> from arithmetics import * >>> primesBelow20 = primesBelow(20,Odd=True) [3,5,7,11,13,17,19] >>> leg = QuadraticResiduesDigraph(primesBelow20) >>> from digraphs import AsymmetricPartialDigraph >>> aleg = AsymmetricPartialDigraph(leg) >>> aleg.exportGraphViz('legendreAsym') .. image:: legendreAsym.png :alt: Quadratic residues digraph asymmetric part :width: 300 px :align: center """ def __init__(self,integers=[3,5,7,11,13,17,19]): """ By default we consider only primes, but the Legendre symbol works for any integer sequence not containing 0. """ import sys,array,copy self.name = 'legendreDigraph' self.order = len(integers) actionsList = integers actions = OrderedDict() for x in actionsList: if x == 0: print('Only positive integers are allowed!') return actions[x] = {'name':str(x),'shortName':str(x)} self.actions = actions Max = Decimal('1') Min = Decimal('-1') Med = Decimal('0') self.valuationdomain = {'min':Min,'med':Med,'max':Max} relation = {} for x in actions: relation[x] = {} for y in actions: relation[x][y] = Med for p in actions: Units = set(zn_units(p)) Sqrts = set(zn_squareroots(p)) for a in actions: q,r = divmod(a,p) if r == 0: relation[p][a] = Med else: if r in Sqrts: relation[p][a] = Max elif r in Units-Sqrts: relation[p][a] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
# ------ Bachet bipolar {-1,0,1} base 3 encoded integers --------------- # Discrete Mathematics lectures 2008 # (c) 2025 RB
[docs] class BachetNumber(object): """ Bipolar-valued {-1,0,+1} base 3 encoded integers due to Claude Gaspard Bachet de Méziriac (1621) https://en.wikipedia.org/wiki/Claude_Gaspar_Bachet_de_M%C3%A9ziriac >>> from arithmetics import BachetNumber >>> n1 = BachetNumber(12) >>> n1 *------- Bachet number description ------* Instance class : BachetNumber String : '+1+10' Vector : [1, 1, 0] Length : 3 Value : 12 Attributes : ['vector'] >>> n2 = BachetNumber(vector=[1,1,1]) >>> n2 *------- Bachet number description ------* Instance class : BachetNumber String : '+1+1+1' Vector : [1, 1, 1] Length : 3 Value : 13 Attributes : ['vector'] >>> n3 = n1 + n2 >>> n3 *------- Bachet number description ------* Instance class : BachetNumber String : '+10-1+1' Vector : [1, 0, -1, 1] Length : 4 Value : 25 Attributes : ['vector'] >>> print('%s (%d) + %s (%d) = %s (%d)' ... % (n1, n1.value(), n2, n2.value(), n3, n3.value() )) '+1+10' (12) + '+1+1+1' (13) = 10-11 (25) >>> print('length of %s = %d' % (n1, len(n1))) length of '+1+10' = 3 >>> n4 = n1.reverse() >>> n5 = -n2 >>> print('%s (%d) + %s (%d) = %s (%d)' ... % ( n4, n4.value(), n5, n5.value(),n4 + n5, (n4+n5).value() )) '0+1+1' (4) + '-1-1-1' (-13) = '-100' (-9) >>> n6 = n5.permute(1,2) >>> n6 """ def __repr__(self): """ Default presentation method for Bchet number instances. """ reprString = '*------- Bachet number description ------*\n' reprString += 'Instance class : %s\n' % self.__class__.__name__ reprString += 'String : %s\n' % str(self) reprString += 'Vector : %s\n' % self.vector reprString += 'Length : %d\n' % len(self) reprString += 'Value : %d\n' % self.value() reprString += 'Attributes : %s\n' % list(self.__dict__.keys()) return reprString def __init__(self,num_int=None,vector=None,length=0): """ Tranforms a potentially signed integer into a Bachet number. """ if num_int is None: if vector is not None: vl = len(vector) if vl >= length: self.vector = vector else: nz = length - vl self.vector = [0 for i in range(nz)] self.vector = self.vector + vector else: self.vector=[0 for i in range(length)] else: self.vector = self._int2bachet(num_int) vl = len(self.vector) if vl < length: nz = length - vl vector = [0 for i in range(nz)] self.vector = vector + self.vector def __str__(self): """ Defines the printable string version of a Bachet number. """ bachet_string = '\'' for i in range(len(self.vector)): if self.vector[i] != 0: bachet_string += '%+d' % (self.vector[i]) else: bachet_string += '%d' % (self.vector[i]) bachet_string += "\'" return bachet_string def __neg__(self): """ Defines an unary negating operator for Bachet encoded numbers. """ from copy import deepcopy neg = deepcopy(self) for i in range(len(neg)): neg.vector[i] = neg.vector[i] * -1 return neg ## def __add__(self,other): ## """ ## Defines the addition operator for Bachet encoded numbers ## """ ## n1 = self.value() ## n2 = other.value() ## n3 = n1 + n2 ## return BachetNumber(n3) def __add__(self,other,Debug=False): """ Defines the addition operator for Bachet encoded numbers. """ from copy import deepcopy srv = self.reverse() orv = other.reverse() n1 = len(self) n2 = len(other) if n1 >= n2: new = deepcopy(self) new.vector = [0 for i in range(n1)] else: new = deepcopy(other) new.vector = [0 for i in range(n2)] n = max(n1,n2) reste = 0 for i in range(n): try: psi = srv.vector[i] except: psi = 0 try: poi = orv.vector[i] except: poi = 0 pi = psi + poi + reste if Debug: print('i,psi,poi,reste,pi', i,psi,poi,reste,pi) if pi == 2: new.vector[i] = -1 reste = 1 elif pi == 3: new.vector[i] = 0 reste = 1 elif pi == -2: new.vector[i] = 1 reste = -1 elif pi == -3: new.vector[i] = 0 reste = -1 else: new.vector[i] = pi reste = 0 if Debug: print("reste",reste) if reste != 0: if Debug: print('add',reste) new.vector = new.vector + [reste] if Debug: print(new.vector) result = new.reverse() return result def __mul__(self,other): """ Defines the multiplication operator for Bachet encoded numbers. """ n1 = self.value() n2 = other.value() n3 = n1 * n2 return BachetNumber(n3) def _base10to3(self,num): """ Change a base 10 number to a base 3 number. """ new_num_string = '' current = num while current != 0: remainder = current%3 remainder_string = str(remainder) new_num_string = remainder_string + new_num_string current = current//3 return new_num_string def _base3toBachet(self,num_string): """ Converts a base 3 encoded integer into a bipolar {-1,0,+1} encoded one. """ new_vector=[0 for x in range(len(num_string))] reste = 0 for i in range(len(num_string)-1,-1,-1): num = eval(num_string[i])+reste if num == 2: new_vector[i] = -1 reste = 1 elif num == 3: new_vector[i] = 0 reste = 1 else: new_vector[i] = num reste = 0 if reste == 1: new_vector = [1] + new_vector return new_vector def _int2bachet(self,num_int): """ Converts a signed integer into a Bachet encoded number. """ if num_int < 0: unsigned_num_int = abs(num_int) else: unsigned_num_int = num_int base3_unsigned_num_int = self._base10to3(unsigned_num_int) bachet_unsigned_num_int = self._base3toBachet(base3_unsigned_num_int) if num_int > 0: return bachet_unsigned_num_int elif num_int == 0: return '0',[0] else: bachet_vector = bachet_unsigned_num_int for i in range(len(bachet_unsigned_num_int)): bachet_vector[i] = bachet_unsigned_num_int[i]*-1 return bachet_vector
[docs] def value(self): """ Renders the integer corresponding to the Bachet number. """ result_int = 0 nv = len(self.vector) base3Power = 1 # 3**0 for i in range(nv): result_int += base3Power*self.vector[nv-i-1] base3Power *= 3 # 3**i return result_int
def __len__(self): """ Returns the length of the Bachet encoding """ return len(self.vector)
[docs] def reverse(self): """ Reverses the Bachet vector. Returns a modified Bachet number. """ from copy import deepcopy rev = deepcopy(self) nv = len(self.vector) result = [0 for i in range(nv)] for i in range(nv): result[i] = self.vector[nv-i-1] rev.vector = result return rev
[docs] def permute(self,i,j): """ Swaps positions i-1 an j-1 in the self Bachet vector. Returns a modified Bachet number. """ if i > len(self) or i < 1: print('Error: index i (%d) not fitting the vector length !!!' % i) print(self) return elif j > len(self) or j < 1: print('Error: index j (%d) not fitting the vector length !!!' % j) print(self) return from copy import deepcopy rev = deepcopy(self) rev.vector[j-1] = self.vector[i-1] rev.vector[i-1] = self.vector[j-1] return rev
#------------- end of BachetNumber class ------------------
[docs] def primesBelow(N,Odd=False): """ http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 Input N>=6, Returns a list of primes, 2 <= p < N >>> import arithmetics as ar >>> ar.primesBelow(30,Odd=True) [3, 5, 7, 11, 13, 17, 19, 23, 29] """ correction = N % 6 > 1 N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6] sieve = [True] * (N // 3) sieve[0] = False for i in range(int(N ** .5) // 3 + 1): if sieve[i]: k = (3 * i + 1) | 1 sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1) sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1) primes = [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]] if Odd: primes.remove(2) return primes
_smallprimeset = set(primesBelow(100000)) _smallprimesetSize = 100000
[docs] def isprime(n, precision=7): """ http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time """ if n == 1 or n % 2 == 0: return False elif n < 1: raise ValueError("Out of bounds, first argument must be > 0") elif n < _smallprimesetSize: return n in _smallprimeset d = n - 1 s = 0 while d % 2 == 0: d //= 2 s += 1 for repeat in range(precision): a = random.randrange(2, n - 2) x = pow(a, d, n) if x == 1 or x == n - 1: continue for r in range(s - 1): x = pow(x, 2, n) if x == 1: return False if x == n - 1: break else: return False return True
def _pollard_brent(n): """ https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/ """ if n % 2 == 0: return 2 if n % 3 == 0: return 3 import random y, c, m = random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1) g, r, q = 1, 1, 1 while g == 1: x = y for i in range(r): y = (pow(y, 2, n) + c) % n k = 0 while k < r and g==1: ys = y for i in range(min(m, r-k)): y = (pow(y, 2, n) + c) % n q = q * abs(x-y) % n g = gcd(q, n) k += m r *= 2 if g == n: while True: ys = (pow(ys, 2, n) + c) % n g = gcd(abs(x - ys), n) if g > 1: break return g _smallprimes = primesBelow(1000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
[docs] def primeFactors(n, sort=True): """ >>> import arithmetics as ar >>> ar.primeFactors(12345) [3, 5, 823] """ factors = [] limit = int(n ** .5) + 1 for checker in _smallprimes: if checker > limit: break while n % checker == 0: factors.append(checker) n //= checker limit = int(n ** .5) + 1 if checker > limit: break if n < 2: return factors while n > 1: if isprime(n): factors.append(n) break factor = _pollard_brent(n) # trial division did not fully factor, switch to pollard-brent factors.extend(primeFactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent n //= factor if sort: factors.sort() return factors
def factorization(n): factors = OrderedDict() for p1 in primeFactors(n): try: factors[p1] += 1 except KeyError: factors[p1] = 1 return factors
[docs] def moebius_mu(n): """ Implements the Moebius mu function on N based on n's prime factorization: n = p1^e1 * ... * pk^ek with each ei >= 1 for i = 1, ..., k. mu = 0 if ei > 1 for some i = 1, ... k else mu = (-1)^k. >>> import arithmetics as ar >>> ar.factorization(15) OrderedDict({3: 1, 5: 1}) >>> ar.moebius_mu(15) 1 >>> ar.factorization(12345) OrderedDict({3: 1, 5: 1, 823: 1}) >>> ar.moebius_mu(12345) -1 >>> ar.factorization(12321) OrderedDict({3: 2, 37: 2}) >>> ar.moebius_mu(12321) 0 """ if n < 1: print('n must be a positive integer!') return factors = factorization(n) k = len(factors) SquareFree = True for p in factors: if factors[p] > 1: SquareFree = False break if SquareFree: return pow(-1,k) else: return 0
[docs] def divisors(n,Sorted=True): """ Renders the list of divisors of integer n. >>> import arithmetics as ar >>> ar.divisors(12) [1, 2, 3, 4, 6, 12] """ if n == 0: return Dn = [n] for i in range(2,n): q,r = divmod(n,i) if r == 0: Dn.append(q) Dn.append(1) if Sorted: Dn.reverse() return Dn
[docs] def divisorsFunction(k,n): """ generic divisor function: - the number of divisors of *n* is divisorsFunction(0,n) - the sum of the divisors of *n* is divisorsFunction(1,n) """ tot = 0 for d in divisors(n): tot += pow(d,k) return tot
_totients = {}
[docs] def totient(n): """ Implements the totient function rendering Euler's number of coprime elements a in Zn. >>> import arithmetics as ar >>> ar.totient(12) 4 """ if n == 0: return 1 try: return _totients[n] except KeyError: pass tot = 1 for p, exp in factorization(n).items(): tot *= (p - 1) * p ** (exp - 1) _totients[n] = tot return tot
[docs] def simpleContinuedFraction(p, q, Comments=False): """ Renders the continued fraction [a_0,a_1,a_2,...,a_n] of the ratio of two positive integers p > 0 and q > 0 by following Euclide's division algorithm. >>> import arithmetics as ar >>> ar.simpleContinuedFraction(12,7,Comments=True) 12//7 = 1R5 7//5 = 1R2 5//2 = 2R1 2//1 = 2R0 [1, 1, 2, 2] """ if p < 0 or q < 0: Print('Error: p and q arguments must be positive integers!') return None if type(p) != int or type(q) != int: Print('Error: p and q arguments must be positive integers!') return None res = [p//q] if Comments: print('%d//%d = %dR%d' % (p,q,(p//q),(p%q)) ) while q > 0: p, q = q, p % q if q > 1: res.append(p//q) elif q == 1 : res.append(p) if Comments: if q > 0: print('%d//%d = %dR%d' % (p,q,(p//q),(p%q)) ) return res
[docs] def simpleConvergents(cf, AsFloats=False): """ Renders the convergents *pi* and *qi* for *i* = 0 to *n* in list format for a given simple continued fraction of two positive integer *a* and *b*. - If AsFloats==True, the float value *fi = pi/qi* are also provided. - The return delivers a tuple (p,q) or (p,q,f). - p[-1] and q[-1] deliver the initial numerator *a* and denominator *b*. - f[-1] delivers the float value of the rational fraction *a/b*. >>> import arithmetics as ar >>> cf = ar.simpleContinuedFraction(12,7) >>> cf [1, 1, 2, 2] >>> p,q,f = ar.simpleConvergents(cf, AsFloats=True) >>> p [1, 2, 5, 12] >>> q [1, 1, 3, 7] >>> f [1.0, 2.0, 1.6666666666666667, 1.7142857142857142] """ n = len(cf) a = cf p = [a[0],(a[1]*a[0] + 1)] q = [1,a[1]] for i in range(2,n): p.append(a[i]*p[i-1] + p[i-2]) q.append(a[i]*q[i-1] + q[i-2]) f = [] if AsFloats: for i in range(n): f.append(p[i]/q[i]) return (p,q,f) else: return (p,q)
[docs] def decimalEvalContinuedFraction(cf): """ Backwise recursive evaluation: ev_i-1 + 1/ev_i, for i = n,..,1 of the continued fraction cf = [a_0,a_1,a_2,...,a_n] and where a_0 corresponds to its integer part. >>> import arithmetics as ar >>> ar.decimalEvalContinuedFraction([1, 1, 2, 1, 1]) # 12/7 Decimal('1.714285714285714285714285714') >>> 12/7 1.7142857142857142 """ from decimal import Decimal n = len(cf) - 1 res = Decimal(str(cf[n])) for i in range(n-1,0,-1): res = Decimal(str(cf[i])) + Decimal('1')/res res = Decimal(str(cf[0])) + Decimal('1')/res return res
[docs] def cf2Rational(cf, AsDecimal=False, Debug=False): """ Converts the finite continued Fraction *cf* back to its corresponding rational number expression. Returns a/b or (a/b, Decimal(a/b)) if AsDecimal is True. >>> import arithmetics as ar >>> ar.simpleContinuedFraction(75,8) [9, 2, 1, 1, 1] >>> ar.cf2Rational([9,2,1,1,1], AsDecimal=True) ('75/8', Decimal('9.375') >>> eval('75/8') 9.375 """ from decimal import Decimal p,q = simpleConvergents(cf) a = p[-1] b = q[-1] res = '%d/%d' % (a, b) if AsDecimal: resd = Decimal(str(a))/Decimal(str(b)) return res,resd else: return res
[docs] def continuedFraction(x, terms=20, AsFloats=False, rel_tol=1e-9, abs_tol=0.0): """ Source: https://leancrew.com/all-this/2023/08/continued-fractions-in-python/ See also: https://en.wikipedia.org/wiki/Continued_fraction Returns a tuple with the continued fraction (in list format) and the convergents (in Fraction format) of the argument. >>> import arithmetics as ar >>> from math import sqrt, pi >>> ar.continuedFraction(sqrt(2)) ([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [Fraction(1, 1), Fraction(3, 2), Fraction(7, 5), Fraction(17, 12), Fraction(41, 29), Fraction(99, 70), Fraction(239, 169), Fraction(577, 408), Fraction(1393, 985), Fraction(3363, 2378), Fraction(8119, 5741), Fraction(19601, 13860), Fraction(47321, 33461)]) >>> ar.continuedFraction(sqrt(2),AsFloats=True) ([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [1.0, 1.5, 1.4, 1.4166666666666667, 1.4137931034482758, 1.4142857142857144, 1.4142011834319526, 1.4142156862745099, 1.4142131979695431, 1.4142136248948696, 1.4142135516460548, 1.4142135642135643, 1.4142135620573204]) >>> ar.continuedFraction(pi,rel_tol=1e-15,AsFloats=True) ([3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14], [3.0, 3.142857142857143, 3.141509433962264, 3.1415929203539825, 3.1415926530119025, 3.141592653921421, 3.1415926534674368, 3.1415926536189365, 3.141592653581078, 3.141592653591404, 3.141592653589389, 3.1415926535898153, 3.1415926535897927]) >>> pi 3.141592653589793 """ from fractions import Fraction from math import isclose # Initialize, using Khinchin's notation a = [] # continued fraction terms p = [0, 1] # convergent numerator terms (-2 and -1 indices) q = [1, 0] # convergent denominator terms (-2 and -1 indices) s = [] # convergent terms remainder = x # Collect the continued fraction and convergent terms for i in range(terms): # Compute the next terms whole, frac = divmod(remainder, 1) an = int(whole) pn = an*p[-1] + p[-2] qn = an*q[-1] + q[-2] sn = Fraction(pn, qn) # Add terms to lists a.append(an) p.append(pn) q.append(qn) s.append(Fraction(sn)) # Convergence check if isclose(x, float(sn), rel_tol=rel_tol, abs_tol=abs_tol): break # Get ready for next iteration remainder = 1/frac # Return the tuple of the continued fraction and the convergents if AsFloats: s = [float(x) for x in s] return(a, s)
[docs] def gcd(a, b): """ Renders the greatest common divisor of two positive integers a and b. >>> import arithmetics as ar >>> ar.gcd(120,16) 8 """ if a < 0 or b < 0: print('Error: both parameters a and b must be positive integers') return None a = int(a) b = int(b) if a == b: return a while b > 0: a, b = b, a % b return a
[docs] def lcm(a, b): """ Renders the least common multiple of a and b. >>> import arithmetics as ar >>> ar.lcm(120,16) 240 """ return abs(a * b) // gcd(a, b)
[docs] def bezout(a,b,Comments=False,Debug=False): """ Renders the tuple *(d,x,y)* wher *d = gcd(a,b)* and *x* and *y* are the Bezout coefficients such that *d = ax + by*. Both arguments *a* and *b* must be positive integers. >>> import arithmetics as ar >>> ar.bezout(120,16,Comments=True) d = 8, x = 1, y = -7 8 = 120*1 + 16*(-7) (8, 1, -7) """ x,y,u,v = 1,0,0,1 if a < 0 or b < 0: print('Error: a and b must be positive integers !') return None,None,None arga = int(a) argb = int(b) if Debug: print(a,0,x,y) print(a,b,u,v) while b != 0: r = a % b q = (a - r)//b x,y, u,v = u,v, x-(q*u),y-(q*v) if Debug: print(a,b,q,r,u,v) a,b = b,r if Comments: print('d = %d, x = %d, y = %d' % (a,x,y)) if x < 0: print('%d = %d*(%d) + %d*%d' % (a,arga,x,argb,y)) elif y < 0: print('%d = %d*%d + %d*(%d)' % (a,arga,x,argb,y)) else: print('%d = %d*%d + %d*%d' % (a,arga,x,argb,y)) return a,x,y
[docs] def solPartEqnDioph(a,b,c,Comments=False): """ renders a particular integer solution of the Diophantian equation ax + by = c. The method returns the tuple (C*x, B*y, A, B) where d = gcd(a,b), C=c//d, A = a//d, and B=b//d. >>> import arithmetics as ar >>> ar.solPartEqnDioph(3,4,5,Comments=True) d = 1, a = 3, x = -5, b = 4, y = 5 (3)*(-5) + (4)*(5) = 5 (-5, 5, 3, 4) """ d = gcd(a,b) if c % d != 0: return None,None,None,None # pas de solution A,B,C = a//d, b//d, c//d D,x,y = bezout(A,B) if Comments: print('C*x = %d, C*y = %d, A = %d, B = %d' % (C*x,C*y,A,B)) print('(%d)*(%d) + (%d)*(%d) = %d' % (a, C*x, b, C*y, d)) return C*x, C*y, A, B # solution particulière plus coefficients
[docs] def zn_units(n,Comments=False): """ Renders the set of units of Zn. >>> import arithmetics as ar >>> ar.zn_units(12) {1, 11, 5, 7} """ units = set() for i in range(1,n): for j in range(1,n): if (i * j) % n == 1: units.add(i) units.add(j) if Comments: print(units) return units
[docs] def zn_squareroots(n,Comments=False): """ Renders the quadratic residues of Zn as a dictionary. >>> import arithmetics as ar >>> ar.zn_squareroots(13,Comments=False) {1: [1, 12], 4: [2, 11], 9: [3, 10], 3: [4, 9], 12: [5, 8], 10: [6, 7]} """ sqrt = {} units = zn_units(n) if Comments: print(units) for i in units: sqi = i*i % n if Comments: print(i,i*i,sqi) try: sqrt[sqi].append(i) except: sqrt[sqi] = [i] return sqrt
[docs] def computePiDecimals(decimalWordLength=4,nbrOfWords=600,Comments=False): """ Renders at least *decimalWordLenght* x *nbrOfWords* (default: 4x600=2400) decimals of :math:`\\pi`. The Python transcription here recodes an original C code of unknown author (see [*]_). Uses the following infinite *Euler* series: :math:`\\pi = 2 * \\sum_{n=0}^{\\infty} [ (n !) / (1 * 3 * 5 ... * (2n+1)) ].` The series gives a new :math:`\\pi` decimal after adding in average 3.32 terms. >>> from arithmetics import computePiDecimals >>> from time import time >>> t0=time();piDecimals = computePiDecimals(decimalWordLength=3,nbrOfWords=100);t1= time() >>> print('pi = '+piDecimals[0]+'.'+piDecimals[1:]) pi = 3.14159265358979323846264338327950288419716939937510582097494459 2307816406286208998628034825342117067982148086513282306647093844609 5505822317253594081284811174502841027019385211055596446229489549303 8196442881097566593344612847564823378678316527120190914564856692346 034861045432664821339360726024914127372458700660630 >>> print('precision = '+str(len(piDecimals[1:]))+'decimals') precision = 314 decimals >>> print('%.4f' % (t1-t0)+' sec.') 0.0338 sec. .. [*] *Source:* J.-P. Delahaye "*Le fascinant nombre* :math:`\\pi`", Pour la science Belin 1997 p.95. """ na = decimalWordLength # maximal string length of a number expressed in base a a = 10**na # base of the integer computations of the decimals prna = nbrOfWords * na # total number of decimals to compute in base a c = prna*3 + prna//2 # Euler's pi series requires about 3.5 steps for one more pi decimal e = 0 # gathers the next na pi-decimals in base a x = a//5 h = [x for i in range(c+1)] # vectrized accumulator for the Horner transform of Euler's pi series # a/10 pi = a/5( 1 + 1/3(1 + 2/5(1 + 3/7(...)))) # ! h index runs from 1 to c; f[0] is ignored ! piDecimals = '' while c > 0: g = 2*c d = 0 b = c while b > 0: d += h[b]*a g -=1 d,h[b] = divmod(d,g) # d = d//g; h[b] = d % g g -= 1 b -= 1 if b != 0: d *= b c -= (na*3 + na//2) # ng * 3.5 steps for each group of ng decimals expressed in base a e += d // a nd = ('%%0%dd' % na) % e if Comments: print(nd) piDecimals += nd e = d % a if Comments: print('pi = '+piDecimals[0]+'.') print(piDecimals[1:]) return piDecimals
[docs] def sternBrocot(m=5,n=7,Debug=False): """ Renders the Stern-Brocot representation of the rational m/n (m and n are positive integers). For instance, sternBrocot(5,7) = ['L','R','R','L']. *Source*: Graham, Knuth, Patashnik, Sec. 4.5 in Concrete Mathematics 2nd Ed., Addison-Wesley 1994, pp 115-123. """ sb = [] while m != n: if m < n: sb.append('L') n -= m else: sb.append('R') m -= n if Debug: print(sb) return sb
[docs] def invSternBrocot(sb=['L','R','R','L'],Debug=False): """ Computing the rational which corresponds to the Stern-Brocot string sb. *Source*: Graham, Knuth, Patashnik, Sec. 4.5 in Concrete Mathematics 2nd Ed., Addison-Wesley 1994, pp 115-123. """ def _matMult(S,X): R = [[S[0][0]*X[0][0]+S[0][1]*X[1][0],S[0][0]*X[0][1]+S[0][1]*X[1][1]], [S[1][0]*X[0][0]+S[1][1]*X[1][0],S[1][0]*X[0][1]+S[1][1]*X[1][1]]] return R L = [[1,1], [0,1]] R = [[1,0], [1,1]] S = [[1,0], [0,1]] while sb != []: if sb[0] == 'L': S = _matMult(S,L) else: S = _matMult(S,R) if Debug: print(S) sb = sb[1:len(sb)] if Debug: print(sb) m = S[1][0]+S[1][1] n = S[0][0]+S[0][1] return m,n
[docs] def computeFareySeries(n=7,AsFloats=False,Debug=False): """ Renders the Farey series, ie the ordered list of positive rational fractions with positive denominator lower or equal to n. For *n* = 1, we obtain: [[0,1],[1,1]]. *Parametrs*: *n*: strictly positive integer (default = 7). *AsFloats*: If True (defaut False), renders the list of approximate floats corresponding to the rational fractions. >>> import arithmetics as ar >>> ar.computeFareySeries(4) [[0, 1], [1, 4], [1, 3], [1, 2], [2, 3], [3, 4], [1, 1]] >>> ar.computeFareySeries(4,AsFloats=True) [0.0, 0.25, 0.3333333333333333, 0.5, 0.6666666666666666, 0.75, 1.0] *Source*: Graham, Knuth, Patashnik, Sec. 4.5 in Concrete Mathematics 2nd Ed., Addison-Wesley 1994, pp 115-123. """ f = [[0,1],[1,1]] if n < 1: print('Error: n >=1! n = %d' % n) elif n == 1: return f i = 1 while i < n: i += 1 if Debug: print(i) fcur=[] j = 0 while j < len(f)-1: if Debug: print(j) fcur.append(f[j]) num = f[j][1] + f[j+1][1] if Debug: print(num,i) if num == i: denom = f[j][0] + f[j+1][0] fcur.append([denom,num]) #fcur.append(f[j+1]) j += 1 if Debug: print(fcur) fcur.append(f[j]) f = list(fcur) if AsFloats: f = [float(x[0])/float(x[1]) for x in f] return f
[docs] def solvingQuadraticEquation(a,b,c,Comments=False): """ Renders both roots x,y of ax2 + bx + c = 0 where x = (-b + sqrt(b^2 -4ac))/2a and y = (-b - sqrt(b^2 -4ac))/2a >>> import arithmetics as ar >>> ar.solvingQuadraticEquation(1,-2,-1,Comments=True) D = (b^2 -4*a*c) = 8.000000 D > 0 => two real roots (2.414213562373095, -0.41421356237309515) >>> ar.solvingQuadraticEquation(1,2,1,Comments=True) D = (b^2 -4*a*c) = 0.000000 D == 0 => one real root (-1.0, -1.0) >>> ar.solvingQuadraticEquation(-1,6,-10,Comments=True) D = (b^2 -4*a*c) = -4.000000 D < 0 => two complex roots ((3-1j), (3+1j)) """ from math import sqrt D = (b*b -4*a*c) if Comments: print('D = (b^2 -4*a*c) = %f' % D ) if D > 0: if Comments: print('D > 0 => two real roots') x = (-b + sqrt(D))/(2*a) y = (-b - sqrt(D))/(2*a) elif D == 0: if Comments: print('D == 0 => one real root') x = -b / (2*a) y = -b / (2*a) else: if Comments: print('D < 0 => two complex roots') z = sqrt(-D) x = (complex(-b,z))/(2*a) y = (complex(-b,-z))/(2*a) return x,y
############################### if __name__ == '__main__': print(""" **************************************************** * Digraph3 arithmetics module * * Revision: Python3.13 * * Copyright (C) 2010-2025 Raymond Bisdorff * * The module comes with ABSOLUTELY NO WARRANTY * * to the extent permitted by the applicable law. * * This is free software, and you are welcome to * * redistribute it if it remains free software. * **************************************************** """) ###### scratch pad for testing the module components ## from math import sqrt ## p = 5 ## q = 8 ## print('p =',p,', q =',q) ## print('cf(p,q) = ', simpleContinuedFraction(p,q) ) ## print('eval(cf(p,q)) = ', decimalEvalContinuedFraction(simpleContinuedFraction(p,q)) ) ## cf = [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] ## print('cf(sqrt(2))_%d = ' % (len(cf)-1), cf ) ## print('eval(cf(sqrt(2))_%d) = ' % (len(cf)-1), decimalEvalContinuedFraction(cf) ) ## print('sqrt(2) = ', sqrt(2) ) ## print('*-----Computing with Bachet numbers----------*') n1 = BachetNumber(12) n2 = BachetNumber(154) n3 = n1 + n2 n4 = n1 * n2 print('%s (%d) + %s (%d) = %s (%d)' % (n1, n1.value(), n2, n2.value(), n3, n3.value() )) print('%s (%d) * %s (%d) = %s (%d)' % (n1, n1.value(), n2, n2.value(), n4, n4.value() )) n5 = n1.reverse() n6 = -n1 print('%s (%d) + %s (%d) = %s (%d)' % ( n5, n5.value(), n6, n6.value(),n5 + n6, (n5+n6).value() )) n7 = n6.permute(1,3) print(n7,n7.value()) print('*------------------*') print('If you see this line all tests were passed successfully :-)') print('Enjoy !') #####################################