Division of complex numbers in Python
# We have already seen addition, subtraction,
# multiplication, conjugation, and
# multiplicative inverses of complex numbers.
#
# We can now define division of complex numbers.
#
# Suppose that w and z are complex numbers and
#
#     z != 0.
#
# Division by z means multiplication by the
# multiplicative inverse of z:
#
#     w/z = w*(1/z).
#
# Thus, complex division follows directly from
# our definition of a multiplicative inverse.

import cmath
import math


w = 2.3 + 4.1j
z = 1.7 - 2.4j

print( "\nw:" )
print( w )

print( "\nz:" )
print( z )

print( "\nw/z:" )
print( w/z )

print( "\nw*(1/z):" )
print( w*(1/z) )


# Apart from possible floating-point rounding,
# these last two values are the same.


# We can derive a formula for division in terms
# of the real and imaginary components.
#
# Suppose
#
#     w = a + bj
#
# and
#
#     z = c + dj.
#
# We want to calculate
#
#     a + bj
#     ------.
#     c + dj
#
# Recall that the multiplicative inverse of z is
#
#        1       c - dj
#     ------ = ---------.
#     c + dj     c^2+d^2
#
# Therefore,
#
#     a + bj
#     ------
#     c + dj
#
#                  c - dj
#       = (a + bj) --------
#                  c^2+d^2
#
#         (a + bj)(c - dj)
#       = -----------------.
#              c^2+d^2
#
# Multiplying out the numerator gives
#
#     (a + bj)(c - dj)
#
#         = ac + bd + (bc - ad)j.
#
# Therefore,
#
#     a + bj
#     ------
#     c + dj
#
#       ac + bd     bc - ad
#       -------  +  ------- j.
#       c^2+d^2     c^2+d^2


# Let us verify this formula.

a = w.real
b = w.imag
c = z.real
d = z.imag

quotient_formula = (
    (a*c + b*d)/(c**2 + d**2)
    + 1j*(b*c - a*d)/(c**2 + d**2)
)

print( "\nw/z calculated directly:" )
print( w/z )

print( "\nw/z calculated from the components:" )
print( quotient_formula )


# Another way to derive the same result is to
# multiply the numerator and denominator by the
# complex conjugate of the denominator:
#
#     w     w*conj(z)
#     -  =  ---------.
#     z     z*conj(z)
#
# Recall that
#
#                         2
#     z*conj(z) = |z|.
#
# Thus,
#
#     w     w*conj(z)
#     -  =  ----------.
#     z          2
#              |z|
#
# This is often the most useful general formula
# for complex division.
#
# In Python, the complex conjugate of z is
#
#     z.conjugate().


print( "\nw/z:" )
print( w/z )

print( "\nw*z.conjugate()/abs(z)**2:" )
print( w*z.conjugate()/abs(z)**2 )


# Notice that the denominator
#
#      2
#     |z|
#
# is a positive real number whenever z != 0.
#
# Multiplying by the conjugate has therefore
# removed the complex number from the
# denominator.


# Division has many useful properties.
#
# First, dividing a number by itself gives 1,
# provided the number is nonzero:
#
#     z/z = 1.


print( "\nz/z:" )
print( z/z )


# Dividing by 1 leaves a number unchanged:
#
#     z/1 = z.

print( "\nz/1:" )
print( z/1 )


# Dividing zero by any nonzero number gives
# zero:
#
#     0/z = 0.

print( "\n0/z:" )
print( 0/z )


# Division by zero, however, is not defined.
#
# Thus, an expression such as
#
#     w/0
#
# does not represent a complex number.
#
# In Python, attempting to evaluate it causes
# a ZeroDivisionError.


# Division is not commutative.
#
# In general,
#
#     w/z != z/w.


print( "\nw/z:" )
print( w/z )

print( "\nz/w:" )
print( z/w )


# Division is also not associative.
#
# In general,
#
#     (w/z)/v != w/(z/v).

v = -0.8 + 1.3j

print( "\n(w/z)/v:" )
print( (w/z)/v )

print( "\nw/(z/v):" )
print( w/(z/v) )


# In fact,
#
#     (w/z)/v = w/(z*v),
#
# while
#
#     w/(z/v) = w*v/z.


print( "\n(w/z)/v and w/(z*v):" )
print( (w/z)/v )
print( w/(z*v) )

print( "\nw/(z/v) and w*v/z:" )
print( w/(z/v) )
print( w*v/z )


# Conjugation works particularly nicely with
# division.
#
# The conjugate of a quotient is the quotient
# of the conjugates:
#
#           / w \     conj(w)
#     conj( | - | ) = -------.
#           \ z /     conj(z)
#
# provided z != 0.
#
# In Python, this is
#
#     (w/z).conjugate()
#
# and
#
#     w.conjugate()/z.conjugate().


print( "\nConjugate of w/z:" )
print( (w/z).conjugate() )

print( "\nw.conjugate()/z.conjugate():" )
print( w.conjugate()/z.conjugate() )


# This follows from the corresponding
# properties for multiplication and
# multiplicative inverses.


# Multiplicative inverses also work naturally
# with division.
#
# Recall that we represent the multiplicative
# inverse of a scalar z by
#
#     1/z.
#
# The inverse of a quotient is
#
#       1
#     -----
#      w/z
#
#       z
#     = -.
#       w
#
# Thus,
#
#       1
#     -----
#      w/z
#
#       1/w
#     = -----.
#       1/z
#
# In other words,
#
#     inverse(w/z)
#
#         = inverse(w)/inverse(z).


print( "\nInverse of w/z:" )
print( 1/(w/z) )

print( "\nz/w:" )
print( z/w )

print( "\n(1/w)/(1/z):" )
print( (1/w)/(1/z) )


# Magnitudes also behave very simply under
# division.
#
# The magnitude of a quotient is the quotient
# of the magnitudes:
#
#     |w/z| = |w|/|z|.


print( "\nabs(w/z):" )
print( abs(w/z) )

print( "\nabs(w)/abs(z):" )
print( abs(w)/abs(z) )


# This follows immediately from
#
#     w/z = w*(1/z)
#
# together with
#
#     |w*z| = |w|*|z|
#
# and
#
#     |1/z| = 1/|z|.


# Arguments also have a simple relationship.
#
# Multiplication adds arguments, while division
# subtracts them:
#
#     arg(w/z)
#
#         = arg(w) - arg(z)    modulo 2*pi.
#
# Because cmath.phase(...) returns a principal
# argument, the two numerical values may differ
# by a multiple of 2*pi.


print( "\nphase(w/z):" )
print( cmath.phase(w/z) )

print( "\nphase(w) - phase(z):" )
print( cmath.phase(w) - cmath.phase(z) )


# The same relationship can be expressed in
# degrees:
#
#     arg(w/z)
#
#         = arg(w) - arg(z)    modulo 360 deg.


print( "\nphase(w/z) in degrees:" )
print( math.degrees(cmath.phase(w/z)) )

print( "\nphase(w) - phase(z) in degrees:" )
print(
    math.degrees(cmath.phase(w))
    - math.degrees(cmath.phase(z))
)


# Geometrically, division therefore has two
# effects.
#
# If
#
#     w = |w| exp(j*alpha)
#
# and
#
#     z = |z| exp(j*beta),
#
# then
#
#     w     |w|
#     -  =  --- exp(j*(alpha - beta)).
#     z     |z|
#
# Thus, dividing by z:
#
#   1. divides the magnitude by |z|, and
#
#   2. subtracts the argument of z.
#
# Equivalently, dividing by z scales by
#
#     1/|z|
#
# and rotates by
#
#     -arg(z).


# There are also several useful algebraic
# identities involving division.
#
# Provided all denominators are nonzero,
#
#     (w*z)/z = w.

print( "\n(w*z)/z:" )
print( (w*z)/z )

print( "\nw:" )
print( w )


# Multiplication can be applied to both the
# numerator and denominator:
#
#     (w*v)/(z*v) = w/z
#
# provided v != 0.

print( "\n(w*v)/(z*v):" )
print( (w*v)/(z*v) )

print( "\nw/z:" )
print( w/z )


# Division distributes over addition and
# subtraction in the numerator:
#
#     (w + v)/z = w/z + v/z
#
# and
#
#     (w - v)/z = w/z - v/z.


print( "\n(w + v)/z:" )
print( (w + v)/z )

print( "\nw/z + v/z:" )
print( w/z + v/z )

print( "\n(w - v)/z:" )
print( (w - v)/z )

print( "\nw/z - v/z:" )
print( w/z - v/z )


# Be careful: division does NOT distribute over
# addition in the denominator.
#
# In general,
#
#       w       w     w
#     ----- != --- + ---.
#     z + v     z     v


print( "\nw/(z + v):" )
print( w/(z + v) )

print( "\nw/z + w/v:" )
print( w/z + w/v )


# Quotients can also be multiplied:
#
#     w   u     w*u
#     - * -  =  ---.
#     z   v     z*v

u = 3.2 - 0.6j

print( "\n(w/z)*(u/v):" )
print( (w/z)*(u/v) )

print( "\n(w*u)/(z*v):" )
print( (w*u)/(z*v) )


# Dividing one quotient by another gives
#
#     w/z     w*v
#     ---  =  ---.
#     u/v     z*u
#
# This is the familiar rule of multiplying by
# the reciprocal.

print( "\n(w/z)/(u/v):" )
print( (w/z)/(u/v) )

print( "\n(w*v)/(z*u):" )
print( (w*v)/(z*u) )


# Integer powers also behave naturally with
# division:
#
#             n
#     (w/z)
#
#          n
#         w
#       = --.
#          n
#         z
#
# This holds for integer n whenever the
# expressions involved are defined.
#
# Recall that exponentiation in Python uses
# '**', not '^'.

n = 4

print( "\n(w/z)**n:" )
print( (w/z)**n )

print( "\nw**n/z**n:" )
print( w**n/z**n )


# We can summarize the most important
# properties of complex division as follows.
#
# Division by z is multiplication by its
# multiplicative inverse:
#
#     w/z = w*(1/z).
#
# The conjugate of a quotient is
#
#                  conj(w)
#     conj(w/z) = ---------.
#                  conj(z)
#
# In Python:
#
#     (w/z).conjugate()
#
#         = w.conjugate()/z.conjugate().
#
# The inverse of a quotient is
#
#       1       z       1/w
#     -----  =  -  =  -----.
#      w/z      w       1/z
#
# The magnitude of a quotient is
#
#               |w|
#     |w/z| = -----.
#               |z|
#
# The argument of a quotient is
#
#     arg(w/z)
#
#         = arg(w) - arg(z)    modulo 2*pi.
#
# Thus, geometrically, division divides
# magnitudes and subtracts arguments.