class AbstractTensorProductOfKRTableaux(FullTensorProductOfCrystals):
    r"""
    Abstract class for all of tensor product of KR tableaux of a given Cartan type.

    See :class:`TensorProductOfKirillovReshetikhinTableaux`. This class should
    never be created directly. 
    """

    def __init__(self, cartan_type, B, biject_class):
        r"""
        Construct a tensor product of KR tableaux.
        
        INPUT:

        - ``cartan_type``    -- The crystal type and n value
        - ``B``              -- An (ordered) list of dimensions
        - ``biject_class``   -- The class the bijection creates

        The dimensions (i.e. `B`) is a list whose entries are lists of the
        form `[r, s]` which correspond to a tableau with `r` rows and `s`
        columns (or of shape `[r]*s`) and corresponds to a
        Kirillov-Reshetikhin crystal `B^{r,s}`.

        TESTS::
        
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,2]]); HW # indirect doctest
            Highest weight tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [2, 2]]
        """
        assert cartan_type.is_affine()

        self.affine_ct = cartan_type
        self.dims = B
        self.letters = CrystalOfLetters(cartan_type)
        self._bijection_class = biject_class
        tensorProd = []
        for rectDims in B:
            tensorProd.append(KirillovReshetikhinTableaux(
              self.letters.cartan_type(),
              rectDims[0], rectDims[1]))
        FullTensorProductOfCrystals.__init__(self, tuple(tensorProd))
        
    def _highest_weight_iter(self):
        r"""
        Iterate through all of the highest weight tensor product of Kirillov-Reshetikhin tableaux.

        EXAMPLES::

            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: list(HW) # indirect doctest
            [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]]
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]])
            sage: for x in HW: x # indirect doctest
            ...
            [[1], [2]]
            [[1], [-1]]
        """
        # This is a hack solution since during construction, the bijection will
        #   (attempt to) create a new KRT object which hasn't been fully created
        #   and stored in the UniqueRepresentation's cache. So this will be
        #   called again, causing the cycle to repeat. This hack just passes
        #   our self as an optional argument to hide it from the end-user and
        #   so we don't try to create a new KRT object. 
        from sage.combinat.rigged_configurations.rigged_configurations import HighestWeightRiggedConfigurations
        for x in HighestWeightRiggedConfigurations(self.affine_ct, self.dims):
            yield x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(KRT_init_hack=self)

    def _element_constructor_(self, *path, **options):
        r"""
        Construct a TensorProductOfKRTableauxElement.

        Typically the user will call this with the option **pathlist** which
        will receive a list and coerce it into a path.

        EXAMPLES::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) # indirect doctest
            [[1], [2], [4]] (X) [[1], [2]]
        """
        from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement
        if isinstance(path[0], KirillovReshetikhinGenericCrystalElement):
            return self.element_class(self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path])

        from sage.combinat.crystals.tensor_product import TensorProductOfCrystalsElement
        if isinstance(path[0], TensorProductOfCrystalsElement) and \
          isinstance(path[0][0], KirillovReshetikhinGenericCrystalElement):
            return self.element_class(self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path[0]])

        from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement
        if isinstance(path[0], RiggedConfigurationElement):
            if self.rigged_configurations() != path[0].parent():
                raise ValueError("Incorrect bijection image.")
            return path[0].to_tensor_product_of_Kirillov_Reshetikhin_tableaux()

        return self.element_class(self, *path, **options)

    def _convert_to_letters(self, index, tableauList):
        """
        Convert the entries of the list to a list of letters.

        This is a helper function to convert the list of ints to letters since
        we do not convert an int to an Integer at compile time.

        TESTS::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3]])
            sage: L = KRT._convert_to_letters(0, [3, 2, 2]); L
            [3, 2, 2]
            sage: type(L[0])
            <class 'sage.combinat.crystals.letters.ClassicalCrystalOfLetters_with_category.element_class'>
            sage: L[0].value
            3
        """
        return([self.letters(x) for x in tableauList])

    def rigged_configurations(self):
        """
        Return the corresponding set of rigged configurations.

        EXAMPLES::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3], [2,1]])
            sage: KRT.rigged_configurations()
            Rigged configurations of type ['A', 3, 1] and factors ((1, 3), (2, 1))
        """
        return self._bijection_class(self.affine_ct, self.dims)

    def list(self):
        r"""
        Create a list of the elements by using the iterator.
        
        TESTS::
            
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: HW.list()
            [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]]
        """
        # This is needed to overwrite the list method from the FiniteCrystals
        #   category which generates the list via f_a applications.
        return [x for x in self]
class AbstractTensorProductOfKRTableaux(FullTensorProductOfCrystals):
    r"""
    Abstract class for all of tensor product of KR tableaux of a given Cartan type.

    See :class:`TensorProductOfKirillovReshetikhinTableaux`. This class should
    never be created directly. 
    """
    def __init__(self, cartan_type, B, biject_class):
        r"""
        Construct a tensor product of KR tableaux.
        
        INPUT:

        - ``cartan_type``    -- The crystal type and n value
        - ``B``              -- An (ordered) list of dimensions
        - ``biject_class``   -- The class the bijection creates

        The dimensions (i.e. `B`) is a list whose entries are lists of the
        form `[r, s]` which correspond to a tableau with `r` rows and `s`
        columns (or of shape `[r]*s`) and corresponds to a
        Kirillov-Reshetikhin crystal `B^{r,s}`.

        TESTS::
        
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,2]]); HW # indirect doctest
            Highest weight tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [2, 2]]
        """
        assert cartan_type.is_affine()

        self.affine_ct = cartan_type
        self.dims = B
        self.letters = CrystalOfLetters(cartan_type)
        self._bijection_class = biject_class
        tensorProd = []
        for rectDims in B:
            tensorProd.append(
                KirillovReshetikhinTableaux(self.letters.cartan_type(),
                                            rectDims[0], rectDims[1]))
        FullTensorProductOfCrystals.__init__(self, tuple(tensorProd))

    def _highest_weight_iter(self):
        r"""
        Iterate through all of the highest weight tensor product of Kirillov-Reshetikhin tableaux.

        EXAMPLES::

            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: list(HW) # indirect doctest
            [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]]
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]])
            sage: for x in HW: x # indirect doctest
            ...
            [[1], [2]]
            [[1], [-1]]
        """
        # This is a hack solution since during construction, the bijection will
        #   (attempt to) create a new KRT object which hasn't been fully created
        #   and stored in the UniqueRepresentation's cache. So this will be
        #   called again, causing the cycle to repeat. This hack just passes
        #   our self as an optional argument to hide it from the end-user and
        #   so we don't try to create a new KRT object.
        from sage.combinat.rigged_configurations.rigged_configurations import HighestWeightRiggedConfigurations
        for x in HighestWeightRiggedConfigurations(self.affine_ct, self.dims):
            yield x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(
                KRT_init_hack=self)

    def _element_constructor_(self, *path, **options):
        r"""
        Construct a TensorProductOfKRTableauxElement.

        Typically the user will call this with the option **pathlist** which
        will receive a list and coerce it into a path.

        EXAMPLES::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) # indirect doctest
            [[1], [2], [4]] (X) [[1], [2]]
        """
        from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement
        if isinstance(path[0], KirillovReshetikhinGenericCrystalElement):
            return self.element_class(
                self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path])

        from sage.combinat.crystals.tensor_product import TensorProductOfCrystalsElement
        if isinstance(path[0], TensorProductOfCrystalsElement) and \
          isinstance(path[0][0], KirillovReshetikhinGenericCrystalElement):
            return self.element_class(
                self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path[0]])

        from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement
        if isinstance(path[0], RiggedConfigurationElement):
            if self.rigged_configurations() != path[0].parent():
                raise ValueError("Incorrect bijection image.")
            return path[0].to_tensor_product_of_Kirillov_Reshetikhin_tableaux()

        return self.element_class(self, *path, **options)

    def _convert_to_letters(self, index, tableauList):
        """
        Convert the entries of the list to a list of letters.

        This is a helper function to convert the list of ints to letters since
        we do not convert an int to an Integer at compile time.

        TESTS::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3]])
            sage: L = KRT._convert_to_letters(0, [3, 2, 2]); L
            [3, 2, 2]
            sage: type(L[0])
            <class 'sage.combinat.crystals.letters.ClassicalCrystalOfLetters_with_category.element_class'>
            sage: L[0].value
            3
        """
        return ([self.letters(x) for x in tableauList])

    def rigged_configurations(self):
        """
        Return the corresponding set of rigged configurations.

        EXAMPLES::

            sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3], [2,1]])
            sage: KRT.rigged_configurations()
            Rigged configurations of type ['A', 3, 1] and factors ((1, 3), (2, 1))
        """
        return self._bijection_class(self.affine_ct, self.dims)

    def list(self):
        r"""
        Create a list of the elements by using the iterator.
        
        TESTS::
            
            sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]])
            sage: HW.list()
            [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]]
        """
        # This is needed to overwrite the list method from the FiniteCrystals
        #   category which generates the list via f_a applications.
        return [x for x in self]