import numpy as np
import matplotlib.pyplot as plt
import math

"""
Explanation: Algebra and Functions
End of explanation
"""


# sample x values
x = np.linspace(-10, 10, 2001).astype(np.float32)

# known variables
a = 2.0
b = 5.0
c = -12.0

# quadratic equation
def eq(x,a,b,c):
    return a*x**2 + b*x + c

y = eq(x,a,b,c)

sym = (-b)/(2*a)

# list ot store x values where y = 0 (aka the x intercepts)
x_intercepts = []

# loop through y values find x intercepts (out of the sample values)
# later we'll implement the maths
for i in range(y.size):
    if y[i] < 0.1 and y[i] > -0.1:
        x_intercepts.append(i)

# plot graph and points
plt.plot(x,y, label='line', color='b')
plt.plot(x[x_intercepts[0]], y[x_intercepts[0]], 'om', color='g', label='x intercept')
plt.plot(x[x_intercepts[1]], y[x_intercepts[1]], 'om', color='g', label='x intercept')
plt.axvline(x=sym, color='r', linestyle='--', label='line of symmetry')
plt.plot(sym, eq(sym,a,b,c), 'om', color='y', label='vertex')


plt.legend(loc=1)
plt.grid(True)
plt.show()

"""
Explanation: Contents
1.Laws of Indices
- Multiplication
- Division
- Raising to power
- Taking a root
- Fraction Indices
- Zero indices
- Negative indices
2.Surds
- Addition and subtraction
- Multiplication and division
- Simplifying
- Rationalising the denominator
3.Quadratic Functions
- Graphing
- Discriminant
- Completing the square
4.Simultaneous Equations
- Elimination
- Substitution
5.Linear and Quadratic Inequalitites
- Rules
- Graphing
6.Polynomials
- Expanding brackets
- Factorising
- Division
- Remainder Theorem
- Factor Theorem
7.Graphs
- Plotting polynomials
- Modulus of linear function
- Graphing log
- Plotting reciprocal of x
8.Functions
- Composite functions
- Inverse functions
- Transformation of functions
9.Rational Functions
- Rational functions to partial fractions
<a id='Laws_of_Indices'></a>
Laws of Indices For All Rational Exponents
<a id='Multiplication'></a>
Multiplication
$x^a(x^b) = x^{a+b}$
<a id='Division'></a>
Division
$\frac{x^a}{x^b} = x^{a-b}$
<a id='Raising_to_power'></a>
Raising to power
$(x^a)^b = x^{ab}$
<a id='Taking_a_root'></a>
Taking a root
$\sqrt[b]{x^a} = x^{\frac{a}{b}}$
<a id='Fraction_Indices'></a>
Fraction Indices
$\frac{x}{y}^{\frac{a}{b}} = \frac{x^{\frac{a}{b}}}{y^{\frac{a}{b}}}$
$= \frac{\sqrt[b]{(x^a)}}{(\sqrt[b]{y})^a}$
note: the order of dealing with  denominator and numerator of the indices doesn't matter
<a id='Zero_indices'></a>
Zero indices
$x^0 = 1$
<a id='Negative_indices'></a>
Negative indices
$x^{-a} = \frac{1}{x^a}$
<a id='Surds'></a>
Surds
<a id='Addition_and_subtraction'></a>
Addition and subtraction
$a\sqrt{b} + c\sqrt{b} = (a + c)\sqrt{b}$
$a\sqrt{b} - c\sqrt{b} = (a - c)\sqrt{b}$
<a id='Multiplication_and_division'></a>
Multiplication and division
$\sqrt{ab} = \sqrt{a}\sqrt{b}$
$\sqrt{\frac{a}{b}} = \frac{\sqrt{a}}{\sqrt{b}}$
<a id='Simplifying'></a>
Simplifying
$\sqrt{75} = \sqrt{25}\sqrt{3}$
$= 5\sqrt{3}$
<a id='Rationalising_the_denominator'></a>
Rationalising the denominator
$\frac{a}{\sqrt{b}} = \frac{a}{\sqrt{b}} \times \frac{\sqrt{b}}{\sqrt{b}}$
$= \frac{a\sqrt{b}}{b}$
Example
$\frac{\sqrt{7} + 1}{\sqrt{7} - 2} = \frac{\sqrt{7} + 1}{\sqrt{7} - 2} \times \frac{\sqrt{7} + 2}{\sqrt{7} + 2}$
$= \frac{9 + 3\sqrt{7}}{3}$
$= 3 + \sqrt{7}$
<a id='Quadratic_Functions'></a>
Quadratic Functions
in format (or any polynomial of degree 2):
$ax^2 + bx + c = 0$
Quadratic Formula: $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$

the graph is always an $x^2$ curve
the solution is always the x intercepts
there can be 2, 1 or 0 solutions

<a id='Quadratic_graphing'></a>
Graphing:
note: a, b and c doesn't always represent the values in $ax^2 + bx + c = 0$
In factored form $(x + a)(x + b) = 0$:
- find x intercepts
- find vertex
In standard form $ax^2 + bx + c = 0$:
- find symmetry with $x = \frac{-b}{2a}$
- find vertex by substituting in x value for symmetry
- find x intercepts of y values for x values either side
In vertex form $a(x + b)^2 + c = 0$:
- find when $x + b = 0$ to find symmetry x value
- c = y value for vertex
- if b < 0, graph is negative
- then substitute x values either side of symmetry for other 2 points required
End of explanation
"""


# sample x values
x = np.linspace(-10, 10, 2001).astype(np.float32)

# quadratic equation
def eq(a, b, c):
    return a*x**2 + b*x + c

# setup figure and axes
fig, ax = plt.subplots(1, 3, sharex=True, figsize=(16,5))

# define variables for each graph using examples from above
graph_vars = [[2, 4, 2], [2, 5, -12], [-4, -11, -16]]

# loop for 3 graphs
for a in range(3):
    
    # put the current variables into the equation
    y = eq(graph_vars[a][0], graph_vars[a][1], graph_vars[a][2])

    # list ot store x values where y = 0 (aka the x intercepts)
    x_intercepts = []

    # loop through y values find x intercepts (out of the sample values)
    for i in range(y.size):
        if y[i] < 0.01 and y[i] > -0.01:
            x_intercepts.append(i)

    # plot the current data and x intercept
    ax[a].plot(x,y)
    for x_intercept in x_intercepts:
        ax[a].plot(x[x_intercept], y[x_intercept], 'om')

    ax[a].grid(True)

plt.show()

"""
Explanation: <a id='Discriminant'></a>
Discriminant
The discriminant is $b^2 - 4ac$
noted as $\Delta$
if $b^2 - 4ac = 0$
- the line touches the x axis
- 1 "real" or repeated solution
- left with $x = \frac{-b}{2a}$
- this is the equation for the axis of symmetry
- eg. $4^2 - 4\times2\times2 = 0$
if $b^2 - 4ac > 0$
- the line intersects the x axis
- 2 "real" solutions
- can be $+\sqrt{b^2 - 4ac}$
- or $-\sqrt{b^2 - 4ac}$
- eg. $5^2 - 4\times2\times-12 = 121$
if $b^2 - 4ac < 0$
- the line doesn't touch or intersect the x axis
- 0 "real" solutions
- because no "real" roots of any negative number
- eg. $-4^2 - 4\times-4\times-16 = -135$
End of explanation
"""


# sample x values
x = np.linspace(-10, 10, 2001).astype(np.float32)

# known variables
M = 1
C = 1

# linear equation
def lin_eq(x, M, C):
    return M*x + C

lin_y = lin_eq(x, M, C)

# known variables
a = 2.0
b = -5.0
c = -12.0

# quadratic equation
def quad_eq(x,a,b,c):
    return a*x**2 + b*x + c

quad_y = quad_eq(x,a,b,c)

# list ot store x values where y = 0 (aka the x intercepts)
x_intercepts = []

# loop through y values find x intercepts (out of the sample values)
# later we'll implement the maths
for i in range(quad_y.size):
    if quad_y[i] < 0.1 and quad_y[i] > -0.1:
        x_intercepts.append(i)

# plot graph and points
fig, ax = plt.subplots(2, 1, sharex=True, figsize=(6,10))

ax[0].set_title('$y = x + 1$')

ax[0].plot(x,lin_y, label='line', color='b')

ax[0].fill_between(x, 20, lin_y, label='$y > x + 1$', facecolor='pink')

ax[1].set_title('$y = 2x^2 - 5x - 12$')

ax[1].plot(x,quad_y, label='line', color='b')
ax[1].plot(x[x_intercepts[0]], quad_y[x_intercepts[0]], 'om', color='g', label='x intercept')
ax[1].plot(x[x_intercepts[1]], quad_y[x_intercepts[1]], 'om', color='g', label='x intercept')

ax[1].fill_between(x[0:x_intercepts[0]], 0, quad_eq(x[0:x_intercepts[0]],a,b,c), label='$0 < 2x^2 - 5x - 12 < y$', facecolor='pink')
ax[1].fill_between(x[x_intercepts[1]:], 0, quad_eq(x[x_intercepts[1]:],a,b,c), facecolor='pink')

for axes in ax:
    axes.legend(loc=1)
    axes.grid(True)
    
plt.show()

"""
Explanation: <a id='Completing_the_square'></a>
Completing the square
to rewrite $ax^2 + bx + c = 0$
to $(x + a)^2 + b$
eg. $x^2 +2x+3 = 0$
$(x + 1)^2 + 2 = 0$
Example:
$4x^2 + 20x + 25 = 0$
$x^2 + 5x + \frac{25}{4} = 0$
$(x + \frac{5}{2})^2 - \frac{25}{4} + \frac{25}{4} = 0$
$(x + \frac{5}{2})^2 = 0$
$x + \frac{5}{2} = 0$
$x = - \frac{5}{2}$
note: the discriminant = 0 $\therefore$ only one repeated solution for x
<a id='Simultaneous_Equations'></a>
Simultaneous Equations
<a id='Elimination'></a>
Elimination
Equations:
a) $3x + 2y = 280$
b) $x + 4y = 260$
Solve x and y of above equations:
2a) $6x + 4y = 560$
2a - b) $5x = 300$
$x = 60$
substitute x's value into original equation
$60 + 4y = 260$
$4y = 200$
$y = 50$
<a id='Substitution'></a>
Substitution
Equations:
a)$y - 3x + 2 = 0$
b)$y^2 - x - 6x^2 = 0$
Solve x and y of above equations:
make unknown variable the subject in a equation (a in my case)
a) $y = 3x - 2$
substitute unknown variable in terms of other unknown into the other equation
b) $(3x - 2)^2 - x - 6x^2 = 0$
$(9x^2 - 12x + 4) - x - 6x^2 = 0$
$3x^2 - 13x + 4 = 0$
$(3x - 1)(x - 4) = 0$
$3x - 1 = 0$
$x = \frac{1}{3}$
or
$x - 4 = 0$
$x = 4$
substitute into original equations
$y = 3(4) - 2$
$y = 10$
or
$y = 3(\frac{1}{3}) - 2$
$y = -1$
Linear and Quadratic example
Equations:
a) $2x + y = 8$
b) $3x^2 + xy = 1$
In form a + b√17, where a and b are integers.
$a\times x$) $2x^2 + xy = 8x$
$b - a\times x$) $x^2 = 1 - 8x$
$x^2 + 8x - 1 = 0$
$\frac{-8 \pm \sqrt{8^2 - 4(-1)}}{2}$
$-4 \pm \sqrt{68}$
$-4 \pm 2\sqrt{17}$
$x = -4 + 2\sqrt{17}$ or $-4 - 2\sqrt{17}$
$y = 8 - 2(-4 \pm 2\sqrt{17})$
$y = 16 - 4\sqrt{17}$ or $16 + 4\sqrt{17}$
<a id='Linear_and_Quadratic_Inequalitites'></a>
Linear and Quadratic Inequalitites
<a id='Rules'></a>
Rules:

Treat like an equation
When multiplying by negative, switch the sign polarity
'or' means either/both statement(s) can be met
'and' means both statements must be met
compound inequalities should be treated like an equation also (can also be split)
there can be no solution

example:
$(2x + 1)(x - 2) > 2(x + 5)$
$2x^2 - 3x - 2 > 2x + 10$
$2x^2 - 5x - 12 > 0$
$(2x + 3)(x - 4) > 0$
since $p \times p = p$ and $n \times n = p$:
either $2x + 3 > 0$ and $x - 4 > 0$
or $2x + 3 < 0$ and $x - 4 < 0$
$2x + 3 > 0$
$x > -\frac{3}{2}$
and $x - 4 > 0$
$x > 4$
which is equivalent to $x > 4$
$2x + 3 < 0$
$x < -\frac{3}{2}$
and $x - 4 < 0$
$x < 4$
which is equivalent to $x < -\frac{3}{2}$
$\therefore$ $x < -\frac{3}{2}$ and $x > 4$
<a id='Inequality_graphing'></a>
End of explanation
"""


# sample x values
x = np.linspace(-10, 10, 2001).astype(np.float32)

# equation
def poly_eq(x, a, n):
    return(a*x**n)

# setup figure and axes
fig, ax = plt.subplots(1, 4, sharex=True, figsize=(16,5))

# define variables for each graph using examples from above
graph_vars = [[1, 2], [1, 3], [-1, 2], [-1, 3]]

# loop for 4 graphs
for a in range(4):
    
    # put the current variables into the equation
    y = poly_eq(x, graph_vars[a][0], graph_vars[a][1])

    # list ot store x values where y = 0 (aka the x intercepts)
    x_intercepts = []

    # loop through y values find x intercepts (out of the sample values)
    for i in range(y.size):
        if y[i] < 0.01 and y[i] > -0.01:
            x_intercepts.append(i)

    # plot the current data and x intercept
    ax[a].plot(x,y)
    for x_intercept in x_intercepts:
        ax[a].plot(x[x_intercept], y[x_intercept], 'om')

    ax[a].grid(True)

plt.show()

"""
Explanation: <a id='Polynomials'></a>
Polynomials
<a id='Expanding_brackets'></a>
Expanding brackets
Single brackets:
$a(b + c) = ab + ac$
$3xy(2x + y^2) = 6x^2y + 3xy^3$
Double brackets:
$(a + b)(a + d) = a^2 + (b + d)a + bd$
$(3x - 10)(5x - 9) = 15x^2 - 27x - 50x + 90$
$= 15x^2 - 77x + 90$
note: $(a + b)(a - b) = a^2 - b^2$ is the difference of 2 squares
<a id='Factorising'></a>
Factorising
Single brackets:
$ab + ac = a(b + c)$
$9xy^2 - 6x^4z = 3x(3y^2 - 2x^3z)$
Double brackets:
$x^2 + bx + c = (x + p)(x + q)$

$p /times q = c$
$p + q = b$

$x^2 - x - 12 = (x - 4)(x + 3)$
With non 1 co-efficient:
$ax^2 + bx + c = (rx + p)(sx + q)$

find factor pair of $ac$
which sum $= b$

$4x^2 + 4x - 15$

$ac = -60$
$b = 4$

factor pair = 10 and -6
then substitute into equation instead of bx
$4x^2 + 10x -6x - 15$
then group:
$= 2x(2x + 5) -3(2x + 5)$
$= (2x - 3)(2x + 5)$
<a id='Division'></a>
Division
Use long division
$\frac{3x^3 - 2x^2 + 7x - 4}{x^2 + 1}$
$x^2 + 1|3x^3 - 2x^2 + 7x - 4$
divide the highest degree ($x^2$ in our case)
our current output:

$3x$

take away what we have just taken out the divided 
$-(3x^3 + 3x)$
$x^2 + 1|- 2x^2 + 4x - 4$
our current output:

$3x - 2$

$-(-2x^2 - 2)$
$x^2 + 1|4x - 2$
remainder of $4x - 2$
$\frac{3x^3 - 2x^2 + 7x - 4}{x^2 + 1} = 3x - 2 + \frac{4x - 2}{x^2+1}$
<a id='Remainder_Theorem'></a>
Remainder Theorem
To find the remainder of division (or any polynomial):
$f(x) = ax^2 + bx + c$
$\frac{ax^2 + bx + c}{x - d}$
remainder = f(d)
Example:
$f(x) = 3x^2 - 4x + 7$
$\frac{3x^2 - 4x + 7}{x - 1} = (3x - 1) + 6$
$3(1)^2 - 4(1) + 7 = 3 - 4 + 6 = 6$
<a id='Factor_Theorem'></a>
Factor Theorem
Opposite of remainder theorem:
$f(x) = ax^2 + bx + c$
if f(d) = 0:
$\frac{ax^2 + bx + c}{x - d}$
$x - d$ is a factor
Example:
$f(x) = x^3 + 6x^2 + 11x + 6$
$f(-1) = (-1)^3 + 6(-1)^2 + 11(-1) + 6$
$= -1 + 6 + -11 + 6$
$= 0$
$x^3 + 6x^2 + 11x + 6 = (x + 1)(x^2 + 5x + 6)$
$= (x + 1)(x + 2)(x + 3)$
<a id='Graphs'></a>
Graphs
<a id='Plotting_polynomials'></a>
Plotting polynomials

changes of direction are called turning points
polynomial of n'th degree has $\le n -1$ turning points
our 0's are the x intercepts

for function:
$f(x) = ax^n ...$
Leading Coefficient:

if a > 0 and n is even: will increase without bound positively at both endpoints (eg. $x^2$)
if a > 0 and n is odd: will increase without bound positively at the right end and decrease without bound at the left end (eg. $x^3$)
if a < 0 and n is even: will decrease without bound negatively at both endpoints (eg. $-x^2$)
if a < 0 and n is odd: will decrease without bound negatively at the right end and increase without bound at the left end (eg. $-x^3$)

Then:
- plot x intercepts
- plot y intercept, (0,f(0))
- plot more points to give sketch of graph
- wont work if any non "real" numbers involved
End of explanation
"""


x = np.linspace(-10, 10, 2001).astype(np.float32)

M = 2
c = 6

y = M*x + c

fig, ax = plt.subplots()

plt.plot(x, np.absolute(y))

ax.grid(True)

plt.show()

"""
Explanation: <a id='Modulus_of_linear_function'></a>
Modulus of linear function
The Modulus is the absolute value or the magnitude of the number
It has no polarity eg. |-3| = 3
End of explanation
"""


x = np.linspace(0, 10, 1001).astype(np.float32)

a = 2
b = np.e
c = 6

y = a*(np.log(x)/np.log(b)) + c

fig, ax = plt.subplots()

plt.plot(x, y)

ax.grid(True)

plt.show()

"""
Explanation: <a id='Graphing_log'></a>
Graphing log
Logarithm: the power which the base must be raised to, to result a specified value
note: ln = natural log ($log_e()$ or $ln()$)
End of explanation
"""


# x data
x = np.linspace(-1, 1, 2001).astype(np.float32)

# variables

a = 1        # coefficient
n = -1       # power (representing all odd values)
n2 = -2      # power (representing all even values)

# y values for first power
y = a*x**n
# y values for second power
y2 = a*x**n2

# set up plot
fig, ax = plt.subplots(1, 2, sharex=True, figsize=(16,5))

ax[0].plot(x, y)
ax[1].plot(x, y2)

ax[0].set_title("$ax^{-1}$")
ax[1].set_title("$ax^{-2}$")

for axes in ax:
    axes.grid(True)

plt.show()

"""
Explanation: <a id='Plotting_reciprocal_of_x'></a>
Plotting reciprocal of x
Reciprocal = $x^{-1}$
$ax^{-n} = \frac{a}{x^n}$
End of explanation
"""


# x data
x = np.linspace(-1, 1, 2001).astype(np.float32)

# variables

a = 1        # coefficient
n = -1       # power (representing all odd values)
n2 = -2      # power (representing all even values)

# y values for first power
y = a*x**n
# y values for second power
y2 = a*x**n2

# set up plot
fig, ax = plt.subplots(1, 2, sharex=True, figsize=(16,5))

ax[0].plot(x, y, color='b', label='$ax^{-1}$')
ax[1].plot(x, y2, color='b', label='$ax^{-2}$')

ax[0].axvline(x=0, color='r', linestyle='--', label='vertical asymptote')
ax[1].axvline(x=0, color='r', linestyle='--', label='vertical asymptote')

ax[0].axhline(y=0, color='g', linestyle='--', label='horizontal asymptote')
ax[1].axhline(y=0, color='g', linestyle='--', label='horizontal asymptote')

ax[0].set_title("$ax^{-1}$")
ax[1].set_title("$ax^{-2}$")

ax[0].set_xlim([-1,1])
ax[0].set_ylim([-100,100])

ax[1].set_xlim([-0.5,0.5])
ax[1].set_ylim([-100,1000])

for axes in ax:
    axes.legend(loc=1)
    axes.grid(True)

plt.show()

"""
Explanation: Horizontal and Vertical Asymptotes
The linear line in the direction to which a curve approaches as it heads towards infinity
End of explanation
"""


# x data
x = np.linspace(-4, 4, 2001).astype(np.float32)

def f(x):
    return x**2

def g(x):
    return 2*x - 1

def f2(x):
    return (x**2)/3 - 1

def f2i(x):
    return np.sqrt(3*(x+1))

fy = f(x)

gy = g(x)

gf = g(f(x))

ff = f(f(x))

f2y = f2(x)

f2iy = f2i(x)

domains = 0
domainf = 3

# set up plot
fig, ax = plt.subplots(3, 1, figsize=(8,18))

ax[0].plot(x, fy, color='b', linestyle='--', label='$f(x) = x^2$')
ax[0].plot(x, gy, color='g', linestyle='--', label='$g(x) = 2x - 1$')
ax[0].plot(x, gf, color='r', linestyle='--', label='$g(f(x)) = 2(x^2) - 1$')
ax[0].plot(x, fg, color='pink', linestyle='--', label='$f(f(x)) = (x^2)^2$')

ax[1].plot(x, f2y, color='b', linestyle='--', label='$f(x) = (x^2)/3 - 1$')
ax[1].plot(x, f2iy, color='g', linestyle='--', label='$g(x) = \sqrt{3(y+1)}$')

x2 = np.linspace(domains, domainf, 1001).astype(np.float32)

ax[2].plot(x, f2y, color='b', linestyle='--', label='$f(x) = (x^2)/3 - 1$')
ax[2].plot(x2, f2i(x2), color='g', linestyle='--', label='$g(x) = \sqrt{3(y+1)}$')

ax[2].plot(x, x, '--', label='line of symmetry')

ax[2].axvline(domains, color='red', lw=2, alpha=0.5, label='domain')
ax[2].axvline(domainf, color='red', lw=2, alpha=0.5)

ax[2].axhline(np.min(f2(domains)).reshape(1), color='green', lw=2, alpha=0.5, label='range')
ax[2].axhline(np.max(f2(domainf)).reshape(1), color='green', lw=2, alpha=0.5)

ax[0].set_title("Composite functions")
ax[1].set_title("Inverse functions")
ax[2].set_title("Inverse functions")

for axes in ax:
    axes.legend(loc=9)
    axes.grid(True)

plt.show()

"""
Explanation: <a id='Functions'></a>
Functions
<a id='Composite_functions'></a>
Composite functions
multiple functions represented as 1 combined function
$hgf(x) = h(g(f(x)))$
$f^2(x) = f(f(x))$
Example:
$g(x) = 4x - 7$
$f(x) = 2x^2 + 3x$
$gf(x) = 4(2x^2 + 3x) - 7$
$gf(3) = 4(18 + 9) - 7$
$= 101$
<a id='Inverse_functions'></a>
Inverse functions
Reverse of a function, maps output to input (compared to normal function)
$f(x) = \frac{1}{\frac{x}{4} + 2}$
$f^{-1}(y) = 4(\frac{1}{y} - 2)$

can solve algebraically  (eg. $y = kx \to \frac{y}{k} = x$)
with $x^2$, can only work with + vals
possibly wont work if you have 0 values

Example:
$f(x) = \frac{4^{x^2}}{5} + 3$
$f^-1(y) = \sqrt{log_4(5(y - 3)}$
$f(3) = 52431.8$
$f^{-1}(52431.8) = 3$
note: doesn't work with certain values because log or root or fractions with 0 denominators wont work
To combat this we add a domain
This is the range of x values as inputs we're using
The range is the range of the output (y) values
When graphed, the line of symmetry between the graphs is y = x
End of explanation
"""


# x data
x = np.linspace(-2, 2, 2001).astype(np.float32)

def f(x, a, b, c, d):
    return c*(a*x+b)**2+d

y = f(x, 1, 0, 1, 0)

y1 = f(x, 1, 0, 1, 1)
y2 = f(x, 1, 1, 1, 0)
y3 = f(x, 1, 0, 2, 0)
y4 = f(x, 2, 0, 1, 0)

y5 = f(x, 2, 1, 2, 1)
y6 = f(x, 2, 1, -1, 0)

# set up plot
fig, ax = plt.subplots(3, 2, figsize=(12,12), sharex=True)

ax[0, 0].plot(x, y, color='b', label='$f(x)$')
ax[0, 0].plot(x, y1, color='g', label='$f(x) + a$')


ax[0, 1].plot(x, y, color='b', label='$f(x)$')
ax[0, 1].plot(x, y2, color='g', label='$f(x + a)$')

ax[1, 0].plot(x, y, color='b', label='$f(x)$')
ax[1, 0].plot(x, y3, color='g', label='$af(x)$')

ax[1, 1].plot(x, y, color='b', label='$f(x)$')
ax[1, 1].plot(x, y4, color='g', label='$f(ax)$')

ax[2, 0].plot(x, y, color='b', label='$f(x)$')
ax[2, 0].plot(x, y5, color='g', label='test')

ax[2, 1].plot(x, y, color='b', label='$f(x)$')
ax[2, 1].plot(x, y6, color='g', label='test')

for i in ax:
    for axes in i:
        axes.legend(loc=9)
        axes.grid(True)

plt.show()

"""
Explanation: <a id='Transformation_of_functions'></a>
Transformation of functions
$f(x) + a$:
- moves in y dimension
- by value $a$
$f(x + a)$:
- moves in x dimension
- by value $a$
$af(x)$:
- stretches or compresses in y dimension
- multiplies all y values by $a$
- therefore, gradient also increases by $a$
$f(ax)$:
- stretches or compresses in x dimension
- multiplies all x values as input by $a$
- therefore multiplies all y values by $f(a)$
- therefore multiplies gradient by $f(a)$
Combinations just add or multiply to the function the same way
End of explanation
"""


