# -*- coding: utf-8 -*-
r"""
KnotInfo Database

This module contains the class :class:`KnotInfoDataBase`  and auxilary classes
for it which serves as an interface to the lists of named knots and links provided
at the web-pages `KnotInfo <https://knotinfo.math.indiana.edu/>`__ and
`LinkInfo <https://linkinfo.sitehost.iu.edu>`__.


AUTHORS:

- Sebastian Oehms August 2020: initial version
"""


##############################################################################
#       Copyright (C) 2020 Sebastian Oehms <seb.oehms@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#                  http://www.gnu.org/licenses/
##############################################################################


import os
from enum import Enum

from sage.structure.sage_object import SageObject
from sage.structure.unique_representation import UniqueRepresentation
from sage.misc.persist import save, load
from sage.misc.verbose import verbose
from sage.misc.cachefunc import cached_method


class KnotInfoColumnTypes(Enum):
    r"""
    Enum class to specify if a column from the table of knots and links provided
    at the web-pages `KnotInfo <https://knotinfo.math.indiana.edu/>`__ and
    `LinkInfo <https://linkinfo.sitehost.iu.edu>`__.  is used for knots only,
    links only or both.

    EXAMPLES::

        sage: from sage.databases.knotinfo_db import KnotInfoColumnTypes
        sage: [col_type for col_type in KnotInfoColumnTypes]
        [<KnotInfoColumnTypes.OnlyKnots: 'K'>,
        <KnotInfoColumnTypes.OnlyLinks: 'L'>,
        <KnotInfoColumnTypes.KnotsAndLinks: 'B'>]
    """

    OnlyKnots =     'K'       # column that is only used in the KnotInfo table
    OnlyLinks =     'L'       # column that is only used in the LinkInfo table
    KnotsAndLinks = 'B'       # column that is only used in both tables


class KnotInfoColumns(Enum):
    r"""
    Enum class to select a column from the table of knots and links provided
    at the web-pages `KnotInfo <https://knotinfo.math.indiana.edu/>`__ and
    `LinkInfo <https://linkinfo.sitehost.iu.edu>`__.

    EXAMPLES::

        sage: from sage.databases.knotinfo_db import KnotInfoDataBase
        sage: ki_db = KnotInfoDataBase()
        sage: cols = ki_db.columns(); cols
        <enum 'Columns'>
        sage: from sage.databases.knotinfo_db import KnotInfoColumns
        sage: isinstance(cols.name, KnotInfoColumns)
        True

        sage: def only_links(c):
        ....:     return c.column_type() == c.types.OnlyLinks
        sage: [c.column_name() for c in cols if only_links(c)]  # optional - database_knotinfo
        ['Name - Unoriented',
         'Orientation',
         'Unoriented Rank',
         'PD Notation (vector)',
         'PD Notation (KnotTheory)',
         'Multivariable Alexander Polynomial',
         'HOMFLYPT Polynomial',
         'Unoriented',
         'Arc Notation',
         'Linking Matrix',
         'Rolfsen Name',
         'Components',
         'DT code',
         'Splitting Number',
         'Nullity',
         'Unlinking Number',
         'Weak Splitting Number']
    """
    @property
    def types(self):
        r"""
        Return :class:`KnotInfoColumnTypes` to be used for checks.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: cols = ki_db.columns()
            sage: cols.dt_code.column_type() == cols.dt_code.types.OnlyLinks
            True
        """
        return KnotInfoColumnTypes

    def column_name(self):
        r"""
        Return the name of ``self`` displayed on the KnotInfo web-page.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: cols = ki_db.columns()
            sage: cols.dt_code.column_name()
            'DT code'
        """
        return self.value[0]

    def column_type(self):
        r"""
        Return the type of ``self``. That is an instance of ``Enum``
        :class:`KnotInfoColumnTypes`.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: cols = ki_db.columns()
            sage: cols.homfly_polynomial.column_type()
            <KnotInfoColumnTypes.OnlyKnots: 'K'>
            sage: cols.homflypt_polynomial.column_type()
            <KnotInfoColumnTypes.OnlyLinks: 'L'>
            sage: cols.name.column_type()
            <KnotInfoColumnTypes.KnotsAndLinks: 'B'>
        """
        return self.value[1]

    def description_webpage(self, new=0, autoraise=True):
        r"""
        Launch the description page of ``self`` in the standard web browser.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: cols = ki_db.columns()
            sage: cols.pd_notation.description_webpage()            # not tested
            True
            sage: cols.homflypt_polynomial.description_webpage()    # not tested
            True
        """
        import webbrowser
        if self.column_type() == self.types.OnlyLinks:
             url = KnotInfoFilename.links.description_url(self)
        else:
             url = KnotInfoFilename.knots.description_url(self)
        return webbrowser.open(url, new=new, autoraise=autoraise)




class KnotInfoFilename(Enum):
    r"""
    Enum for the different data files. The following choices are possible:

    - ``knots`` -- contains the the data from KnotInfo
    - ``links`` -- contains the the data for proper links from LinkInfo

    Examples::

        sage: from sage.databases.knotinfo_db import KnotInfoDataBase
        sage: ki_db = KnotInfoDataBase()
        sage: ki_db.filename
        <enum 'KnotInfoFilename'>
    """

    def url(self):
        r"""
        Return the URL to download the data from the web-page.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.url()
            'https://knotinfo.math.indiana.edu/'
        """
        if self == KnotInfoFilename.knots:
            return self.value[0]
        else:
            return self.value[0]

    def excel(self):
        r"""
        Return the Excel-file name to download the data from the web-page.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.excel()
            'knotinfo_data_complete.xls'
        """
        if self == KnotInfoFilename.knots:
            return '%s.xls' %(self.value[1])
        else:
            return '%s.xlsx' %(self.value[1])

    def csv(self):
        r"""
        Return the file name under which the data from the web-page
        are stored as csv file.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.csv()
            'knotinfo_data_complete.csv'
        """
        return '%s.csv' %(self.value[1])

    def num_knots(self):
        r"""
        Return the file name under which the number of knots is stored
        in an sobj-file.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.num_knots()
            'num_knots.sobj'
        """
        return 'num_knots.sobj'

    def sobj_row(self):
        r"""
        Return the file name under which the row-data of the csv-File
        is stored as python dictionary in a sobj-file.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.sobj_row()
            'row_dict.sobj'
        """
        return 'row_dict.sobj'

    def sobj_column(self):
        r"""
        Return the file name under which the column-data of the csv-File
        is stored as python dictionary in a sobj-file.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.sobj_column()
            'column_dict.sobj'
        """
        return 'column_dict.sobj'


    def sobj_data(self, column):
        r"""
        Return the file name under which the data of the given
        column is stored as python list in a sobj-file.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.sobj_data(ki_db.columns().braid_notation)
            'knotinfo_braid_notation'
        """
        if column.column_type() == column.types.OnlyLinks:
            return 'linkinfo_%s' %(column.name)
        else:
            return 'knotinfo_%s' %(column.name)

    def description_url(self, column):
        r"""
        Return the url of the description page of the given column.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.description_url(ki_db.columns().braid_notation)
            'https://knotinfo.math.indiana.edu/descriptions/braid_notation.html'
        """
        return '%sdescriptions/%s.html' %(self.url(), column.name)

    def diagram_url(self, fname, single=False):
        r"""
        Return the url of the diagram page of the given link.

        Examples::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.knots.diagram_url('3_1-50.png')
            'https://knotinfo.math.indiana.edu/diagram_display.php?3_1-50.png'
            sage: ki_db.filename.knots.diagram_url('3_1', single=True)
            'https://knotinfo.math.indiana.edu/diagrams/3_1'
        """
        if single:
            return '%sdiagrams/%s' %(self.url(), fname)
        else:
            return '%sdiagram_display.php?%s' %(self.url(), fname)


    knots = ['https://knotinfo.math.indiana.edu/', 'knotinfo_data_complete']
    links = ['https://linkinfo.sitehost.iu.edu/',  'linkinfo_data_complete']




#----------------------------------------------------------------------------------------------------------------------------
# Class to provide data for knots and links from the KnotInfo web-page
#----------------------------------------------------------------------------------------------------------------------------
class KnotInfoDataBase(SageObject, UniqueRepresentation):
    r"""
    Database interface to KnotInfo

    The original data are obtained from KnotInfo web-page (URL see the example
    below). In order to have these data installed during the build process as
    a sage-package they are converted as csv files into a tarball. This tarball
    has been created using the method :meth:`create_spkg_tarball`.

    EXAMPLES::

        sage: from sage.databases.knotinfo_db import KnotInfoDataBase
        sage: ki_db = KnotInfoDataBase()
        sage: ki_db.filename.knots
        <KnotInfoFilename.knots: ['https://knotinfo.math.indiana.edu/',
                                  'knotinfo_data_complete']>
    """

    filename = KnotInfoFilename

    def __init__(self, install=False):
        r"""
        Python constructor.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.filename.links
            <KnotInfoFilename.links: ['https://linkinfo.sitehost.iu.edu/',
                                      'linkinfo_data_complete']>
        """
        # some constants
        self._names_column      = 'name'
        self._components_column = 'components'
        self._knot_prefix       = 'K'

        self._knot_list = None
        self._link_list = None
        self._demo      = None
        self._num_knots = None

        from sage.features.databases import DatabaseKnotInfo
        from sage.env import DOT_SAGE
        self._feature   = DatabaseKnotInfo()
        self._sobj_path = os.path.join(DOT_SAGE, 'knotinfo')

    def reset_filecache(self):
        r"""
        Reset the internal files containing the database.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.reset_filecache()    # optional - database_knotinfo
        """
        if not self._feature.is_present():
            return
        sobj_path = self._sobj_path
        os.system('rm -rf %s' %sobj_path)
        from sage.misc.misc import sage_makedirs
        sage_makedirs(sobj_path)

        num_knots_file = os.path.join(sobj_path, self.filename.knots.num_knots())
        knot_list = self.knot_list()
        num_knots = len(knot_list) - 1
        save(num_knots, num_knots_file)
        self._num_knots = num_knots
        self._create_col_dict_sobj()
        self._create_data_sobj()
        return


    def demo_version(self):
        r"""
        Return whether the KnotInfo databases are installed completely or
        just the demo version is used.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.demo_version()       # optional - database_knotinfo
            False
        """
        if self._demo is None:
            if self._feature.is_present():
                num_knots_file = os.path.join(self._sobj_path, self.filename.knots.num_knots())
                from builtins import FileNotFoundError
                try:
                    self._num_knots =  load(num_knots_file)
                except FileNotFoundError:
                    self.reset_filecache()
                self._demo = False
            else:
                self._demo = True
                self._num_knots = len([v for v in row_demo_sample.values() if v[1]==1])
        return self._demo

    def knot_list(self):
        r"""
        Return the KnotInfo table rows as Python list.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: len(ki_db.knot_list())  # not tested (just used on installation)
        """
        if self._knot_list:
            return self._knot_list

        from database_knotinfo import link_list
        self._knot_list = link_list()
        return self._knot_list


    def link_list(self):
        r"""
        Return the LinkInfo table rows as Python list.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: len(ki_db.link_list())  # not tested (just used on installation)
        """
        if self._link_list:
            return self._link_list

        from database_knotinfo import link_list
        self._link_list = link_list(proper_links=True)
        return self._link_list

    def _create_col_dict_sobj(self):
        r"""
        Create ``sobj`` files containing the number of knots and a dictionary
        for the columns of the table.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db._create_col_dict_sobj() # not tested (used on installation)
        """
        knot_list = self.knot_list()
        knot_column_names = knot_list[0]

        link_list = self.link_list()
        link_column_names = link_list[0]

        from sage.misc.misc import sage_makedirs
        sage_makedirs(self._sobj_path)

        column_dict = {}

        # ----------------------------------------------------------------
        # Columns that exist for knots and links
        # ----------------------------------------------------------------
        for col in knot_column_names:

            name = knot_column_names[col]
            if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']:
                # not of interest
                continue

            col_type = KnotInfoColumnTypes.OnlyKnots
            if col in link_column_names:
                col_type = KnotInfoColumnTypes.KnotsAndLinks
            column_dict[col] = [name, col_type]

        # ----------------------------------------------------------------
        # Columns that exist for links only
        # ----------------------------------------------------------------
        for col in link_column_names:

            name = link_column_names[col]
            if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']:
                # not of interest
                continue

            if col in knot_column_names:
                # already used
                continue

            col_type = KnotInfoColumnTypes.OnlyLinks
            column_dict[col] = [name, col_type]

        save(column_dict, '%s/%s' %(self._sobj_path, self.filename.knots.sobj_column()))



    def _create_data_sobj(self):
        r"""
        Create ``sobj`` files containing the contents of the whole table.
        To each column there is created one file containing a list of
        strings giving the entries of the database table.

        The length of these lists depends on the type of the corresponding
        column. If a column is used in both tables
        (``KnotInfoColumnTypes.KnotsAndLinks``) the list of proper links
        is appended to the list of knots.  In both other cases the lenght
        of the list corresponds to the number of listed knots and proper
        links respectively.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db._create_data_sobj() # not tested (just used on installation)
        """
        knot_list = self.knot_list()
        link_list = self.link_list()
        len_knots = len(knot_list)
        len_links = len(link_list)

        row_dict = {}

        # ----------------------------------------------------------------
        # Columns that exist for knots and links
        # ----------------------------------------------------------------
        for col in self.columns():
            val_list = []

            if  col.column_type() != col.types.OnlyLinks:
                for i in range(1 , len_knots):
                    if col.name == self._names_column:
                        row_dict[self._knot_prefix + knot_list[i][col.name]] = [i - 1 , 1]

                    val_list.append(knot_list[i][col.name])

            if  col.column_type() != col.types.OnlyKnots:
                for i in range(1 , len_links):
                    if col.name == self._names_column:
                        link_name = link_list[i][col.name]
                        link_name = link_name.replace('{', '_')
                        link_name = link_name.replace(',', '_')
                        link_name = link_name.replace('}', '')
 
                        num_comp = int(link_list[i][self._components_column])
                        row_dict[link_name] = [i + len_knots - 2 , num_comp]

                    val_list.append(link_list[i][col.name])

            if val_list:
                save(val_list, '%s/%s' %(self._sobj_path, self.filename.knots.sobj_data(col)))

        save(row_dict,    '%s/%s' %(self._sobj_path, self.filename.knots.sobj_row()))


    @cached_method
    def columns(self):
        r"""
        Return the columns ot the databese table as instances of enum class
        :class:`KnotInfoColumns`.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: cols = ki_db.columns()
            sage: [col.column_name() for col in cols if col.name.startswith('pd')]   # optional - database_knotinfo
            ['PD Notation', 'PD Notation (vector)', 'PD Notation (KnotTheory)']
        """
        column_dict = self.read_column_dict()
        return KnotInfoColumns('Columns', column_dict)


    # -------------------------------------------------------------------------------------------------------------
    # read the dictionary for the column names from sobj-file
    # -------------------------------------------------------------------------------------------------------------
    @cached_method
    def read_column_dict(self):
        r"""
        Read the dictionary for the column names from the according sobj-file

        OUTPUT:

        A python dictionary containing the column names and types

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: len(ki_db.read_column_dict())       # optional - database_knotinfo
            125
        """
        if self.demo_version():
            return column_demo_sample
        sobj_path = self._sobj_path
        filename = self.filename.knots.sobj_column()
        return load('%s/%s' %(sobj_path, filename))

    # -------------------------------------------------------------------------------------------------------------
    # read the dictionary for the row names that is the knot and link names from sobj-file
    # -------------------------------------------------------------------------------------------------------------
    @cached_method
    def read_row_dict(self):
        r"""
        Read the dictionary for the row names that is the knot and link names
        from the according sobj-file

        OUTPUT:

        A python dictionary containing the names of the knots and links
        together with their table index and the corresponding number of
        components

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.read_row_dict()['K7_1']
            [8, 1]
        """
        if self.demo_version():
            return row_demo_sample
        sobj_path = self._sobj_path
        filename = self.filename.knots.sobj_row()
        return load('%s/%s' %(sobj_path, filename))

    # -------------------------------------------------------------------------------------------------------------
    # return a dictionary to obtain the original name to a row_dict key
    # -------------------------------------------------------------------------------------------------------------
    @cached_method
    def row_names(self):
        r"""
        Return a dictionary to obtain the original name to a row_dict key

        OUTPUT:

        A python dictionary containing the names of the knots and links
        together with their original names from the database,

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.row_names()['K7_1']      # optional - database_knotinfo
            '7_1'
        """
        row_dict = self.read_row_dict()
        names = self.read(self.columns().name)
        return {k:names[v[0]] for k, v in row_dict.items()}


    # -------------------------------------------------------------------------------------------------------------
    # read the number of knots contained in the database (without proper links) from the according sobj-file.
    # -------------------------------------------------------------------------------------------------------------
    def read_num_knots(self):
        r"""
        Read the number of knots contained in the database (without
        proper links) from the according sobj-file.

        OUTPUT:

        Integer

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: ki_db.read_num_knots()              # optional - database_knotinfo
            2978
        """
        if not self._num_knots:
            self.demo_version()
        return self._num_knots


    # -------------------------------------------------------------------------------------------------------------
    # read an sobj-file obtained from KnotInfo database
    # -------------------------------------------------------------------------------------------------------------
    @cached_method
    def read(self, column):
        r"""
        Access a column of KnotInfo / LinkInfo

        INPUT:

        ``column`` -- instance of enum :class:`KnotInfoColumns`
          to select the data to be read in

        OUTPUT:

        A python list containing the data corresponding to the column.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
        """
        if not isinstance(column, KnotInfoColumns):
            raise TypeError('column must be an instance of enum %s' %(KnotInfoColumns))

        if self.demo_version():
            return data_demo_sample[column]

        sobj_path = self._sobj_path
        if column.column_type() == column.types.OnlyLinks:
            filename = self.filename.links.sobj_data(column)
        else:
            filename = self.filename.knots.sobj_data(column)

        verbose('loading data library %s ...' %(filename))
        res = load('%s/%s' %(sobj_path, filename))
        verbose('... finished!')

        return res

    def _test_database(self, **options):
        r"""
        Method used by TestSuite. Performs :meth:`KnotInfoBase.is_recoverable`.

        EXAMPLES::

            sage: from sage.databases.knotinfo_db import KnotInfoDataBase
            sage: ki_db = KnotInfoDataBase()
            sage: TestSuite(ki_db).run()    # long time
        """
        from sage.knots.knotinfo import KnotInfo
        from sage.misc.misc import some_tuples
        tester = options['tester']
        max_samples = tester._max_samples
        if not max_samples:
            max_samples = 20
        l = list(KnotInfo)
        sample = some_tuples(l, 1, len(l), max_samples=max_samples)
        tester.assertTrue(all(L.is_recoverable(unique=False) for L, in sample))



column_demo_sample = {
    'name':                 ['Name',                 KnotInfoColumnTypes.KnotsAndLinks],
    'name_unoriented':      ['Name - Unoriented',    KnotInfoColumnTypes.OnlyLinks],
    'dt_notation':          ['DT Notation',          KnotInfoColumnTypes.OnlyKnots],
    'gauss_notation':       ['Gauss Notation',       KnotInfoColumnTypes.KnotsAndLinks],
    'pd_notation':          ['PD Notation',          KnotInfoColumnTypes.OnlyKnots],
    'pd_notation_vector':   ['PD Notation (vector)', KnotInfoColumnTypes.OnlyLinks],
    'crossing_number':      ['Crossing Number',      KnotInfoColumnTypes.KnotsAndLinks],
    'braid_index':          ['Braid Index',          KnotInfoColumnTypes.OnlyKnots],
    'braid_length':         ['Braid Length',         KnotInfoColumnTypes.OnlyKnots],
    'braid_notation':       ['Braid Notation',       KnotInfoColumnTypes.KnotsAndLinks],
    'alternating':          ['Alternating',          KnotInfoColumnTypes.KnotsAndLinks],
    'alexander_polynomial': ['Alexander',            KnotInfoColumnTypes.OnlyKnots],
    'jones_polynomial':     ['Jones',                KnotInfoColumnTypes.KnotsAndLinks],
    'conway_polynomial':    ['Conway',               KnotInfoColumnTypes.KnotsAndLinks],
    'homfly_polynomial':    ['HOMFLY',               KnotInfoColumnTypes.OnlyKnots],
    'homflypt_polynomial':  ['HOMFLYPT Polynomial',  KnotInfoColumnTypes.OnlyLinks],
    'kauffman_polynomial':  ['Kauffman',             KnotInfoColumnTypes.KnotsAndLinks],
    'determinant':          ['Determinant',          KnotInfoColumnTypes.KnotsAndLinks],
    'positive':             ['Positive',             KnotInfoColumnTypes.OnlyKnots],
    'fibered':              ['Fibered',              KnotInfoColumnTypes.OnlyKnots],
    'unoriented':           ['Unoriented',           KnotInfoColumnTypes.OnlyLinks],
    'symmetry_type':        ['Symmetry Type',        KnotInfoColumnTypes.OnlyKnots],
    'width':                ['Width',                KnotInfoColumnTypes.OnlyKnots],
    'arc_notation':         ['Arc Notation',         KnotInfoColumnTypes.OnlyLinks],
    'dt_code':              ['DT code',              KnotInfoColumnTypes.OnlyLinks]
}


row_demo_sample = {
    'K0_1':   [0, 1],
    'K3_1':   [1, 1],
    'K4_1':   [2, 1],
    'K5_1':   [3, 1],
    'K5_2':   [4, 1],
    'K6_1':   [5, 1],
    'K6_2':   [6, 1],
    'K6_3':   [7, 1],
    'K7_1':   [8, 1],
    'K7_2':   [9, 1],
    'L2a1_0': [10, 2],
    'L2a1_1': [11, 2],
    'L4a1_0': [12, 2],
    'L4a1_1': [13, 2],
    'L5a1_0': [14, 2],
    'L5a1_1': [15, 2],
    'L6a1_0': [16, 2],
    'L6a1_1': [17, 2],
    'L6a2_0': [18, 2],
    'L6a2_1': [19, 2]
}

db = KnotInfoDataBase()
dc = db.columns()


data_demo_sample = {
    dc.name: ['0_1', '3_1', '4_1', '5_1', '5_2', '6_1', '6_2', '6_3', '7_1', '7_2',
              'L2a1{0}', 'L2a1{1}', 'L4a1{0}', 'L4a1{1}', 'L5a1{0}', 'L5a1{1}',
              'L6a1{0}', 'L6a1{1}', 'L6a2{0}', 'L6a2{1}', 'L6a3{0}'
             ],
    dc.name_unoriented: ['L2a1', 'L2a1', 'L4a1', 'L4a1', 'L5a1', 'L5a1', 'L6a1', 'L6a1', 'L6a2', 'L6a2', 'L6a3'],
    dc.crossing_number: ['0', '3', '4', '5', '5', '6', '6', '6', '7', '7', '2', '2', '4', '4', '5', '5', '6', '6', '6', '6', '6'],
    dc.braid_notation: [
        '',
        '{1,1,1}',
        '{1,-2,1,-2}',
        '{1,1,1,1,1}',
        '{1,1,1,2,-1,2}',
        '{1,1,2,-1,-3,2,-3}',
        '{1,1,1,-2,1,-2}',
        '{1,1,-2,1,-2,-2}',
        '{1,1,1,1,1,1,1}',
        '{1,1,1,2,-1,2,3,-2,3}',
        '{2, {-1, -1}}',
        '{2, {1, 1}}',
        '{4, {1, -2, 3, -2, -1, -2, -3, -2}}',
        '{2, {1, 1, 1, 1}}',
        '{3, {-1, 2, -1, 2, -1}}',
        '{3, {-1, 2, -1, 2, -1}}',
        '{4, {1, -2, 3, -2, 1, -2, -3, -2}}',
        '{4, {1, 2, 3, 2, 2, -1, 2, 2, -3, 2}}',
        '{4, {1, -2, -2, -2, 3, -2, -1, -2, -3, -2}}',
        '{4, {1, 2, -3, 2, -1, 2, 3, 2, 2, 2}}',
        '{2, {-1, -1, -1, -1, -1, -1}}'
        ],
    dc.braid_index: ['1', '2', '3', '2', '3', '4', '3', '3', '2', '4'],
    dc.braid_length: ['', '3', '4', '5', '6', '7', '6', '6', '7', '9'],
    dc.determinant: ['0', '3', '5', '5', '7', '9', '11', '13', '7', '11', '2', '2', '4', '4', '8', '8', '12', '12', '10', '10', '6'],
    dc.positive: ['', 'Y', 'N', 'Y', 'Y', 'N', 'N', 'N', 'Y', 'Y'],
    dc.fibered: ['', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'Y', 'N'],
    dc.unoriented: ['Y', 'N', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'Y'],
    dc.pd_notation: [
        '',
        '[[1,5,2,4],[3,1,4,6],[5,3,6,2]]',
        '[[4,2,5,1],[8,6,1,5],[6,3,7,4],[2,7,3,8]]',
        '[[2,8,3,7],[4,10,5,9],[6,2,7,1],[8,4,9,3],[10,6,1,5]]',
        '[[1,5,2,4],[3,9,4,8],[5,1,6,10],[7,3,8,2],[9,7,10,6]]',
        '[[1,7,2,6],[3,10,4,11],[5,3,6,2],[7,1,8,12],[9,4,10,5],[11,9,12,8]]',
        '[[1,8,2,9],[3,11,4,10],[5,1,6,12],[7,2,8,3],[9,7,10,6],[11,5,12,4]]',
        '[[4,2,5,1],[8,4,9,3],[12,9,1,10],[10,5,11,6],[6,11,7,12],[2,8,3,7]]',
        '[[1,9,2,8],[3,11,4,10],[5,13,6,12],[7,1,8,14],[9,3,10,2],[11,5,12,4],[13,7,14,6]]',
        '[[2,10,3,9],[4,14,5,13],[6,12,7,11],[8,2,9,1],[10,8,11,7],[12,6,13,5],[14,4,1,3]]',
        ],
    dc.pd_notation_vector: [
        '{{4, 1, 3, 2}, {2, 3, 1, 4}}',
        '{{4, 2, 3, 1}, {2, 4, 1, 3}}',
        '{{6, 1, 7, 2}, {8, 3, 5, 4}, {2, 5, 3, 6}, {4, 7, 1, 8}}',
        '{{6, 2, 7, 1}, {8, 4, 5, 3}, {2, 8, 3, 7}, {4, 6, 1, 5}}',
        '{{6, 1, 7, 2}, {10, 7, 5, 8}, {4, 5, 1, 6}, {2, 10, 3, 9}, {8, 4, 9, 3}}',
        '{{8, 2, 9, 1}, {10, 7, 5, 8}, {4, 10, 1, 9}, {2, 5, 3, 6}, {6, 3, 7, 4}}',
        '{{6, 1, 7, 2}, {10, 3, 11, 4}, {12, 8, 5, 7}, {8, 12, 9, 11}, {2, 5, 3, 6}, {4, 9, 1, 10}}',
        '{{10, 2, 11, 1}, {6, 4, 7, 3}, {12, 10, 5, 9}, {8, 6, 9, 5}, {2, 12, 3, 11}, {4, 8, 1, 7}}',
        '{{8, 1, 9, 2}, {12, 5, 7, 6}, {10, 3, 11, 4}, {4, 11, 5, 12}, {2, 7, 3, 8}, {6, 9, 1, 10}}',
        '{{10, 2, 11, 1}, {12, 6, 7, 5}, {8, 4, 9, 3}, {4, 8, 5, 7}, {2, 12, 3, 11}, {6, 10, 1, 9}}',
        '{{8, 1, 9, 2}, {2, 9, 3, 10}, {10, 3, 11, 4}, {12, 5, 7, 6}, {6, 7, 1, 8}, {4, 11, 5, 12}}'
        ],
    dc.dt_notation: [
        '',
        '[4, 6, 2]',
        '[4, 6, 8, 2]',
        '[6, 8, 10, 2, 4]',
        '[4, 8, 10, 2, 6]',
        '[4, 8, 12, 10, 2, 6]',
        '[4, 8, 10, 12, 2, 6]',
        '[4, 8, 10, 2, 12, 6]',
        '[8, 10, 12, 14, 2, 4, 6]',
        '[4, 10, 14, 12, 2, 8, 6]'
        ],
    dc.dt_code: [
        '[{4}, {2}]',
        '[{4}, {2}]',
        '[{6, 8}, {2, 4}]',
        '[{6, 8}, {4, 2}]',
        '[{6, 8}, {4, 10, 2}]',
        '[{8, 6}, {2, 10, 4}]',
        '[{6, 10}, {2, 12, 4, 8}]',
        '[{10, 6}, {8, 4, 12, 2}]',
        '[{8, 10, 12}, {2, 6, 4}]',
        '[{10, 8, 12}, {4, 6, 2}]',
        '[{8, 10, 12}, {6, 2, 4}]'
        ],
    dc.gauss_notation: [
        '',
        '{1, -2, 3, -1, 2, -3}',
        '{-1, 2, -3, 1, -4, 3, -2, 4}',
        '{-1, 2, -3, 4, -5, 1, -2, 3, -4, 5}',
        '{1, -2, 3, -1, 4, -5, 2, -3, 5, -4}',
        '{1, -2, 3, -4, 2, -1, 5, -6, 4, -3, 6, -5}',
        '{1, -2, 3, -4, 5, -6, 2, -1, 6, -3, 4, -5}',
        '{-1, 2, -3, 1, -4, 5, -2, 3, -6, 4, -5, 6}',
        '{1, -2, 3, -4, 5, -6, 7, -1, 2, -3, 4, -5, 6, -7}',
        '{-1, 2, -3, 4, -5, 6, -7, 1, -2, 7, -6, 5, -4, 3}',
        '{{1, -2}, {2, -1}}',
        '{{1, -2}, {2, -1}}',
        '{{1, -3, 2, -4}, {3, -1, 4, -2}}',
        '{{1, -3, 2, -4}, {4, -1, 3, -2}}',
        '{{1, -4, 5, -3}, {3, -1, 2, -5, 4, -2}}',
        '{{1, -4, 5, -3}, {4, -5, 2, -1, 3, -2}}',
        '{{1, -5, 2, -6}, {5, -1, 3, -4, 6, -2, 4, -3}}',
        '{{1, -5, 2, -6}, {4, -2, 6, -4, 3, -1, 5, -3}}',
        '{{1, -5, 3, -4, 2, -6}, {5, -1, 6, -3, 4, -2}}',
        '{{1, -5, 3, -4, 2, -6}, {4, -3, 6, -1, 5, -2}}',
        '{{1, -2, 3, -6, 4, -5}, {5, -1, 2, -3, 6, -4}}'
        ],
    dc.arc_notation: [
        '{{4, 2}, {3, 1}, {4, 2}, {1, 3}}',
        '{{2, 4}, {3, 1}, {2, 4}, {3, 1}}',
        '{{6, 4}, {3, 5}, {4, 2}, {1, 3}, {2, 6}, {5, 1}}',
        '{{3, 6}, {2, 5}, {6, 4}, {1, 3}, {5, 2}, {4, 1}}',
        '{{6, 2}, {1, 4}, {3, 5}, {4, 7}, {2, 6}, {7, 3}, {5, 1}}',
        '{{3, 5}, {6, 4}, {5, 2}, {7, 3}, {1, 6}, {2, 7}, {4, 1}}',
        '{{8, 4}, {3, 5}, {4, 2}, {6, 3}, {5, 7}, {1, 6}, {2, 8}, {7, 1}}',
        '{{2, 8}, {1, 7}, {8, 4}, {5, 3}, {4, 2}, {3, 6}, {7, 5}, {6, 1}}',
        '{{8, 3}, {2, 7}, {3, 1}, {4, 8}, {5, 2}, {6, 4}, {7, 5}, {1, 6}}',
        '{{3, 8}, {2, 7}, {8, 4}, {1, 3}, {5, 2}, {4, 6}, {7, 5}, {6, 1}}',
        '{{8, 2}, {1, 3}, {2, 4}, {3, 5}, {4, 6}, {5, 7}, {6, 8}, {7, 1}}'
        ],
    dc.alternating: ['Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y'],
    dc.symmetry_type: [
        '',
        'reversible',
        'fully amphicheiral',
        'reversible',
        'reversible',
        'reversible',
        'reversible',
        'fully amphicheiral',
        'reversible',
        'reversible'
        ],
    dc.homfly_polynomial: [
        '',
        '(2*v^2-v^4)+(v^2)*z^2',
        '(v^(-2)-1+ v^2)+ (-1)*z^2',
        '(3*v^4-2*v^6)+ (4*v^4-v^6)*z^2+ (v^4)*z^4',
        '(v^2+ v^4-v^6)+ (v^2+ v^4)*z^2',
        '(v^(-2)-v^2+ v^4)+ (-1-v^2)*z^2',
        '(2-2*v^2+ v^4)+ (1-3*v^2+ v^4)*z^2+ (-v^2)*z^4',
        '(-v^(-2)+ 3-v^2)+ (-v^(-2)+ 3-v^2)*z^2+ (1)*z^4',
        '(4*v^6-3*v^8)+ (10*v^6-4*v^8)*z^2+ (6*v^6-v^8)*z^4+ (v^6)*z^6',
        '(v^2+ v^6-v^8)+ (v^2+ v^4+ v^6)*z^2'
        ],
    dc.homflypt_polynomial: [
        '1/(v^3*z)-1/(v*z)-z/v',
        'v/z-v^3/z + v*z',
        '1/(v^5*z)-1/(v^3*z)-z/v^3-z/v',
        'v^3/z-v^5/z + 3*v^3*z-v^5*z + v^3*z^3',
        '1/(v*z)-v/z-z/v^3 + (2*z)/v-v*z + z^3/v',
        '1/(v*z)-v/z-z/v^3 + (2*z)/v-v*z + z^3/v',
        '1/(v^5*z)-1/(v^3*z)-(2*z)/v^3 + z/v-v*z + z^3/v',
        'v^3/z-v^5/z + 2*v^3*z + v^5*z-v^7*z + v^3*z^3 + v^5*z^3',
        '1/(v^7*z)-1/(v^5*z) + z/v^7-(2*z)/v^5-(2*z)/v^3-z^3/v^5-z^3/v^3',
        'v^5/z-v^7/z + 2*v^3*z + 2*v^5*z-v^7*z + v^3*z^3 + v^5*z^3',
        '1/(v^7*z)-1/(v^5*z) + (3*z)/v^7-(6*z)/v^5 + z^3/v^7-(5*z^3)/v^5-z^5/v^5'
        ],
    dc.kauffman_polynomial: [
        '',
        '(-a^(-4)-2*a^(-2))*z^(0)+ (a^(-5)+ a^(-3))*z^(1)+ (a^(-4)+ a^(-2))*z^(2)',
        '(-a^(-2)-1-a^2)*z^(0)+ (-a^(-1)-a)*z^(1)+ (a^(-2)+ 2+ a^2)*z^(2)+ (a^(-1)+ a)*z^(3)',
        '(2*a^(-6)+ 3*a^(-4))*z^(0)+ (a^(-9)-a^(-7)-2*a^(-5))*z^(1)+ (a^(-8)-3*a^(-6)-4*a^(-4))*z^(2)+ (a^(-7)+ a^(-5))*z^(3)+ (a^(-6)+ a^(-4))*z^(4)',
        '(a^(-6)+ a^(-4)-a^(-2))*z^(0)+ (-2*a^(-7)-2*a^(-5))*z^(1)+ (-2*a^(-6)-a^(-4)+ a^(-2))*z^(2)+ (a^(-7)+ 2*a^(-5)+ a^(-3))*z^(3)+ (a^(-6)+ a^(-4))*z^(4)',
        '(a^(-4)+ a^(-2)-a^2)*z^(0)+ (2*a^(-3)+ 2*a^(-1))*z^(1)+ (-3*a^(-4)-4*a^(-2)+ a^2)*z^(2)+ (-3*a^(-3)-2*a^(-1)+ a)*z^(3)+ (a^(-4)+ 2*a^(-2)+ 1)*z^(4)+ (a^(-3)+ a^(-1))*z^(5)',
        '(a^(-4)+ 2*a^(-2)+ 2)*z^(0)+ (-a^(-5)-a^(-3))*z^(1)+ (a^(-6)-2*a^(-4)-6*a^(-2)-3)*z^(2)+ (2*a^(-5)-2*a^(-1))*z^(3)+ (2*a^(-4)+ 3*a^(-2)+ 1)*z^(4)+ (a^(-3)+ a^(-1))*z^(5)',
        '(a^(-2)+ 3+ a^2)*z^(0)+ (-a^(-3)-2*a^(-1)-2*a-a^3)*z^(1)+ (-3*a^(-2)-6-3*a^2)*z^(2)+ (a^(-3)+ a^(-1)+ a+ a^3)*z^(3)+ (2*a^(-2)+ 4+ 2*a^2)*z^(4)+ (a^(-1)+ a)*z^(5)',
        '(-3*a^(-8)-4*a^(-6))*z^(0)+ (a^(-13)-a^(-11)+ a^(-9)+ 3*a^(-7))*z^(1)+ (a^(-12)-2*a^(-10)+ 7*a^(-8)+ 10*a^(-6))*z^(2)+ (a^(-11)-3*a^(-9)-4*a^(-7))*z^(3)+ (a^(-10)-5*a^(-8)-6*a^(-6))*z^(4)+ (a^(-9)+ a^(-7))*z^(5)+ (a^(-8)+ a^(-6))*z^(6)',
        '(-a^(-8)-a^(-6)-a^(-2))*z^(0)+ (3*a^(-9)+ 3*a^(-7))*z^(1)+ (4*a^(-8)+ 3*a^(-6)+ a^(-2))*z^(2)+ (-4*a^(-9)-6*a^(-7)-a^(-5)+ a^(-3))*z^(3)+ (-4*a^(-8)-3*a^(-6)+ a^(-4))*z^(4)+ (a^(-9)+ 2*a^(-7)+ a^(-5))*z^(5)+ (a^(-8)+ a^(-6))*z^(6)',
        'a^2-a/z-a^3/z + a*z + a^3*z',
        'a^(-2)-1/(a^3*z)-1/(a*z) + z/a^3 + z/a',
        '-a^4 + a^3/z + a^5/z + a*z-2*a^3*z-3*a^5*z + a^2*z^2 + a^4*z^2 + a^3*z^3 + a^5*z^3',
        '-a^(-4) + 1/(a^5*z) + 1/(a^3*z) + z/a^7-(2*z)/a^5-(3*z)/a^3 + z^2/a^6 + z^2/a^4 + z^3/a^5 + z^3/a^3',
        '-1 + 1/(a*z) + a/z-(2*z)/a-4*a*z-2*a^3*z-z^2 + a^4*z^2 + z^3/a + 3*a*z^3 + 2*a^3*z^3 + z^4 + a^2*z^4',
        '-1 + 1/(a*z) + a/z-(2*z)/a-4*a*z-2*a^3*z-z^2 + a^4*z^2 + z^3/a + 3*a*z^3 + 2*a^3*z^3 + z^4 + a^2*z^4',
        '-a^4 + a^3/z + a^5/z-z/a-a^3*z-2*a^5*z-3*z^2-3*a^2*z^2 + z^3/a + a^5*z^3 + 2*z^4 + 3*a^2*z^4 + a^4*z^4 + a*z^5 + a^3*z^5',
        '-a^(-4) + 1/(a^5*z) + 1/(a^3*z)-z/a^9-z/a^5-(2*z)/a^3-(3*z^2)/a^8-(3*z^2)/a^6 + z^3/a^9 + z^3/a^3 + (2*z^4)/a^8 + (3*z^4)/a^6 + z^4/a^4 + z^5/a^7 + z^5/a^5',
        'a^6-a^5/z-a^7/z-2*a^3*z + 3*a^5*z + 3*a^7*z-2*a^9*z-a^4*z^2-2*a^6*z^2-a^8*z^2 + a^3*z^3-2*a^5*z^3-2*a^7*z^3 + a^9*z^3 + a^4*z^4 + 2*a^6*z^4 + a^8*z^4 + a^5*z^5 + a^7*z^5',
        'a^(-6)-1/(a^7*z)-1/(a^5*z)-(2*z)/a^9 + (3*z)/a^7 + (3*z)/a^5-(2*z)/a^3-z^2/a^8-(2*z^2)/a^6-z^2/a^4 + z^3/a^9-(2*z^3)/a^7-(2*z^3)/a^5 + z^3/a^3 + z^4/a^8 + (2*z^4)/a^6 + z^4/a^4 + z^5/a^7 + z^5/a^5',
        'a^6-a^5/z-a^7/z + 6*a^5*z + 4*a^7*z-a^9*z + a^11*z-3*a^6*z^2-2*a^8*z^2 + a^10*z^2-5*a^5*z^3-4*a^7*z^3 + a^9*z^3 + a^6*z^4 + a^8*z^4 + a^5*z^5 + a^7*z^5'
        ],
    dc.jones_polynomial: [
        '1',
        't+ t^3-t^4',
        't^(-2)-t^(-1)+ 1-t+ t^2',
        't^2+ t^4-t^5+ t^6-t^7',
        't-t^2+ 2*t^3-t^4+ t^5-t^6',
        't^(-2)-t^(-1)+ 2-2*t+ t^2-t^3+ t^4',
        't^(-1)-1+ 2*t-2*t^2+ 2*t^3-2*t^4+ t^5',
        '-t^(-3)+ 2*t^(-2)-2*t^(-1)+ 3-2*t+ 2*t^2-t^3',
        't^3+ t^5-t^6+ t^7-t^8+ t^9-t^10',
        't-t^2+ 2*t^3-2*t^4+ 2*t^5-t^6+ t^7-t^8',
        '-x^(-5)-x^(-1)',
        '-x-x^5',
        '-x^(-9)-x^(-5) + x^(-3)-x^(-1)',
        '-x^3-x^7 + x^9-x^11',
        'x^(-7)-2/x^5 + x^(-3)-2/x + x-x^3',
        'x^(-7)-2/x^5 + x^(-3)-2/x + x-x^3',
        '-x^(-9) + x^(-7)-3/x^5 + 2/x^3-2/x + 2*x-x^3',
        '-x^3 + x^5-3*x^7 + 2*x^9-2*x^11 + 2*x^13-x^15',
        '-x^(-15) + x^(-13)-2/x^11 + 2/x^9-2/x^7 + x^(-5)-x^(-3)',
        '-x^3 + x^5-2*x^7 + 2*x^9-2*x^11 + x^13-x^15',
        '-x^(-17) + x^(-15)-x^(-13) + x^(-11)-x^(-9)-x^(-5)'
        ],
    dc.alexander_polynomial: [
        '1',
        '1-t+ t^2',
        '1-3*t+ t^2',
        '1-t+ t^2-t^3+ t^4',
        '2-3*t+ 2*t^2',
        '2-5*t+ 2*t^2',
        '1-3*t+ 3*t^2-3*t^3+ t^4',
        '1-3*t+ 5*t^2-3*t^3+ t^4',
        '1-t+ t^2-t^3+ t^4-t^5+ t^6',
        '3-5*t+ 3*t^2']
}
