import numpy as np
import matplotlib.pyplot as plt
from tabulate import tabulate

# Computation of addition and multiplication tables for given basis

# Parameter

B = 10 # Basis

# Function

class Table:
    """Class for creation of addition and multiplication table"""

    def char():
        """List of characters used to represent values in a given base"""
        return list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")

    def Addition(b = B , save = False):
        """Creation of the addition table.
        Inputs:
        - b: Int - Value of the basis. Default: B
        - save: Boolean - Saves the figure or not. Default: False"""
        L = Table.char()
        T = np.zeros((b+2,b+2) , dtype='<U2')
        T[0,0] = "+"

        for k in range(b):
            T[0,k+1] , T[k+1,0] = L[k] , L[k]
        T[0,b+1] , T[b+1,0] = "10" , "10"


        for i in range(b+1):
            for j in range(b+1):
                r = (i+j)%b
                s = L[r]
                r = (i+j)//b
                if r > 0:
                    s = L[r]+s
                T[i+1,j+1] = s

        print(tabulate(T[1:,:], headers=T[0,:] , tablefmt="fancy_grid"))

        plt.figure()
        for i in range(b+2):
            plt.plot([i / 10, i / 10], [0, -(b + 2) / 10], color="black" , linewidth = 1)
            plt.plot([(i + 1) / 10, (i + 1) / 10], [0, -(b + 2) / 10], color="black" , linewidth = 1)
            for j in range(b+2):
                plt.plot([0, (b + 2) / 10], [-j / 10, -j / 10], color="black" , linewidth = 1)
                plt.plot([0, (b + 2) / 10], [-(j + 1) / 10, -(j + 1) / 10], color="black" , linewidth = 1)
                plt.text((i+0.25)/10,-(j+0.6)/10,T[i,j],fontsize=10.0*np.sqrt(10/b))
        plt.plot([0, 0], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([1 / 10, 1 / 10], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([(b+2) / 10, (b+2) / 10], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([0, (b + 2) / 10], [0, 0], color="black", linewidth=2.5)
        plt.plot([0, (b + 2) / 10], [-1 / 10, -1 / 10], color="black", linewidth=2.5)
        plt.plot([0, (b + 2) / 10], [-(b+2) / 10, -(b+2) / 10], color="black", linewidth=2.5)
        plt.xlim([-0.1,(b+2)/10+0.1])
        plt.ylim([-(b+2)/10-0.1,0.1])
        plt.axis("off")
        plt.title("Addition table with basis "+str(b))
        if save == True:
            plt.savefig("Addition_Table_Basis_"+str(b)+".pdf")
        plt.show()
        pass

    def Multiplication(b = B , save = False):
        """Creation of the multiplication table.
        Inputs:
        - b: Int - Value of the basis. Default: B
        - save: Boolean - Saves the figure or not. Default: False"""
        L = Table.char()
        T = np.zeros((b+2,b+2) , dtype='<U3')
        T[0,0] = "x"

        for k in range(b):
            T[0,k+1] , T[k+1,0] = L[k] , L[k]
        T[0,b+1] , T[b+1,0] = "10" , "10"


        for i in range(b+1):
            for j in range(b+1):
                r = (i*j)%b
                s = L[r]
                r = (i*j)//b
                if r > 0:
                    s = L[r]+s
                T[i+1,j+1] = s
        T[b+1, b+1] = "100"

        print(tabulate(T[1:,:], headers=T[0,:] , tablefmt="fancy_grid"))

        plt.figure()
        for i in range(b+2):
            plt.plot([i / 10, i / 10], [0, -(b + 2) / 10], color="black" , linewidth = 1)
            plt.plot([(i + 1) / 10, (i + 1) / 10], [0, -(b + 2) / 10], color="black" , linewidth = 1)
            for j in range(b+2):
                plt.plot([0, (b + 2) / 10], [-j / 10, -j / 10], color="black" , linewidth = 1)
                plt.plot([0, (b + 2) / 10], [-(j + 1) / 10, -(j + 1) / 10], color="black" , linewidth = 1)
                if i==b+1 and j==b+1:
                    plt.text((i+0.25)/10-0.2/10,-(j+0.6)/10,T[i,j],fontsize=10.0*np.sqrt(10/b))
                else:
                    plt.text((i+0.25)/10,-(j+0.6)/10,T[i,j],fontsize=10.0*np.sqrt(10/b))
        plt.plot([0, 0], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([1 / 10, 1 / 10], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([(b+2) / 10, (b+2) / 10], [0, -(b + 2) / 10], color="black" , linewidth = 2.5)
        plt.plot([0, (b + 2) / 10], [0, 0], color="black", linewidth=2.5)
        plt.plot([0, (b + 2) / 10], [-1 / 10, -1 / 10], color="black", linewidth=2.5)
        plt.plot([0, (b + 2) / 10], [-(b+2) / 10, -(b+2) / 10], color="black", linewidth=2.5)
        plt.xlim([-0.1,(b+2)/10+0.1])
        plt.ylim([-(b+2)/10-0.1,0.1])
        plt.axis("off")
        plt.title("Multiplication table with basis "+str(b))
        if save == True:
            plt.savefig("Multiplication_Table_Basis_"+str(b)+".pdf")
        plt.show()
        pass
