# Objective

 Learn about Python's Decimal module. Learn about Decimal objects. Review examples of code containing Decimal objects. Learn how to use Decimal objects to perform calculations on complex numbers. Become familiar with numbers containing many thousands of decimal digits. Create a new class CompleX. Use class CompleX and Python's Decimal module to solve the cubic equation.

# Lesson

Calculations using Python's Decimal module can be performed with (almost) any precision selectable by the user. Unfortunately many functions which you might expect to find in the module don't exist, for example, trigonometric functions and functions that manipulate complex numbers. For these functions you have to find them elsewhere or write them for yourself.

Here are some examples that highlight the power of Python's Decimal Module.

## Conversion to Decimal object

Several different objects are convertible to Decimal object. Type int converts exactly:

>>> from decimal import *
>>> d1 = Decimal(12345678901123456789011234567890112345678901123456789011234567890112345678901);d1
Decimal('12345678901123456789011234567890112345678901123456789011234567890112345678901')
>>>


Precision is not applied when the Decimal object is initialized or displayed. Default precision of 28 is applied when the Decimal object is used:

>>> d1 + 0; d1 - 0; +d1; -d1
Decimal('1.234567890112345678901123457E+76')
Decimal('1.234567890112345678901123457E+76')
Decimal('1.234567890112345678901123457E+76')
Decimal('-1.234567890112345678901123457E+76')
>>>


Although conversion is not necessary, Decimal object converts exactly.

>>> d2 = Decimal(d1) ; d2
Decimal('123456789011234567890112345678901123456789011234567890112345678901')
>>>


Conversion from float to Decimal is tricky:

>>> f1 = 3.14 ; f1
3.14
>>> d3 = Decimal( f1 ) ; d3 ; +d3
Decimal('3.140000000000000124344978758017532527446746826171875')
Decimal('3.140000000000000124344978758')
>>>


Conversion from float is accurate if correct precision for floats is applied.

>>> getcontext().prec = 14
>>> f1 = 1.13-1.1 ; f1
0.029999999999999805
>>> Decimal(f1); +Decimal(f1);
Decimal('0.029999999999999804600747665972448885440826416015625')
Decimal('0.030000000000000')
>>>


Also, conversion from float is accurate if float is correctly formatted.

>>> f1 = 1.13-1.1 ; f1
0.029999999999999805
>>> Decimal( '{:1.15f}'.format(f1) )
Decimal('0.030000000000000')
>>>


For simple, accurate conversion to Decimal, convert float to str first:

>>> f1 = 3.14 ; str(f1) ; Decimal(str(f1))
'3.14'
Decimal('3.14')
>>>
>>> Decimal( '{}'.format(f1) )
Decimal('3.14')
>>>


Conversion from complex to Decimal:

>>> Decimal(3+4j)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: conversion from complex to Decimal is not supported
>>>


Conversion from str representing number to Decimal is accurate.

>>> Decimal('1234.5678901e3')
Decimal('1234567.8901')
>>>


eval() is more forgiving than Decimal().

>>> s1
'  -  3456e-3  '
>>> Decimal(s1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
>>> Decimal( str(eval(s1)) )
Decimal('-3.456')
>>>


Conversion from DecimalTuple is accurate:

>>> dt1 = DecimalTuple(sign=1, digits=(0,0,3, 4, 5, 6,7,8,0,0), exponent=-4) ; dt1 ; Decimal(dt1)
DecimalTuple(sign=1, digits=(0, 0, 3, 4, 5, 6, 7, 8, 0, 0), exponent=-4)
Decimal('-3456.7800') # Leading zeroes are dropped. Trailing zeroes are kept.
>>>


Conversion from well formed list or tuple is accurate:

>>> t1=(1,(2,3,4,5,6),-4);
>>> t2=(1,list(t1[1]),-4);
>>> L1=list(t1);
>>> L2=list(t2);
>>> t1;t2;L1;L2;
(1, (2, 3, 4, 5, 6), -4)
(1, [2, 3, 4, 5, 6], -4)
[1, (2, 3, 4, 5, 6), -4]
[1, [2, 3, 4, 5, 6], -4]
>>> [ Decimal(v) for v in (t1,t2,L1,L2) ]
[Decimal('-2.3456'), Decimal('-2.3456'), Decimal('-2.3456'), Decimal('-2.3456')]
>>>
>>> { Decimal(v) for v in (t1,t2,L1,L2) }
{Decimal('-2.3456')}
>>>


## Using Decimal objects

Decimal objects work well with the usual arithmetic operators:

>>> from decimal import *
>>> D = Decimal
>>> D('234.567') + D('.000000000000000456')
Decimal('234.567000000000000456')
>>> D('234.567') - D('.000000000000000456')
Decimal('234.566999999999999544')
>>> D('234.567') * D('.000000000000000456')
Decimal('1.06962552E-13')
>>> D('234.567') / D('.000000000000000456')
Decimal('514401315789473684.2105263158')
>>> D('234.567') ** ( D('1')/D(3)  )
Decimal('6.167213327076116022863000610') # Cube root.
>>> D('234.567') % 1
Decimal('0.567') # the fractional part.
>>> D('-234.567') % 1
Decimal('-0.567')
>>> D('-45234.567') % 360
Decimal('-234.567')
>>>


If you are doing much heavy math containing cube roots, it might be advantageous for you to write your own cube root function using Newton's method, for example. Newton's method is much faster than raising a number to the power (1/3).

Decimal objects work well with many of the familiar built-in functions:

>>> d1 = D(-5);d1
Decimal('-5')
>>> abs(d1)
Decimal('5')
>>>
>>> Decimal( ascii(123.456) )
Decimal('123.456')
>>>
>>> bool(D(4))
True
>>> bool(D(0))
False
>>>
>>> complex( D('34.56') ) ;  complex(4, D(3))
(34.56+0j)
(4+3j)
>>>
>>> divmod ( -D('2345678.0987654321'), 360 )
(Decimal('-6515'), Decimal('-278.0987654321'))
>>> divmod ( -D('2345678.0987654321'), 1 )
(Decimal('-2345678'), Decimal('-0.0987654321'))
>>>
>>> float ( -D('2345678.0987654321'))
-2345678.098765432
>>>
>>> int ( -D('2345678.987654321'))
-2345678
>>>
>>> isinstance( D(10), Decimal )
True
>>> type( D(10) )
<class 'decimal.Decimal'>
>>>
>>> max(100, -23, D(44))
100
>>> min(100, -23, D(44))
-23
>>>
>>> pow(3,D(2))
Decimal('9')
>>>
>>> sorted(( 3,45,-100, D('234.56') ))
[-100, 3, 45, Decimal('234.56')]
>>>
>>> str(D('456.78'))
'456.78'
>>>
>>> sum(( 3,45,-100, D('234.56') ))
Decimal('182.56')
>>>


## Decimal objects and attributes

>>> D('3.14159').as_integer_ratio()
(314159, 100000)
>>> D(3.14159).as_integer_ratio()
(3537115888337719, 1125899906842624)
>>>
>>> D('3.14159').as_tuple()
DecimalTuple(sign=0, digits=(3, 1, 4, 1, 5, 9), exponent=-5)
>>> D(3.14159).as_tuple()
DecimalTuple(sign=0, digits=(3, 1, 4, 1, 5, 8, 9, 9, 9, 9, 9, 9, 9, 9, ............... , 7, 0, 9, 9, 6, 0, 9, 3, 7, 5), exponent=-50)
>>>
>>> D(3.14159).compare(D('3.14159'))
Decimal('-1') # D(3.14159) < D('3.14159')
>>>
>>> D('-3.14159').copy_abs()
Decimal('3.14159')
>>> abs(D('-3.14159'))
Decimal('3.14159')
>>>
>>> D('-3.14159').is_normal()
True
>>> D('-3.14159').is_zero()
False
>>> D('-3.14159').is_infinite()
False
>>>
>>> D(7).max(D(-9))
Decimal('7')
>>> D(7).max_mag(D(-9))
Decimal('-9')
>>> D(7).min(D(-9))
Decimal('-9')
>>> D(7).min_mag(D(-9))
Decimal('7')
>>>
>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')
>>> Decimal('1.41451356').quantize(Decimal('1.000'))
Decimal('1.415')
>>> Decimal('1.41421356').quantize(Decimal('.001'))
Decimal('1.414')
>>> Decimal('1.41451356').quantize(Decimal('.001'))
Decimal('1.415')
>>>
>>> Decimal('0.321000e+2').normalize()
Decimal('32.1')
>>> Decimal('3.2100e1').normalize()
Decimal('32.1')
>>> Decimal('32100.00000e-3').normalize()
Decimal('32.1')
>>>


### Exponential operations

>>> D('1').exp()
Decimal('2.718281828459045235360287471') # Value of e, base of natural logarithms.
>>> D('2').exp()
Decimal('7.389056098930650227230427461') # e ** 2
>>> D('3.14159').ln() # Natural logarithm (base e).
Decimal('1.144729041185178381216412580') # e ** 1.144729... = 3.14159
>>> (D('1234.5678').ln()).exp()
Decimal('1234.567800000000000000000000') # This simple test gives an impressive result.
>>> (D('1234.5678').exp()).ln()
Decimal('1234.567800000000000000000000') # This also.
>>>
>>> # Raising number to power:
>>> D('1.6') ** D('2.3')
Decimal('2.947650308163391181711649979')
>>> D('1.6').ln()*D('2.3').exp()
Decimal('4.687901952522058518151002058')
>>> (D('1.6').ln()*D('2.3')).exp() # Parentheses are important.
Decimal('2.947650308163391181711649980')
>>>
>>> D('1.6').sqrt() # Method .sqrt() is very fast.
Decimal('1.264911064067351732799557418')
>>>


### Logical operations

The Decimal object D('1110010100011') has the appearance of a binary number and logical operations act on Decimal objects whose only digits are 0,1.

>>> D(1100020011).logical_and(D(1111))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
>>>


When using python's decimal module for logical operations on integers, convert int to appropriate Decimal object first.

>>> int1 = 23 ; bin(int1)
'0b10111'
>>> D( bin(int1)[2:] )
Decimal('10111')
>>>


After the logical operation convert Decimal object with appearance of binary number to int.

>>> int(str(D('10111')),2)
23
>>>

>>> D(110001111).logical_invert()
Decimal('1111_1111_1111_1111_1110_0111_0000') # Default precision of 28.
>>> # Same as:
>>> D(110001111).logical_xor(D('1'*getcontext().prec))
Decimal('1111_1111_1111_1111_1110_0111_0000')
>>>

>>> D(110001111).logical_and(D(1111))
Decimal('1111')
>>> # Equivalent to:
>>> bin(int('18F',16) & int('1111',2))
'0b1111'
>>>

>>> D(1_1000_1111).logical_xor(D('1100_1100'))
Decimal('1_0100_0011')
>>>

>>> D(110001111).shift(3) # +ve means shift left.
Decimal('110001111000')
>>> D(110001111).shift(-3) # -ve means shift right.
Decimal('110001') # Bits shifted out to the right are lost.
>>> getcontext().prec=10
>>> D(110001111).shift(3)
Decimal('1111000') # Bits shifted out to the left are lost.
>>>

>>> getcontext().prec=10
>>> D(110001111).rotate(3) # To left.
Decimal('1111011')
>>> D(110001111).rotate(-3) # To right.
Decimal('1110110001')
>>> # Same as:
>>> D('0110001111').logical_and(D(111)).shift(7) + D('0110001111').shift(-3)
Decimal('1110110001')
>>>


## Context objects and attributes

Contexts are environments for arithmetic operations. They govern precision, set rules for rounding, determine which signals are treated as exceptions, and limit the range for exponents.

After the decimal module has been imported, three supplied contexts are available:

>>> from decimal import *
>>>
>>> DefaultContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> BasicContext
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
>>>
>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
>>>


After the decimal module has been imported, the current context is the same as DefaultContext.

>>> from decimal import *
>>>
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> str(getcontext())
'Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])'
>>>
>>> str(getcontext()) == str(DefaultContext)
True
>>>


After importing the decimal module, set the current context (if necessary) as appropriate for your planned use of the decimal module.

>>> from decimal import *
>>> getcontext().prec = 20
>>> getcontext().clear_flags()
>>> getcontext()
Context(prec=20, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, Overflow, Underflow])
>>>


For a list of valid signals:

>>> getcontext().flags['']=False
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'valid values for signals are:\n  [InvalidOperation, FloatOperation, DivisionByZero,\n   Overflow, Underflow, Subnormal, Inexact, Rounded,\n   Clamped]'
>>>


To create a new context copy an existing context:

>>> myContext = BasicContext # This creates a shallow copy.
>>>
>>> myContext = BasicContext.copy()
>>> myContext.prec = 88
>>> myContext ; BasicContext
Context(prec=88, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow]) # Deep copy.
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
>>>


or use the constructor Context():

>>> from decimal import *
>>>
>>> myContext = Context() ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> str(myContext) == str(DefaultContext)
True
>>> myContext = Context(rounding=ROUND_HALF_UP,flags=[]) ; myContext
Context(prec=28, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> myContext = Context(Emax=9999, flags=[], traps=[InvalidOperation, DivisionByZero]) ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=9999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero])
>>>


To modify a context:

>>> myContext.Emin = -9999 ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-9999, Emax=9999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero])
>>>
>>> myContext.flags[Inexact] = True ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-9999, Emax=9999, capitals=1, clamp=0, flags=[Inexact], traps=[InvalidOperation, DivisionByZero])
>>>
>>> myContext.traps = DefaultContext.traps ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-9999, Emax=9999, capitals=1, clamp=0, flags=[Inexact], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> myContext.clear_flags() ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-9999, Emax=9999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>
>>> myContext.clear_traps() ; myContext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-9999, Emax=9999, capitals=1, clamp=0, flags=[], traps=[])
>>>


To set current context:

>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> setcontext(Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[], traps=[]))
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[], traps=[])
>>>


The rules are:

• A bad operation sets a flag.
• A good operation does not clear a flag.
• Before a decimal operation set traps and clear flags.
• After a decimal operation process exceptions and check flags.
>>> getcontext().clear_flags() ; getcontext().clear_traps() ; getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
>>>
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
>>>
>>> Decimal(1)/0
Decimal('Infinity')
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[DivisionByZero], traps=[])
>>>
>>> getcontext().clear_flags() ; getcontext().clear_traps()
>>>
>>> 1234567890123456789012345678901234567890+2
1234567890123456789012345678901234567892
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
>>>
>>> 1234567890123456789012345678901234567890+Decimal(2)
Decimal('1.234567890123456789012345679E+39')
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>>


### Methods

Many of the methods available for Context objects have the same names as corresponding methods available for Decimal objects, for example : max, quantize, shift and sqrt.

However they usually take an extra argument so that they make sense when attached to Context object;

>>> Decimal(3).sqrt()
Decimal('1.7320508075688772935274463415058723669428053')
>>> BasicContext.sqrt(Decimal(3))
Decimal('1.73205081')
>>>


Others such as clear_traps(), clear_flags() make sense only when attached to Context object.

Context objects are useful if you want to perform an arithmetic operation in a temporary environment without changing current environment.

>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>> Context(prec=99).ln(Decimal(5))
Decimal('1.60943791243410037460075933322618763952560135426851772191264789147417898770765776463013387809317961')
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>>
.......
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[], traps=[])
>>> Context(prec=14).create_decimal_from_float(1.13 - 1.1)
Decimal('0.030000000000000')
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[], traps=[])
>>>


Also if you want to apply a trap to a conversion without affecting current environment:

>>> getcontext().clear_flags()
>>> Decimal('123.456').quantize(Decimal('.01'))
Decimal('123.46')
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>> getcontext().clear_flags()
>>> Context(traps=[Inexact]).quantize( Decimal('123.456'), Decimal('.01') )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.Inexact: [<class 'decimal.Inexact'>]
>>> getcontext()
Context(prec=44, rounding=ROUND_HALF_UP, Emin=-999, Emax=999, capitals=1, clamp=0, flags=[], traps=[])
>>>


To check the result of arithmetic operation in a temporary environment:

>>> myContext=Context(prec=14, flags=[], traps=[], rounding=ROUND_HALF_UP, Emax=99, Emin=-99)
>>> myContext
Context(prec=14, rounding=ROUND_HALF_UP, Emin=-99, Emax=99, capitals=1, clamp=0, flags=[], traps=[])
>>> myContext.quantize( Decimal('123.456'), Decimal('.01') )
Decimal('123.46')
>>> myContext
Context(prec=14, rounding=ROUND_HALF_UP, Emin=-99, Emax=99, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>>
>>> myContext.flags[Inexact]
True
>>> myContext.flags[Overflow]
False
>>>


Is Decimal object int?

>>> d1 = Decimal('123.000');d1
Decimal('123.000')
>>> myContext.clear_flags()
>>> myContext.quantize( d1, Decimal(1) )
Decimal('123')
>>> myContext.flags[Inexact]
False # Conversion was exact. d1 is equivalent to int.
>>>
>>> d1 = Decimal('123.007');d1
Decimal('123.007')
>>> myContext.clear_flags()
>>> myContext.quantize( d1, Decimal(1) )
Decimal('123')
>>> myContext.flags[Inexact]
True # Conversion was not exact. d1 is not equivalent to int.
>>>


## Making the Decimal object

The following function verifies that we are working with Decimal objects. It provides for a tuple containing two decimal objects to simulate a complex number.

from decimal import *

getcontext().prec += 5  # Extra digits for intermediate steps.

D = Decimal

def makeDecimal (x) :
'''
x is a single object convertible to Decimal object/s.
returns Decimal object or
tuple containing 2 Decimal objects to simulate complex or
exits with error status
'''

if isinstance(x, Decimal) :
return (x)
if isinstance(x, int) or isinstance(x, float) :
return (Decimal(str(x)))
if isinstance(x, complex) :
return ( Decimal(str(x.real)), Decimal(str(x.imag)) )
if isinstance(x, str) :
status = 0
try : x1 = Decimal(x)
except : status = -1
if not status : return x1

status = 0
try : x1 = eval(x)
except : status = -1
if status :
print ('makeDecimal: bad status after eval.')
exit(79)

if isinstance(x1, str) :
print ('makeDecimal: eval must not produce another string.')
exit(78)
return makeDecimal(x1)
print ('makeDecimal: input must be Decimal, int, float, complex or str.')
exit(77)


# Trigonometric Functions

The following trigonometric functions are sufficient to convert from complex type polar to complex type rectangular and vice-versa.

For the functions ${\displaystyle \cos x}$ and ${\displaystyle \sin x}$ see Recipes.

## arctan ${\displaystyle x}$

We'll try ${\displaystyle \arctan x}$ first because it's easily understood and implemented, and it can be used to calculate ${\displaystyle \pi }$ because the result of this expression (as in all theoretical math) is in radians.

${\displaystyle \arctan x=\sum _{n=0}^{\infty }{\frac {(-1)^{n}}{2n+1}}x^{2n+1}=x-{\frac {x^{3}}{3}}+{\frac {x^{5}}{5}}-\cdots {\text{for }}|x|\leq 1,\ x\neq \pm i}$

def arctan (tanθ) : # The Python interpreter recognizes international text. Handy.
# value returned is in radians.
# Check input:
x = makeDecimal (tanθ)
if not isinstance(x, Decimal) :
print ('arctan: type of input should be Decimal.')
exit(79)

if not x : return 0 # tan(0) = 0

if abs(x) > 1 :
# abs() function is valid for Decimal objects.
print ('arctan: abs(x) should be <= 1.')
exit(78)
if x < 0 :
print ('arctan: input must be >= 0.')
exit(77) # Only non-negative values in this implementation.

sum = x
x_sq = x*x

divisor = 3

last_dividend = x

multiplier = -1

getcontext().prec += 2  # extra digits for intermediate steps

almostZero = Decimal('1e-' + str(getcontext().prec))

while 1 :
this_dividend = last_dividend * x_sq
if this_dividend < almostZero : break
sum += multiplier * this_dividend / divisor

last_dividend = this_dividend
multiplier *= -1
divisor += 2

getcontext().prec -= 2  # Restore original precision.
return sum+0 # Apply original precision to sum.


### Calculating ${\displaystyle \pi }$

There are many ways to calculate ${\displaystyle \pi }$. For example:

${\displaystyle \pi =6*\arctan(\tan 30)=6*\arctan({\frac {\sqrt {3}}{3}}).}$

Based on the following, we will perform six calculations of ${\displaystyle \pi }$ and compare the results.

${\displaystyle \pi =30*\arctan(\tan 6)}$

${\displaystyle \pi =24*\arctan(\tan 7.5)}$

${\displaystyle \pi =12*\arctan(\tan 15)}$

${\displaystyle \pi =8*\arctan(\tan 22.5)}$

${\displaystyle \pi =6*\arctan(\tan 30)}$

${\displaystyle \pi =5*\arctan(\tan 36)}$

precision = getcontext().prec
getcontext().prec = 502

tan6 = (
( (D('10') - D('20').sqrt()).sqrt() + D('3').sqrt() - D('15').sqrt() )
/
2
)
tan7_5 = D(6).sqrt() - D(3).sqrt() + D(2).sqrt() - 2
tan15 = D(2) - D(3).sqrt()
tan22_5 = D(2).sqrt() - 1
tan30 = D('3').sqrt()/3
tan36 = ( D(5) - 2*(D(5).sqrt()) ).sqrt()

values = [
(tan6, 30),
(tan7_5, 24),
(tan15, 12),
(tan22_5, 8),
(tan30, 6),
(tan36, 5)
]

L2 = [ (v[1]*arctan(v[0])).quantize(
D('1e-'+str(getcontext().prec-3)), rounding=ROUND_HALF_UP
)
for v in values
]

print ('number of calculations =', len(L2))

set2 = set(L2)
len(set2) == 1 or exit(93) # All values in L2 must be equal.
π, = list(set2)
str_π = str(π)

L3 = [
str_π[start:start+70]
for start in range(0,len(str_π),70)
]

str1 = '"' + '" + \n"'.join(L3) + '"'

print (
'''
π = (
{} )
len(str(π)) = {}
isinstance(π, Decimal): {}
'''.format ( str1,
len(str_π),
isinstance(π, Decimal)
)
)

getcontext().prec = precision

number of calculations = 6

π = (
"3.14159265358979323846264338327950288419716939937510582097494459230781" +
"6406286208998628034825342117067982148086513282306647093844609550582231" +
"7253594081284811174502841027019385211055596446229489549303819644288109" +
"7566593344612847564823378678316527120190914564856692346034861045432664" +
"8213393607260249141273724587006606315588174881520920962829254091715364" +
"3678925903600113305305488204665213841469519415116094330572703657595919" +
"5309218611738193261179310511854807446237996274956735188575272489122793" +
"81830119491" )
len(str(π)) = 501
isinstance(π, Decimal): True


All six calculations agree to 500 digits of precision and π is available globally as a Decimal object.

## tan ${\displaystyle (\theta /2)}$

${\displaystyle \tan {\frac {\theta }{2}}}$${\displaystyle =\csc \theta -\cot \theta }$.

def tanθ_2 (tanθ):
'''
tan(θ/2) = csc θ - cot θ
x = tanθ
'''
x = makeDecimal(tanθ)
if not isinstance(x, Decimal) :
print ('tanθ_2: x should be Decimal.')
exit(76)

if x == 0 : return 0

if x < 0 :
# Only non-negative values in this implementation.
print ('tanθ_2: input < 0.')
exit(75)

getcontext().prec += 2  # extra digits for intermediate steps
cscθ = ((Decimal(1)+x*x).sqrt()) / x
cotθ = Decimal(1)/x
tan_θ2 = cscθ - cotθ
getcontext().prec -=2

return (tan_θ2 + 0)


## decATAN2 ${\displaystyle (y,x)}$

For the corresponding function using floating point arithmetic see math.atan2(y, x)

This function invokes ${\displaystyle arctan(x)\ 0\leq x\leq 1}$.

If ${\displaystyle \tan \theta >1,\ x={\frac {1}{\tan \theta }}}$ and the result is ${\displaystyle {\frac {\pi }{2}}-\arctan(x).}$

However this function invokes ${\displaystyle \arctan(x)}$ with a value less than ${\displaystyle 0.325}$ so that the expression ${\displaystyle x^{2n+1}}$ in the expansion of ${\displaystyle \arctan x}$ vanishes as rapidly as possible. Therefore:

${\displaystyle 75=90-\arctan({\frac {1}{\tan 75}})}$

${\displaystyle 68=4\arctan(\tan {\frac {68}{4}})}$

${\displaystyle 32=2\arctan(\tan {\frac {32}{2}})}$

${\displaystyle 12=\arctan(\tan 12)}$

def decATAN2 (y,x) :
'''
y
Input is value -.
x
Both x,y must be convertible to Decimal object.
Only 1 of x or y may be 0.
Returns value of angle in degrees.
Value of π must be available globally.
'''
x = makeDecimal(x)
if not isinstance(x, Decimal) :
print ('decATAN2: type of x',type(x),'should be Decimal.')
exit(70)

y = makeDecimal(y)
if not isinstance(y, Decimal) :
print ('decATAN2: type of y',type(y),'should be Decimal.')
exit(69)

if x == y == 0 :
print ('decATAN2: both x and y cannot be 0.')
exit(68)

if y == 0 :
if x > 0 : return 0
return 180

if x == 0 :
if y > 0 : return 90
return 270

if abs(x) == abs(y) :
if x > 0 :
if y > 0 : return 45
return 360-45
if y > 0 : return 180-45
return 180+45

getcontext().prec += 2  # Extra digits for intermediate steps.
tanθ = abs(y)/abs(x)

flip = 0
if tanθ > Decimal('3.078') : # > 72 degrees
tanθ = 1/tanθ
flip += 1

reductionCount = 0
while tanθ > Decimal('0.325') : # > 18 degrees
tanθ = tanθ_2 (tanθ)
reductionCount += 1

θ = arctan(tanθ)
if flip :
θ = π/2 - θ
else :
while reductionCount :
θ *= 2
reductionCount -= 1

θinDegrees = θ*180/π
if x > 0 :
if y < 0 :
θinDegrees = 360-θinDegrees
else :
if y > 0 :
θinDegrees = 180-θinDegrees
else :
θinDegrees = 180+θinDegrees

getcontext().prec -= 2
return θinDegrees+0


def degreesToRadians (θinDegrees) :
'''
Value of π must be available globally.
Value returned: -π < radians <= π
-180 degrees is returned as π.
270 degrees is returned as -π/2
'''
x = makeDecimal(θinDegrees)

if not isinstance(x, Decimal) :
print ('degreesToRadians: type of x should be Decimal.')
exit(54)

x = x % 360
if x < 0 : x += 360
if x > 180 : x -= 360
return x * π/180


# Complex Functions

Within the context of this page a complex number is contained in a tuple with three members, thus:

(modulus, phaseInDegrees, 'polar')


The above tuple represents complex number ${\displaystyle modulus\ *\ (\ \cos(phaseInDegrees)\ +\ 1j*\sin(phaseInDegrees)\ )}$.

Or:

(realPart, imaginaryPart, 'rect')


where ${\displaystyle realPart,imaginaryPart}$ are the rectangular coordinates of complex number ${\displaystyle realPart\ +\ 1j*imaginaryPart}$.

The four values ${\displaystyle modulus,phaseInDegrees,realPart,imaginaryPart}$ are all Decimal objects.

The rectangular format is useful for addition and subtraction of complex numbers.

The polar format is useful for raising a complex number to a power including a power less than unity.

Both formats are useful for multiplication, division and square root.

When working with polar format it's generally more advantageous to work with a positive modulus. Therefore:

${\displaystyle -n*(\cos \phi +1j*\sin \phi )=n*(\cos(\phi \pm 180)+1j*\sin(\phi \pm 180))}$

and, for example:

${\displaystyle {\sqrt {-4}}={\sqrt {-4(\cos 0+1j*\sin 0)}}={\sqrt {4(\cos 180+1j*\sin 180)}}=2(\cos 90+1j*\sin 90)=2*1j*1=2j}$

and the other sqrt of ${\displaystyle -4}$ is:

${\displaystyle {\sqrt {4(\cos(-180)+1j*\sin(-180))}}=2(\cos(-90)+1j*\sin(-90))=2*1j*(-1)=-2j}$.

>>> (2j)**2 ; (-2j)**2
(-4+0j)
(-4+0j)
>>>


Both of the following complex tuples equate to 0:

>>> (0,0,'rect')
>>> (0,anyNumber,'polar')


The following functions will enable us to do some serious math with complex numbers, such as solving the cubic equation with three real roots.

### checkComplex(x)

This function verifies that the object is a valid complex tuple.

def checkComplex(x) :
if not isinstance(x,tuple) :
print ('checkComplex: input type should be tuple')
exit(69)
if not (len(x) == 3) :
print ('checkComplex:', 'len(x) =', len(x), 'should be 3 in', x)
exit(68)
if not isinstance(x[0], Decimal) :
print ('checkComplex:', 'x[0] =', x[0], 'should be Decimal in', x)
exit(67)
if not isinstance(x[1], Decimal) :
print ('checkComplex:', 'x[1] =', x[1], 'should be Decimal in', x)
exit(66)
if not isinstance (x[2], str) :
print ('checkComplex: x[2] must be str.')
exit(65)
if (x[2] not in ('polar', 'rect')) :
print ('checkComplex:', 'x[2] =', x[2], 'should be "polar"/"rect" in', x)
exit(64)


### makeComplex(x)

def makeComplex(x) :
'''
Input can be tuple with 1,2 or 3 members.
If 1 or 2 members, 'rect' is understood.
The one member or single object may be int, float, complex, CompleX
or string convertible to int, float or complex.

x = makeComplex(4)
x = makeComplex((4,))
x = makeComplex(('4',0))
x = makeComplex((4,'0', 'rect'))
x = makeComplex(4+0j)
x = makeComplex('4+0j')
x = makeComplex(('4+0j',))
In all seven cases above x = ( Decimal('4'), Decimal('0'), 'rect' )

output is always
(modulus, phase, "polar") or
(real_part, imag_part, "rect")
modulus, phase, real_part, imag_part are Decimal objects.
'''
if isinstance(x, CompleX) :  # New class CompleX (note the spelling.)
x.check()
return (x.r, x.i, 'rect')

if isinstance(x, tuple) :
if len(x) not in (1,2,3) :
print ('makeComplex: input has wrong length.',len(x),'should be 3.')
exit(76)
if len(x) == 3 :
if not isinstance (x[2], str) :
print ('makeComplex: x[2] must be str.')
exit(75)
if x[2] not in ('polar','rect') :
print ('makeComplex: type of complex in x[2] not recognized.')
exit(75)
v1,v2,type_ = makeComplex(x[:2])
output = (v1,v2,x[2])
checkComplex(output)
return output

if len(x) == 2 :
v1,v2 = [ makeDecimal (v) for v in x ]
if not isinstance(v1, Decimal) :
print ('makeComplex: v1 should be Decimal.')
exit(74)
if not isinstance(v2, Decimal) :
print ('makeComplex: v2 should be Decimal.')
exit(73)
output = (v1,v2, 'rect')
return output

x = x[0]
t1 = makeDecimal (x)
if isinstance(t1,Decimal) :
output = (t1,Decimal(0),'rect')
return output

if (isinstance(t1, tuple) and len(t1) == 2) :
output = t1 + ('rect',)
checkComplex(output)
return output

print ('makeComplex: t1 must be tuple with length 2.')
exit(72)


### convertComplex(x)

def convertComplex(x) :
'''
If input is rectangular, output is polar and vice-versa
'''
x = makeComplex(x)

if x[2] == 'polar' :
modulus, phase = x[0], x[1]
a,b = (modulus*cosθ, modulus*sinθ)
output = (a,b,'rect')
else :
real,imag = x[0], x[1]
modulus = (real*real + imag*imag).sqrt()
phase = decATAN2(imag, real)
output = (modulus, phase, 'polar')

output = makeComplex(output)
return output


### clean_complex (x)

def clean_complex (x) :
'''
output = clean_complex (x)
output is input returned as complex tuple
with values "cleaned".
1e-50 is returned as 0.
12.999999999999999..........9999999999999999999999
is returned as 13.
12.888888888888........8888888888888888888888888888 is left unchanged.
Note the following Decimal operations:
>>> getcontext().prec = 20
>>> Decimal('3.9999999999999999999999999999999')
Decimal('3.9999999999999999999999999999999')
>>> Decimal('3.9999999999999999999999999999999')+0
Decimal('4.0000000000000000000')
>>> (Decimal('3.9999999999999999999999999999999')+0).normalize()
Decimal('4')
>>>
>>> Decimal(76500)
Decimal('76500')
>>> Decimal(76500).normalize()
Decimal('7.65E+4')
>>> Decimal(76500).normalize()+0
Decimal('76500')
>>>
Hence the line: ((value+0).normalize()+0)
'''
x = makeComplex(x)
getcontext().prec -= 3

almostZero = Decimal ('1e-' + str(getcontext().prec) )
L1 = [
( ( (v, Decimal(0))[int(offset)] + 0 ).normalize() + 0 )
for v in x[:2]
for offset in (
(v > -almostZero) and (v < almostZero),
)
]

for offset in range (0,2,1) :
v1 = L1[offset]
if v1 == 0 : pass
else :
t1 = v1.as_tuple()
if len( t1[1] ) < getcontext().prec  : pass
else : L1[offset] = x[offset]

getcontext().prec += 3

while x[2] == 'polar' :
if L1[0] == 0 :
L1[1] = L1[0] ; break
if L1[0] < 0 :
L1[0] = (L1[0]).copy_negate()
L1[1] += 180
L1[1] %= 360
if L1[1] <= -180 : L1[1] += 360
if L1[1] > 180 : L1[1] -= 360
break

return tuple(L1)


### CompleX_to_complex (x)

def CompleX_to_complex (x) :
'''
complex, float = CompleX_to_complex (x)
'''
x = makeComplex(x)
if x[2] == 'polar' : x = convertComplex (x)

x = clean_complex (x)

cx1 = complex( float(x[0]), float(x[1]) )
fl1 = float(cx1.real)
if cx1.imag : fl1 = None
return (cx1, fl1)


## Arithmetic functions

def addComplex(v1,v2) :
'''
Calculation is rectangular.
The spelling of SuM indicates type CompleX.
'''
ToP, BottoM = [ CompleX(v) for v in (v1,v2) ]

sum = (ToP.r+BottoM.r, ToP.i+BottoM.i, 'rect')

SuM = CompleX(sum)

return SuM


### subtractComplex(v1,v2)

def subtractComplex(v1,v2) :
'''
DifferencE = subtractComplex(v1,v2)
where DifferencE = v1 - v2
Calculation is rectangular.
'''

ToP, BottoM = [ CompleX(v) for v in (v1,v2) ]
difference = (ToP.r-BottoM.r, ToP.i-BottoM.i, 'rect')
DifferencE = CompleX(difference)

return DifferencE


### multiplyComplex (v1, v2)

def multiplyComplex (v1, v2) :
'''
ProducT = multiplyComplex (multiplicand, multiplier)
Calculation is 'rect'.
'''

ToP, BottoM = [ CompleX(v) for v in (v1,v2) ]
a,b = ToP.r, ToP.i
c,d = BottoM.r, BottoM.i
product = ( a*c-b*d, b*c+a*d, 'rect' )
ProducT = CompleX(product)
return ProducT


### divideComplex (dividend, divisor)

def divideComplex (dividend, divisor) :
'''
QuotienT = divideComplex (dividend, divisor)
Calculation is 'polar'.
divisor must be non-zero.
if dividend == 0, output is 0.
'''
DividenD, DivisoR = [ CompleX(v) for v in (dividend, divisor) ]
if not DivisoR.m :
print ('divideComplex: polar divisor must be non-zero.')
exit(88)
if (not DividenD.m) : return CompleX(0)
quotient = (DividenD.m/DivisoR.m, DividenD.p-DivisoR.p, 'polar')
return CompleX(quotient)


## Exponential functions

### complexSQRT (x)

def complexSQRT (x) :
'''
RooT = complexSQRT (x)
calculation is 'polar'.
'''
CX1 = CompleX(x)
if not CX1.m : return CompleX(0)

modulus, phase = CX1.m, CX1.p
if modulus < 0 :
modulus *= -1 ; phase += 180
modulus = modulus.sqrt()
phase = phase/2
root = (modulus,phase,'polar')
return CompleX(root)


### complexCUBEroot (x)

def complexCUBEroot (x) :
'''
RooT = complexCUBEroot (x)
'polar' output is useful because the other 2 cube roots are:
(root[0], root[1]+120, 'polar')
(root[0], root[1]+240, 'polar')
'''
CX1 = CompleX(x)
if not CX1.m : return CompleX(0)

# Calculating the cube root is a polar operation.

modulus, phase = CX1.m, CX1.p
if modulus < 0 :
modulus *= -1 ; phase += 180
modulus_of_root = modulus ** ( Decimal(1) / Decimal(3) )
phase_of_root = phase/3
root = (modulus_of_root, phase_of_root, 'polar')
RooT = CompleX(root)

return RooT


# class CompleX

class CompleX :
'''
This class has 5 attributes:
self.r : the real coordinate of the complex number
self.i : the imaginary coordinate of the complex number
self.m : the modulus of the complex number
self.p : the phase of the complex number in degrees
self.c : the class expressed as Python type complex
'''

def __init__(self, value=0):
self.set(value)
return

def check(self) :
#        print ('entering check(self) :')
precisions = [] ; status = 0
for v in (self.r, self.i, self.m, self.p) :
if not isinstance(v, Decimal) :
print ('value not Decimal:', str(v)[:20]) ; status = -1 ; continue
precisions += [len(v.as_tuple()[1])]
if not isinstance(self.c, complex) :
print ('self.c not complex:', str(self.c)[:20]) ; status = -1

if status : exit(39)

cx1, not_used = CompleX_to_complex ((self.r, self.i, 'rect'))
if cx1 != self.c : print ("""
for self = {}
Rect values don't match self.c:
{}
{}""".format(
self,cx1,self.c
)
)
cx1, not_used = CompleX_to_complex ((self.m, self.p, 'polar'))
if cx1 != self.c : print ("""
for self = {}
Polar values don't match self.c:
{}
{}""".format(
self,cx1,self.c
)
)
return

def set(self, value=Decimal(0)):
#        print ('entering set(self, ',value,'):')

t1 = makeComplex(value)
if t1[2] == 'rect' :
self.r, self.i = t1[:2]
if not self.r and not self.i : self.m = self.p = Decimal(0)
else :
t1 = convertComplex(value)
self.m, self.p = t1[:2]
else :
self.m, self.p = t1[:2]
if not self.m : self.r = self.i = self.p = Decimal(0)
else :
t1 = convertComplex(value)
self.r, self.i = t1[:2]

self.c, not_used = CompleX_to_complex ((self.r, self.i, 'rect'))
self.check()
return

def clean(self) :
#        print ('entering clean(self) :')
self.check()
#output = clean_complex (x)
self.r, self.i = clean_complex ((self.r, self.i, 'rect'))
self.m, self.p = clean_complex ((self.m, self.p, 'polar'))
self.check()
return

def _print(self) :
'''
output a string containing all the info
about self and suitable for printing.'''
self.check()
return '''
self = {}
real = {}
imag = {}
modulus = {}
phase = {}
as type complex: {}
'''.format(
self,
self.r,    self.i,
self.m,    self.p,
self.c,
)


The above code highlights the power of Python in a new class called CompleX. When a new instance of this class is created, it can be accessed through its attributes and changed through its methods with the simplest of syntax.

Seemingly complicated functions such as complex cube root can be implemented in only a few lines of simple code.

## Examples

CX1 = CompleX()
print ('isinstance(CX1, CompleX):', isinstance(CX1, CompleX))
print ('CX1 =', CX1._print())

isinstance(CX1, CompleX): True
CX1 =
self = <__main__.CompleX object at 0x101a463c8>
real = 0
imag = 0
modulus = 0
phase = 0
as type complex: 0j

CX2 = CompleX((5,30,'polar'))
print ('isinstance(CX2, CompleX):', isinstance(CX2, CompleX))
print ('CX2 =', CX2._print())

isinstance(CX2, CompleX): True
CX2 =
self = <__main__.CompleX object at 0x101245240>
real = 4.33012701892219323381861585376468
imag = 2.50000000000000000000000000000000
modulus = 5
phase = 30
as type complex: (4.330127018922194+2.5j)

CX3 = CompleX('-5+12j')
print ('isinstance(CX3, CompleX):', isinstance(CX3, CompleX))
print ('CX3 =', CX3._print())

isinstance(CX3, CompleX): True
CX3 =
self = <__main__.CompleX object at 0x101416f28>
real = -5.0
imag = 12.0
modulus = 13.0
phase = 112.619864948040426172949010876680
as type complex: (-5+12j)

CX2 = CX1 # shallow copy.
CX2 = CompleX(CX1) # deep copy.

CX3.check() # This should not produce error.

CX3.set(-7.5)
print ('CX3 =', CX3._print())

self = <__main__.CompleX object at 0x101415f60>
real = -7.5
imag = 0
modulus = 7.5
phase = 180
as type complex: (-7.5+0j)


# Solving the cubic equation

Figure 1: Cubic function and X intercepts.

Origin at point ${\displaystyle (0,0)}$.
Intercepts at points ${\displaystyle (3,0),\ (-1,0),\ (-2.5,0)}$

The cubic equation is expressed thus: ${\displaystyle ax^{3}+bx^{2}+cx+d=0}$ where both ${\displaystyle a,d}$ are non-zero.

We will solve the equation ${\displaystyle 2x^{3}+x^{2}-16x-15=0}$ as an example of the use of Python's Decimal module and the new class CompleX. See Figure 1.

a,b,c,d = (2,1,-16,-15)

A = 2*b*b*b - 9*a*b*c + 27*a*a*d
C = b*b-3*a*c

p = -3*C
q = -A


## The depressed cubic and Vieta's substitution

Let ${\displaystyle x={\frac {-(b+t)}{3a}}}$, substitute this value of ${\displaystyle x}$ in the cubic equation and produce the depressed cubic ${\displaystyle t^{3}+pt+q=0.}$

Let ${\displaystyle t=w-{\frac {p}{3w}}}$, substitute this value of ${\displaystyle t}$ in the depressed equation and produce the quadratic: ${\displaystyle W^{2}+qW+C^{3}=0}$ where ${\displaystyle W=w^{3}}$.

Therefore, ${\displaystyle W={\frac {-q\pm {\sqrt {q^{2}-4C^{3}}}}{2}}}$.

disc = q*q - 4*C*C*C

RooT = complexSQRT (disc)

DividenD = addComplex ( -q, RooT )

W = divideComplex(DividenD, 2)

isinstance(W, CompleX) or exit(48)

r = Decimal(3).sqrt()

W.clean()

print ('\n################\nW =', W._print())

################
W =
self = <__main__.CompleX object at 0x10137cbe0>
real = -665
imag = 685.892119797275408236868751236328
modulus = 955.33920677422215800938250724701
phase = 134.113967095785813976410473653471
as type complex: (-665+685.8921197972754j)


### The cube roots

Figure 2: Three cube roots of W.
${\displaystyle w_{1}}$ = RooT1; ${\displaystyle w_{2}}$ = RooT2; ${\displaystyle w_{3}}$ = RooT3.
${\displaystyle W\ =\ w_{1}^{3}\ =\ w_{2}^{3}\ =\ w_{3}^{3}}$.
Phase of ${\displaystyle W\ =\ \alpha }$
Phase of ${\displaystyle w_{1}\ =\ \beta \ =\ {\frac {\alpha }{3}}}$
Phase of ${\displaystyle w_{2}\ =\ \beta \ +\ 120.}$
Phase of ${\displaystyle w_{3}\ =\ \beta \ -\ 120.}$
RooT1 =  complexCUBEroot(W)
t1 = (RooT1.m, RooT1.p+120, 'polar')
t2 = (RooT1.m, RooT1.p-120, 'polar')
RooT2 = CompleX(t1)
RooT3 = CompleX(t2)
for root in ( RooT1, RooT2, RooT3 ) :
root.clean()
print ('++++++++++++++++\nRooTx:', root._print())

print ('''
self.i
------- = {}
sqrt(3)
'''.format(
float(root.i/r)
)
)
# Each value of w has format (k + 1j*m*sqrt(3)). It can be shown that t = 2k.
t = 2*root.c.real
x = -(b+t)/(3*a)
result = a*x*x*x + b*x*x + c*x + d
print ('x =', x, 'result =',result)

++++++++++++++++
RooTx:
self = <__main__.CompleX object at 0x1012572e8>
real = 7
imag = 6.92820323027550917410978536602346
modulus = 9.84885780179610472174621141491761
phase = 44.7046556985952713254701578844903
as type complex: (7+6.928203230275509j)

self.i
------- = 4.0
sqrt(3)

x = -2.5 result = 0.0
++++++++++++++++
RooTx:
self = <__main__.CompleX object at 0x101257240>
real = -9.5
imag = 2.59807621135331594029116951225890
modulus = 9.84885780179610472174621141491761
phase = 164.704655698595271325470157884490
as type complex: (-9.5+2.598076211353316j)

self.i
------- = 1.5
sqrt(3)

x = 3.0 result = 0.0
++++++++++++++++
RooTx:
self = <__main__.CompleX object at 0x10136d400>
real = 2.5
imag = -9.52627944162882511440095487828231
modulus = 9.84885780179610472174621141491761
phase = -75.2953443014047286745298421155097
as type complex: (2.5-9.526279441628825j)

self.i
------- = -5.5
sqrt(3)

x = -1.0 result = 0.0


The three roots of the given cubic are ${\displaystyle -1,3,-2.5}$.

# Assignments

 Calculate the value of ${\displaystyle \pi }$ to 10,000 places of decimals.  When calculating ${\displaystyle \pi }$:  >>> Decimal(str(math.pi)) Decimal('3.141592653589793') # π accurate to precision 16. >>> >>> Decimal(math.pi) Decimal('3.141592653589793115997963468544185161590576171875') # Not an accurate value of π. Why? >>> # 3.14159265358979323846264338327950288419716939937510582097494459230781 # Correct value of π.  ${\displaystyle N}$ = ${\displaystyle (9,60,}$ 'polar'${\displaystyle )}$.  What are the square roots of ${\displaystyle N}$?   ${\displaystyle ({\sqrt {9}},\ {\frac {60}{2}},}$ 'polar'${\displaystyle )\ =\ (\pm 3,\ 30,}$ 'polar'${\displaystyle )}$. ${\displaystyle (-3,\ 30,}$ 'polar'${\displaystyle )}$ = ${\displaystyle (3,\ 30\pm 180,}$ 'polar'${\displaystyle )}$  ${\displaystyle r_{1}=(2,60,}$ 'polar'${\displaystyle )}$ is one of the cube roots of a certain number ${\displaystyle N}$.  What is the number ${\displaystyle N}$? What are the other two cube roots of ${\displaystyle N}$?   ${\displaystyle r_{1}=2(\cos 60+1j*\sin 60).}$ ${\displaystyle N=r_{1}^{3}=2^{3}(\cos(60*3)+1j*\sin(60*3))=8(\cos 180+1j*\sin 180)=8(-1)=-8}$. The other two cube roots of ${\displaystyle N}$ are: ${\displaystyle r_{2},r_{3}=(2,60\pm 120,}$ 'polar'${\displaystyle )}$  Given that CX1 is type CompleX, add method sqrt() to class CompleX so that the expression CX1.sqrt() returns the square root of CX1 as type CompleX.

# References

Python's built-in functions:

Python's documentation: