# 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 $\cos x$ and $\sin x$ see Recipes.
* arctan x
* We'll try $\arctan x$ first because it's easily understood and implemented, and it can be used to calculate $\pi$ because the result of this expression (as in all theoretical math) is in radians.
* $\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 π
* There are many ways to calculate $\pi$. For example:
* $\pi =6*\arctan(\tan 30)=6*\arctan({\frac {\sqrt {3}}{3}}).$
* Based on the following, we will perform six calculations of $\pi$ and compare the results.
* $\pi =30*\arctan(\tan 6)$
* $\pi =24*\arctan(\tan 7.5)$
* $\pi =12*\arctan(\tan 15)$
* $\pi =8*\arctan(\tan 22.5)$
* $\pi =6*\arctan(\tan 30)$
* $\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 (Θ / 2)
* $\tan {\frac {\theta }{2}}$$=\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 (y, x)
* For the corresponding function using floating point arithmetic see
* math.atan2(y, x)
* This function invokes $arctan(x)\ 0\leq x\leq 1$.
* If $\tan \theta >1,\ x={\frac {1}{\tan \theta }}$ and the result is ${\frac {\pi }{2}}-\arctan(x).$
* However this function invokes $\arctan(x)$ with a value less than $0.325$ so that the expression
* $x^{2n+1}$ in the expansion of $\arctan x$ vanishes as rapidly as possible. Therefore:
* $75=90-\arctan({\frac {1}{\tan 75}})$
* $68=4\arctan(\tan {\frac {68}{4}})$
* $32=2\arctan(\tan {\frac {32}{2}})$
* $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
* degreesToRadians (θinDegrees)
* 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 $modulus\ *\ (\ \cos(phaseInDegrees)\ +\ 1j*\sin(phaseInDegrees)\ )$.
* Or:
* (realPart, imaginaryPart, 'rect')
* where $realPart,imaginaryPart$ are the rectangular coordinates of complex number $realPart\ +\ 1j*imaginaryPart$.
* The four values $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:
* $-n*(\cos \phi +1j*\sin \phi )=n*(\cos(\phi \pm 180)+1j*\sin(\phi \pm 180))$
* and, for example:
* ${\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 $-4$ is:
* ${\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.
* Administrative functions
* 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]
* θinRadians = degreesToRadians (phase)
* cosθ = cos(θinRadians)
* sinθ = sin(θinRadians)
* 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
* addComplex(v1,v2)
* def addComplex(v1,v2) :
* SuM = 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 $(0,0)$.
* Intercepts at points $(3,0),\ (-1,0),\ (-2.5,0)$
* The cubic equation is expressed thus: $ax^{3}+bx^{2}+cx+d=0$ where both $a,d$ are non-zero.
* We will solve the equation $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 $x={\frac {-(b+t)}{3a}}$, substitute this value of $x$ in the cubic equation and produce the depressed cubic $t^{3}+pt+q=0.$
* Let $t=w-{\frac {p}{3w}}$, substitute this value of $t$ in the depressed equation and
* produce the quadratic: $W^{2}+qW+C^{3}=0$ where $W=w^{3}$.
* Therefore, $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.
* $w_{1}$ = RooT1; $w_{2}$ = RooT2; $w_{3}$ = RooT3.
* $W\ =\ w_{1}^{3}\ =\ w_{2}^{3}\ =\ w_{3}^{3}$.
* Phase of $W\ =\ \alpha$
* Phase of $w_{1}\ =\ \beta \ =\ {\frac {\alpha }{3}}$
* Phase of $w_{2}\ =\ \beta \ +\ 120.$
* Phase of $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 $-1,3,-2.5$.

# Assignments

* Calculate the value of $\pi$ to 10,000 places of decimals.
* When calculating $\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 π.
* $N$ = $(9,60,$ 'polar'$)$.
* What are the square roots of $N$?
* $({\sqrt {9}},\ {\frac {60}{2}},$ 'polar'$)\ =\ (\pm 3,\ 30,$ 'polar'$)$. $(-3,\ 30,$ 'polar'$)$ = $(3,\ 30\pm 180,$ 'polar'$)$
* $r_{1}=(2,60,$ 'polar'$)$ is one of the cube roots of a certain number $N$.
* What is the number $N$?
* What are the other two cube roots of $N$?
* $r_{1}=2(\cos 60+1j*\sin 60).$
* $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 $N$ are: $r_{2},r_{3}=(2,60\pm 120,$ 'polar'$)$
* 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.

# Further Reading or Review

* Previous Lesson: Iteration and Iterators
* This Lesson:Using Python's Decimal module
* Next Lesson: Interfacing with Unix
* Course Home Page

# References

Python's built-in functions:
"abs",
Python's documentation:
Decimal fixed point and floating point arithmetic, A first look at classes