from abelian import LCA, HomLCA
from IPython.display import display, Math

def show(arg):
    return display(Math(arg.to_latex()))

"""
Explanation: Tutorial: Factoring homomorphisms
This is an interactive tutorial written with real code.
We start by importing the LCA class, the HomLCA class and setting up $\LaTeX$ printing.
End of explanation
"""


phi = HomLCA([[5, 10, 15], 
              [10, 20, 30], 
              [10, 5, 30]], 
              target = [50, 20, 30])
show(phi)

"""
Explanation: Initialization and source/target projections
We create a HomLCA instance, which may represent a homomorphism between FGAs. In this tutorial we will only consider homomorphisms between FGAs.
End of explanation
"""


# Project to source, i.e. orders of generator columns
phi = phi.project_to_source()
show(phi)

"""
Explanation: Projecting to source
The source (or domain) is assumed to be free (infinite order). Calculating the orders is done with the project_to_source method, after which the orders of the columns are shown in the source group.
End of explanation
"""


# Project the generator columns to the target group
phi = phi.project_to_target()
show(phi)

"""
Explanation: Projecting to target
Projecting the columns onto the target group will make the morphism more readable. The project_to_target() method will project every column to the target group.
End of explanation
"""


# Calculate the kernel
show(phi.kernel())

"""
Explanation: The kernel monomorphism
The kernel morphism is a monomorphism such that $\phi \circ \operatorname{ker} (\phi) = 0$. The kernel of $\phi$ is:
End of explanation
"""


show(phi.kernel().project_to_source())

"""
Explanation: The kernel monomorphism is not projected to source by default, but doing so is simple.
End of explanation
"""


show(phi * phi.kernel())

"""
Explanation: Verify that $\phi \circ \operatorname{ker} (\phi) = 0$.
End of explanation
"""


zero = phi * phi.kernel()
zero = zero.project_to_target()
show(zero)

"""
Explanation: To clearly see that this is the zero morphism, use the project_to_target() method as such.
End of explanation
"""


show(phi.cokernel())

"""
Explanation: The cokernel epimorphism
The kernel morphism is an epimorphism such that $\operatorname{coker}(\phi) \circ \phi = 0$. The cokernel of $\phi$ is:
End of explanation
"""


show((phi.cokernel() * phi))

"""
Explanation: We verify the factorization.
End of explanation
"""


zero = phi.cokernel() * phi
zero = zero.project_to_target()

show(zero)

"""
Explanation: Again it is not immediately clear that this is the zero morphism. To verify this, we again use the project_to_target() method as such.
End of explanation
"""


im = phi.image()
show(im)

"""
Explanation: The image/coimage factorization
The image/coimage factorization is $\phi = \operatorname{im}(\phi) \circ \operatorname{coim}(\phi)$, where the image is a monomorphism and the coimage is an epimorphism.
The image monomorphism
Finding the image is easy, just call the image() method.
End of explanation
"""


im = im.remove_trivial_groups()
show(im)

"""
Explanation: A trivial group $\mathbb{Z}_1$ is in the source. It can be removed using remove_trivial_subgroups().
End of explanation
"""


coim = phi.coimage().remove_trivial_groups()
show(coim)

"""
Explanation: The coimage epimorphism
Finding the coimage is done by calling the coimage() method.
End of explanation
"""


show(phi)
show((im * coim).project_to_target())

(im * coim).project_to_target() == phi

"""
Explanation: Verify the image/coimage factorization
We now verify that $\phi = \operatorname{im}(\phi) \circ \operatorname{coim}(\phi)$.
End of explanation
"""


