    # Elementary Maths

Maths and Python seem to be a marriage made in heaven. Math is behind absolutely everything. Maths and Python are both easy and important, useful to learn and apply.

# Divisors

``````user@pc:~\$ python
Python 3.8.6 (default, May 27 2021, 13:28:02)
[GCC 10.2.0] on linux
>>> from sympy import divisors # We need to import the SymPy library
>>> print(divisors(24)) # Return all divisors of n: [1, 2, 3, 4, 6, 8, 12, 24]
>>> print(divisors(124)) # [1, 2, 4, 31, 62, 124]
``````

# Primes

``````>>> from sympy import primerange, prime, isprime
# We need to import the primerange, prime, and isprime functions from the SymPy library
>>> print(list(primerange(0, 36)))
# Return all prime numbers in the range [0, 36): [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
``````

1 is not a prime number. A prime number is a positive integer that has exactly two positive divisors. However, 1 only has one positive divisor (1 itself), so it is not prime.

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> from sympy import factorint, pprint
>>> pprint(factorint(420, visual=True))
``````
22 ⋅31 ⋅51 ⋅71
``````>>> pprint(factorint(180, visual=True))
``````
22 ⋅32 ⋅51

# Greatest common divisor (gcd) and least common multiple (lcm)

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.gcd(420, 54)
6
>>> math.lcm(420, 54)
3780
>>> 420*54/6
# lcm(420, 54) = 420*54/math.gcd(420, 54)
``````

# Powers and roots

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.sqrt(49) # sqrt() function returns the square root of any number. 7
>>> pow(4, 3) # pow() returns the power of a number. 64
>>> 64**(1/3) # It calculates the cube root of 64, 3.9999999999999996
``````

# Roman numeric system

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> import roman # Small helper library to convert arabic to roman numerals.
>>> print(roman.toRoman(124)) # It converts Arabic numbers to Roman Numerals: CXXIV
>>> print(roman.toRoman(87)) # It converts Arabic numbers to Roman Numerals: LXXXVII
>>> print(roman.fromRoman("MML")) # It converts Roman Numerals to Arabic numbers: 2050
>>> print(roman.fromRoman("XCVIII")) # It converts Roman Numerals to Arabic numbers: 98
``````

# Other number systems: binary, octal, and hexadecimal

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> print(bin(79)) # It converts from decimal to binary. The prefix 0b indicates that the value is in binary: 0b1001111
>>> print(oct(79)) # It converts from decimal to octal. The prefix 0o indicates that the value is in octal: 0o117
>>> print(hex(79)) # It converts from decimal to hexadecimal. The prefix 0x indicates that the value is in hexadecimal: 0x4f
>>> print(int("0b1001111", 2)) # It converts from binary to decimal: 79
>>> print(int("0o117", 8)) # It converts from octal to decimal: 79
>>> print(int("0x4f", 16)) # It converts from hexadecimal to decimal: 79
``````

# Integers

``````user@pc:~\$ python Python 3.8.6 (default, May 27 2021, 13:28:02) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> 4-5 returns -1
>>> -5*(-4) returns 20
>>> (-8)/(-4) returns 2.0
>>> (-4*7)-(-3*(-2)) returns -34
>>> abs(-8) # # abs() calculates the absolute value of the number passed as an argument: 8
``````

# Implementing a Natural class in Python

A natural number is a non-negative whole number. Natural or counting numbers begin at 1 and increment to infinity: 1, 2, 3, 4, 5, etc.

``````from __future__ import division # It is necessary to be able to overload the "/" operator.
import math
import roman
from fingerbinary import finger_binary
from sympy import divisors, primerange, isprime, factorint, pprint

class myNumber:
## This is our default constructor, e.g., myNumber = myNumber("24") or myNumber = myNumber("101", 2).
# @param number, the number itself
# @param base, the base in which the number is written (default is 10)
def __init__(self, number, base = 10):
if (base == 10 and not str(number).lstrip('-').isnumeric()) or not str(base).isnumeric():
raise ValueError("Number must be a number (base=10) and base needs to be a number.")
if base>= 2 and base<= 36:
try:
self.number = int(number, base)
except ValueError:
raise ValueError("Invalid number for int() with base " + str(base))
self.base = base
else:
raise TypeError("The base must be a value between 2 and 36.")
``````

@classmethod is a decorator that is used to declare a method as a class’s method. It always receives the class as its first argument. One of its main uses is to define alternative constructors.

``````    # This is one of the cleanest ways to have multiple constructors in Python, e.g., myNumber = myNumber.roman("XCVIII").
# @param cls refers to the class itself
# @param romanNumber is the number written in Roman numerals
@classmethod
def roman(cls, romanNumber):
return cls(str(roman.fromRoman(romanNumber)))

# This is an alternative constructor to create an object by passing it an integer, e.g., myNumber = myNumber.fromInteger(23)
@classmethod
def fromInteger(cls, myInteger):
return cls(str(myInteger))

# It creates an object representing a newly generated random number, e.g., myNumber = myNumber.random(100)
@classmethod
def random(cls, range=1000):
return cls(str(randint(1, range)))

# Sets need two methods to make an object hashable: __hash__ and __eq__
def __eq__(self, other):
return self.number == other.number

# A function hash returns an integer number representing the value of the object
def __hash__(self):
return hash(self.number)

# It is called when print() or str() are invoked on an object of the class and returns its string representation.
def __str__(self):
return str(self.number)

# It is called when repr() is invoked on an instance of the class and returns the object representation in string format.
def __repr__(self):
return str(self.number)
``````

This is Python’s approach to operator overloading. It allows our class to define its own behavior with respect to language operators. It provides additional functionality to the operators.

``````# When we sum two objects in Python like a + b, a special (magic) method __add__ is invoked: a._add__(b)
""" It returns a new object that represents the sum of two objects, self (the object itself) and other (the object passed as an argument)."""
return myNumber(str(self.number + other.number))

def __sub__(self, other):
""" It returns a new object that represents the subtraction of other from self."""
return myNumber(str(self.number - other.number))

def __mul__(self, other):
""" It returns a new object that represents the multiplication of self and other."""
return myNumber(str(self.number * other.number))

def __truediv__(self, other):
""" It returns a new object that represents the floor division of self by other. It requires from __future__ import division."""
return myNumber(str(self.number // other.number)) # It rounds the result down to the nearest whole number, e.g., 15//2 returns 7.
``````

15//2 = 7*2 + 1; 15//2 = 7 with remainder 1. 15//2 = 7, 15%2 = 1.

``````    def __mod__(self, other):
""" It returns a new object that represents the modulus or remainder of self divided by other."""
return myNumber(str(self.number % other.number))

def divisors(self):
""" It returns all divisors of self."""
print("divisors(" + str(self.number)  + ") = ", divisors(self.number))

def primes(self):
""" It generates a list with all the prime numbers in the range [0, number)."""
return list(primerange(0, self.number))

def isPrime(self):
""" It checks if the object or instance of the class (self) is prime or not."""
if isprime(self.number):
print(self.number, "is a prime number.")
else:
print(self.number, "is not a prime number.")

def factorization(self):
""" It prints the prime factorization of self."""
pprint(factorint(self.number, visual=True))

def gcd(self, other):
""" It prints the gcd/gcf or _greatest common factor_ of self and other."""
print("gcd(" + str(self.number) + ", "  + str(other.number) +  ") =", math.gcd(self.number, other.number))

def lcm(self, other):
""" It prints the lcm or least common multiple of self and other."""
lcmResult = abs(self.number * other.number) // math.gcd(self.number, other.number)
print("lcm(" + str(self.number) + ", " + str(other.number) + ") =", lcmResult)

def sqrt(self):
""" It prints the square root of self."""
print("sqrt(" + str(self.number) + ") =", math.sqrt(self.number))

def toRoman(self):
""" It prints the Roman number representation of self."""
print(str(self.number) + " =", roman.toRoman(self.number))

def toBinary(self):
""" It prints the binary representation of self."""
print(str(self.number) + "=" + str(bin(self.number)[2:]) + "\\u2082")
print(finger_binary(self.number))
``````

We are going to use the Finger Binary ASCII art generator for Python project. Finger binary is a system for counting and displaying binary numbers on the fingers and thumbs of one or more hands.

It is possible to count from 0 to 1023 = 210−1. You need to download the file fingerbinary.py. ``````    def toOctal(self):
""" It prints the octal representation of self."""
print(str(self.number) + "=" + str(oct(self.number)[2:]) + "\u2088")

""" It prints the hexadecimal representation of self."""
print(str(self.number) + "=" + str(hex(self.number)[2:]) + "\u2081\u2086")

def abs(self):
""" It prints the absolute value of self."""
print("|" + str(self.number) + "| = " +  str(abs(self.number)) )

def fact(self):
""" It prints the factorial of self."""
print(str(self.number) + "! = " +  str(math.factorial(self.number)) )

def elementaryMaths():
myFirstNumber = myNumber("24")
myFirstNumber.divisors() # divisors(24) =  [1, 2, 3, 4, 6, 8, 12, 24]
print(myFirstNumber.primes()) # [2, 3, 5, 7, 11, 13, 17, 19, 23]
myFirstNumber.isPrime() # 24 is not prime.
myFirstNumber.factorization() # 2^3 * 3^1
mySecondNumber = myNumber("4200")
mySecondNumber.factorization() # 2^3 * 3^1 * 5^2 * 7^1
myNumber("60").gcd(myNumber("48")) # gcd(60, 48) = 12
myNumber("60").lcm(myNumber("48")) # lcm(60, 48) = 240
myNumber("16").sqrt() # sqrt(16) = 4.0
mySecondNumber.toRoman() # 4200 = MMMMCC
myNumber("18").toBinary() # 18=10010₂
myNumber("19").toOctal() # 19=23₈
myNumber("-19").abs() # |-19| = 19
myThirdNumber = myNumber.roman("XCVIII")
print(myThirdNumber) # 98
print(myNumber("4F", 16)) # 79
print(myNumber("7").fact()) # 7! = 5040
print(myNumber.roman("XVIII") + myNumber("1F", 16)) # 49(=18+31)
print(myNumber("1F", 16) - myNumber.roman("XVIII")) # 13(=31-18)
print(myNumber("1F", 16) * myNumber.roman("XVIII")) # 558(=31*18)
print(myNumber("1F", 16) / myNumber.roman("XVIII")) # 1(=31/18)
print(myNumber("1F", 16) % myNumber.roman("XVIII")) # 13(=31%18)
if __name__ == '__main__':
elementaryMaths()
``````
Bitcoin donation 