# coding=utf-8
r"""
Combinatorial classes of words.

To define a new class of words, please refer to the documentation file:
sage/combinat/words/notes/word_inheritance_howto.txt

AUTHORS:

    - Franco Saliola (2008-12-17): merged into sage
    - Sebastien Labbe (2008-12-17): merged into sage
    - Arnaud Bergeron (2008-12-17): merged into sage
    - Sebastien Labbe (2009-07-21): Improved morphism iterator (#6571).

EXAMPLES::

    sage: Words()
    Words
    sage: Words(4)
    Words over {1, 2, 3, 4}
    sage: Words('ab')
    Words over {'a', 'b'}
    sage: Words('natural numbers')
    Words over Non negative integers
"""
#*****************************************************************************
#       Copyright (C) 2008 Arnaud Bergeron <abergeron@gmail.com>,
#                          Sébastien Labbé <slabqc@gmail.com>,
#                          Franco Saliola <saliola@gmail.com>
#
#  Distributed under the terms of the GNU General Public License version 2 (GPLv2)
#
#  The full text of the GPLv2 is available at:
#
#                  http://www.gnu.org/licenses/
#*****************************************************************************
from sage.combinat.combinat import InfiniteAbstractCombinatorialClass
from sage.combinat.combinat import CombinatorialObject
from sage.combinat.words.alphabet import build_alphabet
from sage.misc.lazy_attribute import lazy_attribute
from sage.plot.misc import rename_keyword
from sage.misc.mrange import xmrange
from sage.rings.all import Infinity
from sage.rings.integer import Integer
from sage.rings.integer_ring import ZZ
import itertools
from sage.structure.parent import Set_PythonType

def Words(alphabet=None, length=None, finite=True, infinite=True):
    """
    Returns the combinatorial class of words of length k over an alphabet.

    EXAMPLES::

        sage: Words()
        Words
        sage: Words(length=7)
        Words of length 7
        sage: Words(5)
        Words over {1, 2, 3, 4, 5}
        sage: Words(5, 3)
        Words of length 3 over {1, 2, 3, 4, 5}
        sage: Words(5, infinite=False)
        Finite Words over {1, 2, 3, 4, 5}
        sage: Words(5, finite=False)
        Infinite Words over {1, 2, 3, 4, 5}
        sage: Words('ab')
        Words over {'a', 'b'}
        sage: Words('ab', 2)
        Words of length 2 over {'a', 'b'}
        sage: Words('ab', infinite=False)
        Finite Words over {'a', 'b'}
        sage: Words('ab', finite=False)
        Infinite Words over {'a', 'b'}
        sage: Words('positive integers', finite=False)
        Infinite Words over Positive integers
        sage: Words('natural numbers')
        Words over Non negative integers
    """
    if isinstance(alphabet, Words_all):
        return alphabet
    if alphabet is None:
        if length is None:
            if finite and infinite:
                return Words_all()
            elif finite:
                raise NotImplementedError
            else:
                raise NotImplementedError
        elif isinstance(length, (int,Integer)) and finite:
            return Words_n(length)
    else:
        if isinstance(alphabet, (int,Integer)):
            from sage.sets.integer_range import IntegerRange
            alphabet = IntegerRange(1,alphabet+1)
        elif alphabet == "integers" \
                or alphabet == "positive integers" \
                or alphabet == "natural numbers":
            alphabet = build_alphabet(name=alphabet)
        else:
            alphabet = build_alphabet(data=alphabet)
        if length is None:
            if finite and infinite:
                return Words_over_OrderedAlphabet(alphabet)
            elif finite:
                return FiniteWords_over_OrderedAlphabet(alphabet)
            else:
                return InfiniteWords_over_OrderedAlphabet(alphabet)
        elif isinstance(length, (int,Integer)):
                return FiniteWords_length_k_over_OrderedAlphabet(alphabet, length)
    raise ValueError, "do not know how to make a combinatorial class of words from your input"

from sage.structure.unique_representation import UniqueRepresentation
class Words_all(InfiniteAbstractCombinatorialClass):
    r"""
    TESTS::

        sage: from sage.combinat.words.words import Words_all
        sage: list(Words_all())
        Traceback (most recent call last):
        ...
        NotImplementedError
        sage: Words_all().list()
        Traceback (most recent call last):
        ...
        NotImplementedError: infinite list
        sage: Words_all().cardinality()
        +Infinity

    We would like the instance of this class to be unique::

        sage: Words() is Words()   # todo: not implemented
        True

    .. WARNING::

       The design of these classes is not particularly robust so extra care must
       be taken when extending this class in order to prevent unintended
       side-effects. This is particularly evident in the equality test
       :meth:`__eq__` for words.
    """
    @lazy_attribute
    def _element_classes(self):
        r"""
        Returns a dictionary that gives the class of the element of self.

        The word may be finite, infinite or of unknown length.
        Its data may be str, list, tuple, a callable or an iterable.
        For callable and iterable, the data may be cached.

        TESTS::

            sage: d = Words()._element_classes
            sage: type(d)
            <type 'dict'>
            sage: len(d)
            13
            sage: e = Words('abcdefg')._element_classes
            sage: d == e
            True
        """
        import sage.combinat.words.word as word
        return {
            'FiniteWord_list': word.FiniteWord_list,
            'FiniteWord_str': word.FiniteWord_str,
            'FiniteWord_tuple': word.FiniteWord_tuple,
            'FiniteWord_callable_with_caching': word.FiniteWord_callable_with_caching,
            'FiniteWord_callable': word.FiniteWord_callable,
            'FiniteWord_iter_with_caching': word.FiniteWord_iter_with_caching,
            'FiniteWord_iter': word.FiniteWord_iter,
            'InfiniteWord_callable_with_caching': word.InfiniteWord_callable_with_caching,
            'InfiniteWord_callable': word.InfiniteWord_callable,
            'InfiniteWord_iter_with_caching': word.InfiniteWord_iter_with_caching,
            'InfiniteWord_iter': word.InfiniteWord_iter,
            'Word_iter_with_caching': word.Word_iter_with_caching,
            'Word_iter': word.Word_iter
            }

    def __call__(self, data=None, length=None, datatype=None, caching=True, **kwds):
        r"""
        Construct a new word object with parent self.

        INPUT:

        -  ``data`` - (default: None) list, string, tuple, iterator, None
           (shorthand for []), or a callable defined on [0,1,...,length].

        -  ``length`` - (default: None) This is dependent on the type of data.
           It is ignored for words defined by lists, strings, tuples,
           etc., because they have a naturally defined length.
           For callables, this defines the domain of definition,
           which is assumed to be [0, 1, 2, ..., length-1].
           For iterators: Infinity if you know the iterator will not
           terminate (default); "unknown" if you do not know whether the
           iterator terminates; "finite" if you know that the iterator
           terminates, but do know know the length.

        -  ``datatype`` - (default: None) None, "list", "str", "tuple", "iter",
           "callable" or "pickled_function". If None, then the function tries
           to guess this from the data.

        -  ``caching`` - (default: True) True or False. Whether to keep a cache
           of the letters computed by an iterator or callable.

        NOTE:

            We only check that the first 40 letters of the word are
            actually in the alphabet. This is a quick check implemented to
            test for small programming errors. Since we also support
            infinite words, we cannot really implement a more accurate
            check.

        EXAMPLES::

            sage: from itertools import count
            sage: Words()(count())
            word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,...
            sage: Words(range(10))(count())
            Traceback (most recent call last):
            ...
            ValueError: 10 not in alphabet!
            sage: Words()("abba")
            word: abba
            sage: Words("ab")("abba")
            word: abba
            sage: Words("ab")("abca")
            Traceback (most recent call last):
            ...
            ValueError: c not in alphabet!
        """
        from sage.combinat.words.word import Word
        kwds['data'] = data
        kwds['length'] = length
        kwds['datatype'] = datatype
        kwds['caching'] = caching
        #kwds['alphabet'] = self

        # The function _construct_word handles the construction of the words.
        w = self._construct_word(**kwds)
        self._check(w)
        return w

    def _construct_word(self, data=None, length=None, datatype=None, caching=True):
        r"""
        Construct a word.

        INPUT:

        -  ``data`` - (default: None) list, string, tuple, iterator, None
           (shorthand for []), or a callable defined on [0,1,...,length].

        -  ``length`` - (default: None) This is dependent on the type of data.
           It is ignored for words defined by lists, strings, tuples,
           etc., because they have a naturally defined length.
           For callables, this defines the domain of definition,
           which is assumed to be [0, 1, 2, ..., length-1].
           For iterators: Infinity if you know the iterator will not
           terminate (default); "unknown" if you do not know whether the
           iterator terminates; "finite" if you know that the iterator
           terminates, but do know know the length.

        -  ``datatype`` - (default: None) None, "list", "str", "tuple", "iter",
           "callable". If None, then the function
           tries to guess this from the data.
        -  ``caching`` - (default: True) True or False. Whether to keep a cache
           of the letters computed by an iterator or callable.

        .. note::

           Be careful when defining words using callables and iterators. It
           appears that islice does not pickle correctly causing various errors
           when reloading. Also, most iterators do not support copying and
           should not support pickling by extension.

        EXAMPLES:

        Empty word::

            sage: Words()._construct_word()
            word:

        Word with string::

            sage: Words()._construct_word("abbabaab")
            word: abbabaab

        Word with string constructed from other types::

            sage: Words()._construct_word([0,1,1,0,1,0,0,1], datatype="str")
            word: 01101001
            sage: Words()._construct_word((0,1,1,0,1,0,0,1), datatype="str")
            word: 01101001

        Word with list::

            sage: Words()._construct_word([0,1,1,0,1,0,0,1])
            word: 01101001

        Word with list constructed from other types::

            sage: Words()._construct_word("01101001", datatype="list")
            word: 01101001
            sage: Words()._construct_word((0,1,1,0,1,0,0,1), datatype="list")
            word: 01101001

        Word with tuple::

            sage: Words()._construct_word((0,1,1,0,1,0,0,1))
            word: 01101001

        Word with tuple constructed from other types::

            sage: Words()._construct_word([0,1,1,0,1,0,0,1], datatype="tuple")
            word: 01101001
            sage: Words()._construct_word("01101001", datatype="str")
            word: 01101001

        Word with iterator::

            sage: from itertools import count
            sage: Words()._construct_word(count())
            word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,...
            sage: Words()._construct_word(iter("abbabaab")) # iterators default to infinite words
            word: abbabaab
            sage: Words()._construct_word(iter("abbabaab"), length="unknown")
            word: abbabaab
            sage: Words()._construct_word(iter("abbabaab"), length="finite")
            word: abbabaab

        Word with function (a 'callable')::

            sage: f = lambda n : add(Integer(n).digits(2)) % 2
            sage: Words()._construct_word(f)
            word: 0110100110010110100101100110100110010110...
            sage: Words()._construct_word(f, length=8)
            word: 01101001

        Word over a string with a parent::

            sage: w = Words('abc')._construct_word("abbabaab"); w
            word: abbabaab
            sage: w.parent()
            Words over {'a', 'b', 'c'}

        The default parent is the combinatorial class of all words::

            sage: w = Words()._construct_word("abbabaab"); w
            word: abbabaab
            sage: w.parent()
            Words

        Creation of a word from a word::

            sage: Words([0,1,2,3])(Words([2,3])([2,2,2,3,3,2]))
            word: 222332
            sage: _.parent()
            Words over {0, 1, 2, 3}

        ::

            sage: Words([3,2,1])(Words([2,3])([2,2,2,3,3,2]))
            word: 222332
            sage: _.parent()
            Words over {3, 2, 1}

        Construction of a word from a word when the parents are the same::

            sage: W = Words()
            sage: w = W(range(8))
            sage: z = W(w)
            sage: w is z
            True

        Construction of a word path from a finite word::

            sage: W = Words('abcd')
            sage: P = WordPaths('abcd')
            sage: w = W('aaab')
            sage: P(w)
            Path: aaab

        Construction of a word path from a Christoffel word::

            sage: w = words.ChristoffelWord(5,8)
            sage: w
            word: 0010010100101
            sage: P = WordPaths([0,1,2,3])
            sage: P(w)
            Path: 0010010100101

        Construction of a word represented by a list from a word
        represented by a str ::

            sage: w = Word('ababbbabab')
            sage: type(w)
            <class 'sage.combinat.words.word.FiniteWord_str'>
            sage: z = Word(w, datatype='list')
            sage: type(z)
            <class 'sage.combinat.words.word.FiniteWord_list'>
            sage: y = Word(w, alphabet='abc', datatype='list')
            sage: type(y)
            <class 'sage.combinat.words.word.FiniteWord_list'>

        Creation of a word from a concatenation of words::

            sage: W = Words()
            sage: w = W() * W('a')
            sage: Z = Words('ab')
            sage: Z(w)
            word: a

        Creation of a word path from a FiniteWord_iter::

            sage: w = words.FibonacciWord()
            sage: f = w[:100]
            sage: P = WordPaths([0,1,2,3])
            sage: p = P(f); p
            Path: 0100101001001010010100100101001001010010...
            sage: p.length()
            100

        Creation of a word path from a FiniteWord_callable::

            sage: g = Word(lambda n:n%2, length = 100)
            sage: P = WordPaths([0,1,2,3])
            sage: p = P(g); p
            Path: 0101010101010101010101010101010101010101...
            sage: p.length()
            100

        Creation of a word from a pickled function::

            sage: f = lambda n : n % 10
            sage: from sage.misc.fpickle import pickle_function
            sage: s = pickle_function(f)
            sage: Word(s, datatype='pickled_function')
            word: 0123456789012345678901234567890123456789...

        """
        from sage.combinat.words.abstract_word import Word_class
        from sage.combinat.words.word_infinite_datatypes import WordDatatype_callable, WordDatatype_iter
        from sage.combinat.words.word_datatypes import WordDatatype
        if isinstance(data, Word_class):
            ####################
            # If `data` is already a word and if its parent is self,
            # then return `data` (no matter what the parameter length,
            # datatype and length are).
            ###########################
            if data.parent() is self:
                return data
            ###########################
            # Otherwise, if self is not the parent of `data`, then we
            # try to recover the data, the length and the datatype of the
            # input `data`
            ###########################
            if isinstance(data,  WordDatatype_callable):
                from sage.combinat.words.finite_word import CallableFromListOfWords
                if isinstance(data._func, CallableFromListOfWords):
                    # The following line is important because, in this case,
                    # data._func is also a tuple (indeed
                    # CallableFromListOfWords inherits from tuple)
                    datatype = "callable"
                if length is None:
                    length = data._len
                data = data._func
            elif isinstance(data,  WordDatatype_iter):
                if length is None:
                    length = data._len
                data = iter(data)
            elif isinstance(data, WordDatatype):
                data = data._data
            else:
                raise TypeError, "Any instance of Word_class must be an instance of WordDatatype."

        if data is None:
            data = []

        # Guess the datatype if it is not given.
        if datatype is None:
            if isinstance(data, (list, CombinatorialObject)):
                datatype = "list"
            elif isinstance(data, (str)):
                datatype = "str"
            elif isinstance(data, tuple):
                datatype = "tuple"
            elif callable(data):
                datatype = "callable"
            elif hasattr(data,"__iter__"):
                datatype = "iter"
            else:
                raise ValueError, "Cannot guess a datatype from data (=%s); please specify one"%data
        else:
            # type check the datatypes
            if datatype == "iter" and not hasattr(data, "__iter__"):
                raise ValueError, "Your data is not iterable"
            elif datatype == "callable" and not callable(data):
                raise ValueError, "Your data is not callable"
            elif datatype not in ("list", "tuple", "str",
                                "callable", "iter", "pickled_function"):
                raise ValueError, "Unknown datatype (=%s)" % datatype

        # If `data` is a pickled_function, restore the function
        if datatype == 'pickled_function':
            from sage.misc.fpickle import unpickle_function
            data = unpickle_function(data)
            datatype = 'callable'

        # Construct the word class and keywords
        if datatype in ('list','str','tuple'):
            cls_str = 'FiniteWord_%s'%datatype
            kwds = dict(parent=self,data=data)
        elif datatype == 'callable':
            if length in (None, Infinity, 'infinite'):
                cls_str = 'InfiniteWord_callable'
            else:
                cls_str = 'FiniteWord_callable'
            if caching:
                cls_str += '_with_caching'
            kwds = dict(parent=self,callable=data,length=length)
        elif datatype == 'iter':
            if length in (None, Infinity, 'infinite'):
                cls_str = 'InfiniteWord_iter'
            elif length == 'finite':
                cls_str = 'FiniteWord_iter'
            elif length == 'unknown':
                cls_str = 'Word_iter'
            elif length in ZZ and length >= 0:
                cls_str = 'FiniteWord_iter'
            else:
                raise ValueError, "not a correct value for length (%s)" % length
            if caching:
                cls_str += '_with_caching'
            kwds = dict(parent=self,iter=data,length=length)
        else:
            raise ValueError, "Not known datatype"

        wordclass = self._element_classes
        cls = wordclass[cls_str]
        w = cls(**kwds)
        return w

    def _check(self, w, length=40):
        r"""
        Check that the first length elements are actually in the alphabet.

        NOTE:

        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: W = Words_over_Alphabet(['a','b','c'])
            sage: W._check('abcabc') is None
            True
            sage: W._check('abcabcd')
            Traceback (most recent call last):
            ...
            ValueError: d not in alphabet!
            sage: W._check('abcabc'*10+'z') is None
            True
            sage: W._check('abcabc'*10+'z', length=80)
            Traceback (most recent call last):
            ...
            ValueError: z not in alphabet!
        """
        for a in itertools.islice(w, length):
            if a not in self._alphabet:
                raise ValueError, "%s not in alphabet!" % a

    def _repr_(self):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_all
            sage: Words_all()._repr_()
            'Words'
        """
        return 'Words'

    def __contains__(self, x):
        """
        Returns True if x is contained in self.

        EXAMPLES::

            sage: from sage.combinat.words.words import Words_all
            sage: 2 in Words_all()
            False
            sage: [1,2] in Words_all()
            False
            sage: Words('ab')('abba') in Words_all()
            True
        """
        from sage.combinat.words.abstract_word import Word_class
        return isinstance(x, Word_class)

    def __eq__(self, other):
        r"""
        Returns True if self is equal to other and False otherwise.

        EXAMPLES::

            sage: Words('ab') == Words()
            False
            sage: Words() == Words('ab')
            False
            sage: Words('ab') == Words('ab')
            True
            sage: Words('ab') == Words('ba')
            False
            sage: Words('ab') == Words('abc')
            False
            sage: Words('abc') == Words('ab')
            False

        ::

            sage: WordPaths('abcd') == Words('abcd')
            True
            sage: Words('abcd') == WordPaths('abcd')
            True
            sage: Words('bacd') == WordPaths('abcd')
            False
            sage: WordPaths('bacd') == WordPaths('abcd')
            False

        TESTS:

        :trac:`15480`::

            sage: Words(3, 10) == Words(3,900)
            False
            sage: Words(2, finite=False) == Words(2)
            False
            sage: Words(2) == Words(2,30)
            False
            sage: Words(10,0) == Words(20,0)
            True
            sage: WordPaths('abcd') == Words("abcd",3)
            False
            sage: Words(3) == Words(3,finite=False)
            False
            sage: Words(3) == Words(3,infinite=False)
            False
        """

        from paths import WordPaths_all
        # Specific case of Words_over_Alphabet and WordPath. See #15480
        # i.e. when self,other in Words_over_Alphabet, WordPath and one of them at least is a wordpath
        if ((isinstance(self,WordPaths_all) and isinstance(other,WordPaths_all)) or
            (type(self) is Words_over_OrderedAlphabet and isinstance(other,WordPaths_all)) or
            (type(other) is Words_over_OrderedAlphabet and isinstance(self,WordPaths_all))):
            return self.alphabet() == other.alphabet()

        if not (type(self) is type(other)):
            return False

        cardinality = self.cardinality()

        if cardinality != other.cardinality():
            return False
        if cardinality == 1:
            return True
        if self.alphabet() != other.alphabet():
            return False

        # This method's code cannot be trusted. It's the only way I see to fix
        # the wrong results reported in #15480. But really, this kind of
        # code should not be trusted. It is likely to return wrong results if
        # whenever new classes extending Words_all are added.
        return True

    def __ne__(self, other):
        r"""
        Returns True if self is not equal to other and False otherwise.

        TESTS::

            sage: Words('ab') != Words('ab')
            False
            sage: Words('ab') != Words('abc')
            True
            sage: Words('abc') != Words('ab')
            True

        ::

            sage: WordPaths('abcd') != Words('abcd')
            False
            sage: Words('abcd') != WordPaths('abcd')
            False

        ::

            Words('ab') != 2
            True
        """
        if isinstance(other, Words_all):
            return not self.__eq__(other)
        else:
            return NotImplemented

    _alphabet = Set_PythonType(object)

    def alphabet(self):
        r"""
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: W = Words_over_Alphabet([1,2,3])
            sage: W.alphabet()
            [1, 2, 3]
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: W = Words_over_Alphabet(build_alphabet('ab'))
            sage: W.alphabet()
            {'a', 'b'}
        """
        return self._alphabet

    def size_of_alphabet(self):
        r"""
        Returns the size of the alphabet.

        EXAMPLES::

            sage: Words().size_of_alphabet()
            +Infinity
        """
        return Infinity

    cmp_letters = cmp

    def has_letter(self, letter):
        r"""
        Returns True if the alphabet of self contains the given letter.

        INPUT:

        -  ``letter`` - a letter

        EXAMPLES::

            sage: W = Words()
            sage: W.has_letter('a')
            True
            sage: W.has_letter(1)
            True
            sage: W.has_letter({})
            True
            sage: W.has_letter([])
            True
            sage: W.has_letter(range(5))
            True
            sage: W.has_letter(Permutation([]))
            True

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: W = Words_over_Alphabet(['a','b','c'])
            sage: W.has_letter('a')
            True
            sage: W.has_letter('d')
            False
            sage: W.has_letter(8)
            False
        """
        return letter in self._alphabet

class Words_over_Alphabet(Words_all):
    def __init__(self, alphabet):
        """
        Words over Alphabet.

        INPUT:

        -  ``alphabet`` - assumed to be an instance of Alphabet, but no
           type checking is done here.

        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: W = Words_over_Alphabet([1,2,3])
            sage: W == loads(dumps(W))
            True

        The input alphabet must be an instance of Alphabet::

            sage: W = Words_over_Alphabet(Alphabet([1,2,3]))
            sage: W([1,2,2,3])
            word: 1223
        """
        self._alphabet = alphabet

    def _repr_(self):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: Words_over_Alphabet([1,2,3])._repr_()
            'Words over [1, 2, 3]'
        """
        return "Words over %s"%self._alphabet

    def __contains__(self, x):
        """
        Tests whether self contains x.

        OUTPUT:
            This method returns True if x is a word of the appropriate
            length and the alphabets of the parents match. Returns False
            otherwise.

        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_Alphabet
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: A = build_alphabet('ab')
            sage: Words(A)('abba') in Words_over_Alphabet(A)
            True
            sage: Words(A)('aa') in Words_over_Alphabet(A)
            True
            sage: Words('a')('aa') in Words_over_Alphabet(A)
            False
            sage: 2 in Words_over_Alphabet([1,2,3])
            False
            sage: [2] in Words_over_Alphabet([1,2,3])
            False
            sage: [1, 'a'] in Words_over_Alphabet([1,2,3])
            False
        """
        from sage.combinat.words.abstract_word import Word_class
        return isinstance(x, Word_class) and x.parent().alphabet() == self.alphabet()

    def size_of_alphabet(self):
        r"""
        Returns the size of the alphabet.

        EXAMPLES::

            sage: Words('abcdef').size_of_alphabet()
            6
            sage: Words('').size_of_alphabet()
            0
        """
        return self.alphabet().cardinality()

    def identity_morphism(self):
        r"""
        Returns the identity morphism from self to itself.

        EXAMPLES::

            sage: W = Words('ab')
            sage: W.identity_morphism()
            WordMorphism: a->a, b->b

        ::

            sage: W = Words(range(3))
            sage: W.identity_morphism()
            WordMorphism: 0->0, 1->1, 2->2

        There is no support yet for infinite alphabet::

            sage: W = Words(alphabet=Alphabet(name='NN'))
            sage: W
            Words over Non negative integers
            sage: W.identity_morphism()
            Traceback (most recent call last):
            ...
            NotImplementedError: size of alphabet must be finite
        """
        if self.size_of_alphabet() not in ZZ:
            raise NotImplementedError, 'size of alphabet must be finite'
        from sage.combinat.words.morphism import WordMorphism
        return WordMorphism(dict((a,a) for a in self.alphabet()))

class Words_n(Words_all):
    def __init__(self, n):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_n
            sage: w = Words_n(3)
            sage: w == loads(dumps(w))
            True
        """
        self._n = n

    def __repr__(self):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_n
            sage: Words_n(3).__repr__()
            'Words of length 3'
        """
        return "Words of length %s"%self._n

    def __contains__(self, x):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_n
            sage: 2 in Words_n(3)
            False
            sage: [1,'a',3] in Words_n(3)
            False
            sage: [1,2] in Words_n(3)
            False
            sage: "abc" in Words_n(3)
            False
            sage: Words("abc")("ababc") in Words_n(3)
            False
            sage: Words([0,1])([1,0,1]) in Words_n(3)
            True
        """
        from sage.combinat.words.finite_word import FiniteWord_class
        return isinstance(x, FiniteWord_class) and x.length() == self._n

class Words_over_OrderedAlphabet(Words_over_Alphabet):
    def __init__(self, alphabet):
        r"""
        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_OrderedAlphabet
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: A = build_alphabet("abc")
            sage: W = Words_over_OrderedAlphabet(A)
            sage: W == loads(dumps(W))
            True
        """
        super(Words_over_OrderedAlphabet, self).__init__(alphabet)

    def iterate_by_length(self, l=1):
        r"""
        Returns an iterator over all the words of self of length l.

        INPUT:

        - ``l`` - integer (default: 1), the length of the desired words

        EXAMPLES::

            sage: W = Words('ab')
            sage: list(W.iterate_by_length(1))
            [word: a, word: b]
            sage: list(W.iterate_by_length(2))
            [word: aa, word: ab, word: ba, word: bb]
            sage: list(W.iterate_by_length(3))
            [word: aaa,
             word: aab,
             word: aba,
             word: abb,
             word: baa,
             word: bab,
             word: bba,
             word: bbb]
            sage: list(W.iterate_by_length('a'))
            Traceback (most recent call last):
            ...
            TypeError: the parameter l (='a') must be an integer
        """
        if not isinstance(l, (int,Integer)):
            raise TypeError, "the parameter l (=%r) must be an integer"%l
        #if l == Integer(0):
        #    yield self()
        for w in xmrange([self.size_of_alphabet()]*l):
            yield self(map(lambda x: self.alphabet().unrank(x), w))

    def __iter__(self):
        r"""
        Returns an iterator over all the words of self.

        The iterator outputs the words in lexicographic order,
        based on the order of the letters in the alphabet.

        EXAMPLES::

            sage: W = Words([4,5])
            sage: for w in W:
            ...     if len(w)>3:
            ...         break
            ...     else:
            ...         w
            ...
            word:
            word: 4
            word: 5
            word: 44
            word: 45
            word: 54
            word: 55
            word: 444
            word: 445
            word: 454
            word: 455
            word: 544
            word: 545
            word: 554
            word: 555
            sage: W = Words([5,4])
            sage: for w in W:
            ...     if len(w)>3:
            ...         break
            ...     else:
            ...         w
            ...
            word:
            word: 5
            word: 4
            word: 55
            word: 54
            word: 45
            word: 44
            word: 555
            word: 554
            word: 545
            word: 544
            word: 455
            word: 454
            word: 445
            word: 444
        """
        for l in itertools.count():
            for w in self.iterate_by_length(l):
                yield w

    @rename_keyword(deprecation=10134, l='arg')
    def iter_morphisms(self, arg=None, codomain=None, min_length=1):
        r"""
        Iterate over all morphisms with domain ``self`` and the given
        codomain.

        INPUT:

        - ``arg`` - (optional, default: None) It can be one of the following :

          - ``None`` - then the method iterates through all morphisms.

          - tuple `(a, b)` of two integers  - It specifies the range
            ``range(a, b)`` of values to consider for the sum of the length
            of the image of each letter in the alphabet.

          - list of nonnegative integers - The length of the list must be
            equal to the size of the alphabet, and the i-th integer of
            ``arg`` determines the length of the word mapped to by the i-th
            letter of the (ordered) alphabet.

        - ``codomain`` - (default: None) a combinatorial class of words.
          By default, ``codomain`` is ``self``.

        - ``min_length`` - (default: 1) nonnegative integer. If ``arg`` is
          not specified, then iterate through all the morphisms where the
          length of the images of each letter in the alphabet is at least
          ``min_length``. This is ignored if ``arg`` is a list.

        OUTPUT:

        iterator

        EXAMPLES:

        Iterator over all non-erasing morphisms::

            sage: W = Words('ab')
            sage: it = W.iter_morphisms()
            sage: for _ in range(7): it.next()
            WordMorphism: a->a, b->a
            WordMorphism: a->a, b->b
            WordMorphism: a->b, b->a
            WordMorphism: a->b, b->b
            WordMorphism: a->aa, b->a
            WordMorphism: a->aa, b->b
            WordMorphism: a->ab, b->a

        Iterator over all morphisms including erasing morphisms::

            sage: W = Words('ab')
            sage: it = W.iter_morphisms(min_length=0)
            sage: for _ in range(7): it.next()
            WordMorphism: a->, b->
            WordMorphism: a->a, b->
            WordMorphism: a->b, b->
            WordMorphism: a->, b->a
            WordMorphism: a->, b->b
            WordMorphism: a->aa, b->
            WordMorphism: a->ab, b->

        Iterator over morphisms where the sum of the lengths of the images
        of the letters is in a specific range::

            sage: for m in W.iter_morphisms((0, 3), min_length=0): m
            WordMorphism: a->, b->
            WordMorphism: a->a, b->
            WordMorphism: a->b, b->
            WordMorphism: a->, b->a
            WordMorphism: a->, b->b
            WordMorphism: a->aa, b->
            WordMorphism: a->ab, b->
            WordMorphism: a->ba, b->
            WordMorphism: a->bb, b->
            WordMorphism: a->a, b->a
            WordMorphism: a->a, b->b
            WordMorphism: a->b, b->a
            WordMorphism: a->b, b->b
            WordMorphism: a->, b->aa
            WordMorphism: a->, b->ab
            WordMorphism: a->, b->ba
            WordMorphism: a->, b->bb

        ::

            sage: for m in W.iter_morphisms( (2, 4) ): m
            WordMorphism: a->a, b->a
            WordMorphism: a->a, b->b
            WordMorphism: a->b, b->a
            WordMorphism: a->b, b->b
            WordMorphism: a->aa, b->a
            WordMorphism: a->aa, b->b
            WordMorphism: a->ab, b->a
            WordMorphism: a->ab, b->b
            WordMorphism: a->ba, b->a
            WordMorphism: a->ba, b->b
            WordMorphism: a->bb, b->a
            WordMorphism: a->bb, b->b
            WordMorphism: a->a, b->aa
            WordMorphism: a->a, b->ab
            WordMorphism: a->a, b->ba
            WordMorphism: a->a, b->bb
            WordMorphism: a->b, b->aa
            WordMorphism: a->b, b->ab
            WordMorphism: a->b, b->ba
            WordMorphism: a->b, b->bb

        Iterator over morphisms with specific image lengths::

            sage: for m in W.iter_morphisms([0, 0]): m
            WordMorphism: a->, b->
            sage: for m in W.iter_morphisms([0, 1]): m
            WordMorphism: a->, b->a
            WordMorphism: a->, b->b
            sage: for m in W.iter_morphisms([2, 1]): m
            WordMorphism: a->aa, b->a
            WordMorphism: a->aa, b->b
            WordMorphism: a->ab, b->a
            WordMorphism: a->ab, b->b
            WordMorphism: a->ba, b->a
            WordMorphism: a->ba, b->b
            WordMorphism: a->bb, b->a
            WordMorphism: a->bb, b->b
            sage: for m in W.iter_morphisms([2, 2]): m
            WordMorphism: a->aa, b->aa
            WordMorphism: a->aa, b->ab
            WordMorphism: a->aa, b->ba
            WordMorphism: a->aa, b->bb
            WordMorphism: a->ab, b->aa
            WordMorphism: a->ab, b->ab
            WordMorphism: a->ab, b->ba
            WordMorphism: a->ab, b->bb
            WordMorphism: a->ba, b->aa
            WordMorphism: a->ba, b->ab
            WordMorphism: a->ba, b->ba
            WordMorphism: a->ba, b->bb
            WordMorphism: a->bb, b->aa
            WordMorphism: a->bb, b->ab
            WordMorphism: a->bb, b->ba
            WordMorphism: a->bb, b->bb

        The codomain may be specified as well::

            sage: Y = Words('xyz')
            sage: for m in W.iter_morphisms([0, 2], codomain=Y): m
            WordMorphism: a->, b->xx
            WordMorphism: a->, b->xy
            WordMorphism: a->, b->xz
            WordMorphism: a->, b->yx
            WordMorphism: a->, b->yy
            WordMorphism: a->, b->yz
            WordMorphism: a->, b->zx
            WordMorphism: a->, b->zy
            WordMorphism: a->, b->zz
            sage: for m in Y.iter_morphisms([0,2,1], codomain=W): m
            WordMorphism: x->, y->aa, z->a
            WordMorphism: x->, y->aa, z->b
            WordMorphism: x->, y->ab, z->a
            WordMorphism: x->, y->ab, z->b
            WordMorphism: x->, y->ba, z->a
            WordMorphism: x->, y->ba, z->b
            WordMorphism: x->, y->bb, z->a
            WordMorphism: x->, y->bb, z->b
            sage: it = W.iter_morphisms(codomain=Y)
            sage: for _ in range(10): it.next()
            WordMorphism: a->x, b->x
            WordMorphism: a->x, b->y
            WordMorphism: a->x, b->z
            WordMorphism: a->y, b->x
            WordMorphism: a->y, b->y
            WordMorphism: a->y, b->z
            WordMorphism: a->z, b->x
            WordMorphism: a->z, b->y
            WordMorphism: a->z, b->z
            WordMorphism: a->xx, b->x

        TESTS::

            sage: list(W.iter_morphisms([1,0]))
            [WordMorphism: a->a, b->, WordMorphism: a->b, b->]
            sage: list(W.iter_morphisms([0,0], codomain=Y))
            [WordMorphism: a->, b->]
            sage: list(W.iter_morphisms([0, 1, 2]))
            Traceback (most recent call last):
            ...
            TypeError: arg (=[0, 1, 2]) must be an iterable of 2 integers
            sage: list(W.iter_morphisms([0, 'a']))
            Traceback (most recent call last):
            ...
            TypeError: arg (=[0, 'a']) must be an iterable of 2 integers
            sage: list(W.iter_morphisms([0, 1], codomain='a'))
            Traceback (most recent call last):
            ...
            TypeError: codomain (=a) must be an instance of Words_over_OrderedAlphabet

        The argument ``l`` is now deprecated::

            sage: W = Words('ab')
            sage: it = W.iter_morphisms(l=None)
            doctest:...: DeprecationWarning: use the option 'arg' instead of 'l'
            See http://trac.sagemath.org/10134 for details.
        """
        n = self.size_of_alphabet()
        # create an iterable of compositions (all "compositions" if arg is
        # None, or [arg] otherwise)
        if arg is None:
            from sage.combinat.integer_list import IntegerListsLex
            compositions = IntegerListsLex(itertools.count(),
                    length=n, min_part = max(0,min_length))
        elif isinstance(arg, tuple):
            if not len(arg) == 2 or not all(isinstance(a, (int,Integer)) for a in arg):
                raise TypeError("arg (=%s) must be a tuple of 2 integers" %arg)
            from sage.combinat.integer_list import IntegerListsLex
            compositions = IntegerListsLex(range(*arg),
                    length=n, min_part = max(0,min_length))
        else:
            arg = list(arg)
            if (not len(arg) == n or not
                    all(isinstance(a, (int,Integer)) for a in arg)):
                raise TypeError(
                    "arg (=%s) must be an iterable of %s integers" %(arg, n))
            compositions = [arg]

        # set the codomain
        if codomain is None:
            codomain = self
        elif not isinstance(codomain, Words_over_OrderedAlphabet):
            raise TypeError, "codomain (=%s) must be an instance of Words_over_OrderedAlphabet"%codomain

        # iterate through the morphisms
        from sage.combinat.words.morphism import WordMorphism
        for composition in compositions:
            cuts = [0] + list(composition)
            for i in range(1,len(cuts)):
                cuts[i] += cuts[i-1]
            s = cuts[-1] # same but better than s = sum(composition)
            for big_word in codomain.iterate_by_length(s):
                d = {}
                i = 0
                for a in self.alphabet():
                    d[a] = big_word[cuts[i]:cuts[i+1]]
                    i += 1
                yield WordMorphism(d, codomain=codomain)

    def cmp_letters(self, letter1, letter2):
        r"""
        Returns a negative number, zero or a positive number if
        ``letter1`` < ``letter2``, ``letter1`` == ``letter2`` or
        ``letter1`` > ``letter2`` respectively.

        INPUT:

        - ``letter1`` - a letter in the alphabet
        - ``letter2`` - a letter in the alphabet

        EXAMPLES::

            sage: from sage.combinat.words.words import Words_over_OrderedAlphabet
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: A = build_alphabet('woa')
            sage: W = Words_over_OrderedAlphabet(A)
            sage: W.cmp_letters('w','a')
            -2
            sage: W.cmp_letters('w','o')
            -1
            sage: W.cmp_letters('w','w')
            0
        """
        return int(self._alphabet.rank(letter1) - self._alphabet.rank(letter2))

class InfiniteWords_over_OrderedAlphabet(Words_over_OrderedAlphabet):
    def __init__(self, alphabet):
        r"""
        EXAMPLES::

            sage: from sage.combinat.words.words import InfiniteWords_over_OrderedAlphabet
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: A = build_alphabet("abc")
            sage: W = InfiniteWords_over_OrderedAlphabet(A)
            sage: W == loads(dumps(W))
            True
        """
        super(InfiniteWords_over_OrderedAlphabet, self).__init__(alphabet)

    def _repr_(self):
        r"""
        Returns a string representation of self.

        EXAMPLES::

            sage: Words('ab', finite=False)._repr_()
            "Infinite Words over {'a', 'b'}"
        """
        return "Infinite Words over %s" % self.alphabet()

class FiniteWords_over_OrderedAlphabet(Words_over_OrderedAlphabet):
    def __init__(self, alphabet):
        r"""
        EXAMPLES::

            sage: from sage.combinat.words.words import FiniteWords_over_OrderedAlphabet
            sage: from sage.combinat.words.alphabet import build_alphabet
            sage: A = build_alphabet("abc")
            sage: W = FiniteWords_over_OrderedAlphabet(A)
            sage: W == loads(dumps(W))
            True
        """
        super(FiniteWords_over_OrderedAlphabet, self).__init__(alphabet)

    def _repr_(self):
        r"""
        Returns a string representation of self.

        EXAMPLES::

            sage: Words('ab', infinite=False)._repr_()
            "Finite Words over {'a', 'b'}"
        """
        return "Finite Words over %s" % self.alphabet()

class FiniteWords_length_k_over_OrderedAlphabet(FiniteWords_over_OrderedAlphabet):
    def __init__(self, alphabet, length):
        """
        TESTS::

            sage: from sage.combinat.words.words import FiniteWords_length_k_over_OrderedAlphabet
            sage: A = sage.combinat.words.alphabet.build_alphabet([0,1])
            sage: W = FiniteWords_length_k_over_OrderedAlphabet(A, 3)
            sage: W == loads(dumps(W))
            True
        """
        super(FiniteWords_length_k_over_OrderedAlphabet, \
                self).__init__(alphabet)
        self._length = length

    def __contains__(self, x):
        """
        EXAMPLES::

            sage: from sage.combinat.words.words import FiniteWords_length_k_over_OrderedAlphabet
            sage: A = sage.combinat.words.alphabet.build_alphabet([0,1])
            sage: W = FiniteWords_length_k_over_OrderedAlphabet(A, 3)
            sage: [1,2,3] in W
            False
            sage: [1,2] in W
            False
            sage: Words([0,1])([1,0,1]) in W
            True
            sage: Words([1,0])([1,0,1]) in W
            False
            sage: W([1,0,1]) in W
            True
            sage: Word([2,0]) in W
            False
        """
        if super(FiniteWords_length_k_over_OrderedAlphabet, \
                self).__contains__(x) and x.length() == self._length:
            return True
        else:
            return False

    def _repr_(self):
        """
        TESTS::

            sage: from sage.combinat.words.words import FiniteWords_length_k_over_OrderedAlphabet
            sage: A = sage.combinat.words.alphabet.build_alphabet([1,0])
            sage: FiniteWords_length_k_over_OrderedAlphabet(A,3)._repr_()
            'Words of length 3 over {1, 0}'
        """
        from sage.combinat.words.word_options import word_options
        if word_options['old_repr']:
            return "Words over %s of length %s"%(self.alphabet(), self._length)
        return "Words of length %s over %s"%(self._length, self.alphabet())

    def cardinality(self):
        r"""
        Returns the number of words of length `n` from alphabet.

        EXAMPLES::

            sage: Words(['a','b','c'], 4).cardinality()
            81
            sage: Words(3, 4).cardinality()
            81
            sage: Words(0,0).cardinality()
            1
            sage: Words(5,0).cardinality()
            1
            sage: Words(['a','b','c'],0).cardinality()
            1
            sage: Words(0,1).cardinality()
            0
            sage: Words(5,1).cardinality()
            5
            sage: Words(['a','b','c'],1).cardinality()
            3
            sage: Words(7,13).cardinality()
            96889010407
            sage: Words(['a','b','c','d','e','f','g'],13).cardinality()
            96889010407
        """
        n = self.size_of_alphabet()
        return n**self._length

    def list(self):
        r"""
        Returns a list of all the words contained in self.

        EXAMPLES::

            sage: Words(0,0).list()
            [word: ]
            sage: Words(5,0).list()
            [word: ]
            sage: Words(['a','b','c'],0).list()
            [word: ]
            sage: Words(5,1).list()
            [word: 1, word: 2, word: 3, word: 4, word: 5]
            sage: Words(['a','b','c'],2).list()
            [word: aa, word: ab, word: ac, word: ba, word: bb, word: bc, word: ca, word: cb, word: cc]
        """
        return list(self)

    def __iter__(self):
        """
        Returns an iterator for all of the words of length k from
        ``self.alphabet()``. The iterator outputs the words in lexicographic
        order, with respect to the ordering of the alphabet.

        TESTS::

            sage: [w for w in Words(['a', 'b'], 2)]
            [word: aa, word: ab, word: ba, word: bb]
            sage: [w for w in Words(['b', 'a'], 2)]
            [word: bb, word: ba, word: ab, word: aa]
            sage: [w for w in Words(['a', 'b'], 0)]
            [word: ]
            sage: [w for w in Words([], 3)]
            []
        """
        return super(FiniteWords_length_k_over_OrderedAlphabet, \
                self).iterate_by_length(self._length)

    def iterate_by_length(self, length):
        r"""
        All words in this class are of the same length, so use iterator
        instead.

        TESTS::

            sage: W = Words(['a', 'b'], 2)
            sage: list(W.iterate_by_length(2))
            [word: aa, word: ab, word: ba, word: bb]
            sage: list(W.iterate_by_length(1))
            []
        """
        if length == self._length:
            return iter(self)
        else:
            return iter([])

###########################################################################
##### DEPRECATION WARNINGS ################################################
##### Added July 2009 #####################################################
###########################################################################

def is_Words(obj):
    r"""
    Returns True if obj is a word set and False otherwise.

    EXAMPLES::

        sage: from sage.combinat.words.words import is_Words
        sage: is_Words(33)
        doctest:1: DeprecationWarning: is_Words is deprecated, use isinstance(your_object, Words_all) instead!
        See http://trac.sagemath.org/6519 for details.
        False
        sage: is_Words(Words('ab'))
        True
    """
    from sage.misc.superseded import deprecation
    deprecation(6519, "is_Words is deprecated, use isinstance(your_object, Words_all) instead!")
    return isinstance(obj, Words_all)


#  Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import unittest
import numpy as np
import sys

sys.path.append("..")

import paddle
from op_test import OpTest
from op_test_xpu import XPUOpTest
from xpu.get_test_cover_info import create_test_class, get_xpu_op_support_types, XPUOpTestWrapper

paddle.enable_static()

################## TEST OP: BitwiseAnd ##################
class XPUTestBitwiseAnd(XPUOpTestWrapper):

    def __init__(self):
        self.op_name = 'bitwise_and'

    class XPUTestBitwiseAndBase(XPUOpTest):

        def setUp(self):
            self.place = paddle.XPUPlace(0)
            self.init_case()
            self.set_case()

        def set_case(self):
            self.op_type = 'bitwise_and'

            x = np.random.randint(self.low,
                                  self.high,
                                  self.x_shape,
                                  dtype=self.dtype)
            y = np.random.randint(self.low,
                                  self.high,
                                  self.y_shape,
                                  dtype=self.dtype)
            out = np.bitwise_and(x, y)

            self.attrs = {'use_xpu': True}
            self.inputs = {
                'X': OpTest.np_dtype_to_fluid_dtype(x),
                'Y': OpTest.np_dtype_to_fluid_dtype(y)
            }
            self.outputs = {'Out': out}

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

        def test_check_output(self):
            self.check_output_with_place(self.place)

        def test_check_grad(self):
            pass

    class XPUTestBitwiseAndCase1(XPUTestBitwiseAndBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseAndCase2(XPUTestBitwiseAndBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [4, 1]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseAndCase3(XPUTestBitwiseAndBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = 0
            self.high = 100

support_types = get_xpu_op_support_types('bitwise_and')
for stype in support_types:
    create_test_class(globals(), XPUTestBitwiseAnd, stype)

################## TEST OP: BitwiseOr ##################
class XPUTestBitwiseOr(XPUOpTestWrapper):

    def __init__(self):
        self.op_name = 'bitwise_or'

    class XPUTestBitwiseOrBase(XPUOpTest):

        def setUp(self):
            self.place = paddle.XPUPlace(0)
            self.init_case()
            self.set_case()

        def set_case(self):
            self.op_type = 'bitwise_or'

            x = np.random.randint(self.low,
                                  self.high,
                                  self.x_shape,
                                  dtype=self.dtype)
            y = np.random.randint(self.low,
                                  self.high,
                                  self.y_shape,
                                  dtype=self.dtype)
            out = np.bitwise_or(x, y)

            self.attrs = {'use_xpu': True}
            self.inputs = {
                'X': OpTest.np_dtype_to_fluid_dtype(x),
                'Y': OpTest.np_dtype_to_fluid_dtype(y)
            }
            self.outputs = {'Out': out}

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

        def test_check_output(self):
            self.check_output_with_place(self.place)

        def test_check_grad(self):
            pass

    class XPUTestBitwiseOrCase1(XPUTestBitwiseOrBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseOrCase2(XPUTestBitwiseOrBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [4, 1]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseOrCase3(XPUTestBitwiseOrBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = 0
            self.high = 100

support_types = get_xpu_op_support_types('bitwise_or')
for stype in support_types:
    create_test_class(globals(), XPUTestBitwiseOr, stype)

################## TEST OP: BitwiseXor ##################
class XPUTestBitwiseXor(XPUOpTestWrapper):

    def __init__(self):
        self.op_name = 'bitwise_xor'

    class XPUTestBitwiseXorBase(XPUOpTest):

        def setUp(self):
            self.place = paddle.XPUPlace(0)
            self.init_case()
            self.set_case()

        def set_case(self):
            self.op_type = 'bitwise_xor'

            x = np.random.randint(self.low,
                                  self.high,
                                  self.x_shape,
                                  dtype=self.dtype)
            y = np.random.randint(self.low,
                                  self.high,
                                  self.y_shape,
                                  dtype=self.dtype)
            out = np.bitwise_xor(x, y)

            self.attrs = {'use_xpu': True}
            self.inputs = {
                'X': OpTest.np_dtype_to_fluid_dtype(x),
                'Y': OpTest.np_dtype_to_fluid_dtype(y)
            }
            self.outputs = {'Out': out}

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

        def test_check_output(self):
            self.check_output_with_place(self.place)

        def test_check_grad(self):
            pass

    class XPUTestBitwiseXorCase1(XPUTestBitwiseXorBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseXorCase2(XPUTestBitwiseXorBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [4, 1]
            self.low = -100
            self.high = 100

    class XPUTestBitwiseXorCase3(XPUTestBitwiseXorBase):

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.y_shape = [2, 3, 4, 5]
            self.low = 0
            self.high = 100

support_types = get_xpu_op_support_types('bitwise_xor')
for stype in support_types:
    create_test_class(globals(), XPUTestBitwiseXor, stype)

##################  TEST OP: BitwiseNot ##################
class XPUTestBitwiseNot(XPUOpTestWrapper):

    def __init__(self):
        self.op_name = 'bitwise_not'

    class XPUTestBitwiseNotBase(XPUOpTest):

        def setUp(self):
            self.place = paddle.XPUPlace(0)
            self.init_case()
            self.set_case()

        def set_case(self):
            self.op_type = 'bitwise_not'

            x = np.random.randint(self.low,
                                  self.high,
                                  self.x_shape,
                                  dtype=self.dtype)
            out = np.bitwise_not(x)

            self.attrs = {'use_xpu': True}
            self.inputs = {'X': OpTest.np_dtype_to_fluid_dtype(x)}
            self.outputs = {'Out': out}

        def init_case(self):
            self.dtype = np.int32
            self.x_shape = [2, 3, 4, 5]
            self.low = -100
            self.high = 100

        def test_check_output(self):
            self.check_output_with_place(self.place)

        def test_check_grad(self):
            pass

    class XPUTestBitwiseNotBool(XPUTestBitwiseNotBase):

        def setUp(self):
            self.place = paddle.XPUPlace(0)
            self.init_case()
            self.set_case()

        def set_case(self):
            self.op_type = 'bitwise_not'

            x = np.random.choice([True, False], self.x_shape)
            out = np.bitwise_not(x)

            self.attrs = {'use_xpu': True}
            self.inputs = {'X': x}
            self.outputs = {'Out': out}

        def init_case(self):
            self.dtype = np.bool
            self.x_shape = [2, 3, 4, 5]

support_types = get_xpu_op_support_types('bitwise_not')
for stype in support_types:
    create_test_class(globals(), XPUTestBitwiseNot, stype)

if __name__ == '__main__':
    unittest.main()


# -*- coding: utf-8 -*-
"""
Created on Wed Sep 16 14:02:38 2020

@author: viardcrl
"""

import pytest as pt
import S1_algotools as s1
import numpy as np

#------------------------------------------------------------------------#
#------------------------------ EXERCISE 1 ------------------------------#
#------------------------------------------------------------------------#

def test_average_above_zero_0():
    assert s1.average_above_zero([2,2]) == 2

def test_average_above_zero_1():
    with pt.raises(ZeroDivisionError):
         s1.average_above_zero([0])

def test_average_above_zero_2():
    with pt.raises(Exception):
        assert s1.average_above_zero([-1, 2]) == 2

def test_average_above_zero_3():
    with pt.raises(TypeError):
         s1.average_above_zero(['n'])

def test_average_above_zero_4():
    with pt.raises(TypeError):
         s1.average_above_zero([None])

#------------------------------------------------------------------------#
#------------------------------ EXERCISE 2 ------------------------------#
#------------------------------------------------------------------------#

def test_max_value_0():
    assert s1.max_value([1,5,3,4,9,5]) == 4

def test_max_value_1():
    assert s1.max_value([1.4,5.3,3.2,4.5,9.9,5.2]) == 4

def test_max_value_2():
    with pt.raises(TypeError):
        assert s1.max_value([1,5,'n'])

def test_max_value_3():
    with pt.raises(TypeError):
        assert s1.max_value([2,None])

def test_max_value_4():
    assert s1.max_value([0,0,0,0,0]) == 0

#------------------------------------------------------------------------#
#------------------------------ EXERCISE 3 ------------------------------#
#------------------------------------------------------------------------#

def test_reverse_table_0():
    assert s1.reverse_table([1,5,3,4,9,5]) == [5,9,4,3,5,1]

def test_reverse_table_1():
    assert s1.reverse_table([0,0,0,0,0]) == [0,0,0,0,0]

def test_reverse_table_2():
    with pt.raises(TypeError):
        assert s1.reverse_table([1,5,'n'])

def test_reverse_table_3():
    with pt.raises(TypeError):
        assert s1.reverse_table([2,None])

def test_reverse_table_4():
    assert s1.reverse_table([1.1,2,9,4.2]) == [4.2,9,2,1.1]

#------------------------------------------------------------------------#
#------------------------------ EXERCISE 4 ------------------------------#
#------------------------------------------------------------------------#

@pt.fixture
def zero_numpy():
    zero_numpy = np.zeros((10,10), dtype=float)
    return zero_numpy

@pt.fixture
def zero_numpy_with_one(zero_numpy):
    zero_numpy[2:5, 2:5] = np.ones((3,3), dtype=float)
    return zero_numpy

def test_roi_bbox(zero_numpy_with_one, zero_numpy):
    assert s1.roi_bbox(zero_numpy) == ([2,2],[4,4])


"""Miscellaneous stuff that doesn't really fit anywhere else."""

from sympy.core import sympify

def default_sort_key(item):
    """
    A default sort key for lists of SymPy objects to pass to functions like sorted().

    This uses the default ordering. If you want a nonstandard ordering, you will
    have to create your own sort key using the sort_key() method of the object.

    **Examples**

    >>> from sympy import Basic, S, I, default_sort_key
    >>> from sympy.abc import x

    >>> sorted([S(1)/2, I, -I], key=default_sort_key)
    [1/2, -I, I]
    >>> a = [S(1)/2, I, -I]
    >>> a.sort(key=default_sort_key)
    >>> a
    [1/2, -I, I]

    >>> b = S("[x, 1/x, 1/x**2, x**2, x**(1/2), x**(1/4), x**(3/2)]")
    >>> b.sort(key=default_sort_key)

    The built-in functions min() and max() also take a key function (in Python
    2.5 or higher), that this can be used for.
    """

    #XXX: The following should also be in the docstring, but orders do not
    # actually work at the moment.

    # To use a nonstandard order, you must create your own sort key.  The default
    # order is lex.

    # >>> from sympy import sympify
    # >>> mykey = lambda item: sympify(item).sort_key(order='rev-lex')
    # >>> sorted([x, x**2, 1], key=default_sort_key)
    # [x**2, x, 1]
    # >>> sorted([x, x**2, 1], key=mykey)
    # [1, x, x**2]

    return sympify(item).sort_key()

import sys
size = getattr(sys, "maxint", None)
if size is None: #Python 3 doesn't have maxint
    size = sys.maxsize
if size > 2**32:
    ARCH = "64-bit"
else:
    ARCH = "32-bit"


"""Import statements tell python what packages you'll be using.

You can use 'as' to change how you refer to the package. In this file,
I import matplotlib.pyplot as plt so I don't have to type out
'matplotlib.pyplot' every time I want to use it.
"""
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

###############################################################################
"""After the import statements, I define one function: plot_with_pandas.
Function definitions start with the word 'def' and end when
the indentation ends. For example:

def ex1():
    print("This line is part of the function 'ex1' because it's indented")

    print("This line is still part of the function 'ex1'")
    print("Blank lines do not end the current level of indentation")

print("This line is not part of the function 'ex1' because it's not indented")

"""

def plot_with_pandas():
    """Generate plot, using pandas."""
    # pandas has an easy way to read csv data.
    # you can use pd.read_csv(), with a string that
    # tells pandas where to find the data file.
    # See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html
    # for full documentation.
    df = pd.read_csv("inputs/data.csv")

    # Now, there's a pandas dataframe named 'df' that holds all the information
    # that was in inputs/data.csv
    # A dataframe is a container we can store data in. For now, you can
    # visualize it as a table, or a spreadsheet in excel.

    # Dataframes come with their own built-in way to generate graphs.
    # This next line will generate a graph that plots the x column along the
    # x axis, and the sin and cos columns on the y-axis.
    df.plot(x="x", y=["sin", "cos"])

    # Pandas uses matplotlib.pyplot to generate graphs so you don't have to
    # interact with matplotlib.pyplot directly. In the next line, we DO interact
    # with matplotlib.pyplot directly to show the graph. We set block=False
    # because otherwise, our program will pause here until the graph is shown.
    # Instead, our program continues to run until the line below that
    # starts with 'input'
    # input() will print a string (a string is just text with quotes
    # around it) and wait for the user to press enter.
    plt.show(block=False)

###############################################################################
# These next lines 'call' the functions defined above.
# If you erase the lines below (or put a # in front of them) this code won't
# 'do' anything (it won't generate a graph anymore.)
plot_with_pandas()
input("Press enter to close the graph (if it's still open) and end the program")

# This program is broken into three parts:
# Part 1 pulls in (imports) code that other people wrote.
# Part 2 defines a function that you'll be using later.
#     Our function, plot_with_pandas, is really only 3 lines of running code,
#     but defining a function allows us to use those 3 lines together, without
#     having to re-type them every time we want them to run.
# Part 3 is the 'execution' part. It's the part that python actually 'does'.
# It runs the function defined in part 2, then it pauses on the input line until
# the user (you) hits enter.


# -*- coding: utf-8 -*-
# 本檔案為正體中文教學文件，於部署環境使用請再三留意

""" 從目前路徑 data 資料夾讀取圖像，轉換成 tfrecord 以便在 tensorflow 中使用 """

###############################################################################
#             使用到的 TensorFlow 函式，若有不懂，請見 '末尾附註'
###############################################################################

import sys

import numpy as np
import tensorflow as tf
import glob
import cv2

from random import shuffle

# parameters
# -----------------------------------------------------------------------------

IMAGE_SIZE          = 128                   # 圖像調整為正方形的矩陣大小
ZERO_LABEL          = 'down'                # 標籤為 0 的圖像檔案名稱

LABEL_NAME          = 'label'               # 樣本中標籤的索引
IMAGE_NAME          = 'image'               # 樣本中圖像的索引

TRAIN_DATA_RATIO    = 0.9                   # 訓練集資料比例
VAL_DATA_RATIO      = 0.0                   # 驗證集資料比例
TEST_DATA_RATIO     = 0.1                   # 測試集資料比例

SHUFFLE_DATA        = False                 # 是否要隨機打亂資料順序
DISPLAY_STEP        = 1                     # 每處理多少圖像顯示訊息

DATA_PATH        = 'data/bitcoin/*.jpg'     # 所有資料路徑

TRAIN_FILENAME   = 'train.tfrecords'        # 訓練集資料 TFRecord 名稱
VAL_FILENAME     = 'val.tfrecords'          # 驗證集資料 TFRecord 名稱
TEST_FILENAME    = 'test.tfrecords'         # 測試集資料 TFRecord 名稱

# =============================================================================
#                                                                       define
# =============================================================================

# -----------------------------------------------------------------------------
# load_image(addr, sq_size=224)
#
#   - 從給定的資料夾路徑讀取圖像，調整大小為正方形 , 並轉換成 RGB 格式
#
# inputs  :
#   - addr    < string > : 圖像檔案路徑 　　　　　　　p.s. 建議使用 glob 得到路徑
#   - sq_size < int >    : 圖片校正至正方形大小 　p.s. 預設調整至 224 x 224
#
# outputs :
#   - img < np.float32 > : 圖像的 RGB 矩陣數值格式
#
# -----------------------------------------------------------------------------
def load_image(addr, sq_size=IMAGE_SIZE):
    img = cv2.imread(addr)
    img = cv2.resize(img, (sq_size, sq_size), interpolation=cv2.INTER_CUBIC)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img = img.astype(np.float32)
    return img

# -----------------------------------------------------------------------------
# _int64_feature(value)
#
#   - 將給定的 label 存成 TensorFlow 的 Ｆｅａｔｕｒｅ 格式
#
# inputs  :
#   - value < 1 x N > : label 值, 例如 [0 0 0 1 1 1 0 0 0 1 0]
#
# outputs :
#   - feature < tf object > : TensorFlow int64 格式的資料
#                             相當於 C++ 中的 long long
#
# -----------------------------------------------------------------------------
def _int64_feature(value):
  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

# -----------------------------------------------------------------------------
# _bytes_feature(value)
#
#   - 將給定的　ｂｙｔｅｓｔｒｅａｍ　存成 tensorFlow 的 Ｆｅａｔｕｒｅ 格式
#
# inputs  :
#   - value < bytestream > :  二進制的表示
#
# outputs :
#   - feature < tf object > : TensorFlow bytestream 格式的資料
#
# -----------------------------------------------------------------------------
def _bytes_feature(value):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

# -----------------------------------------------------------------------------
# create_tfrecord(file_name)
#
#   - 建立給定資料集的 TFRecords 檔案
#
# inputs  :
#   - tfrecord_filename < string >       : 二進制的表示
#   - data_addrs        < 1 x N string > : N 個圖像位址
#   - data_labels       < 1 x N int >    : N 個圖像的對應標籤值
#   - dispstep          < int >          : 每處理多少圖像需要顯示進度
#
# outputs :
#   - none
#
# -----------------------------------------------------------------------------
def create_tfrecord(tfrecord_filename, data_addrs, data_labels, dispstep=1):
    writer = tf.python_io.TFRecordWriter(tfrecord_filename)
    for i in range(len(data_addrs)):

        # 每 dispstep 告知目前儲存進 TFRecords 的圖像資料有多少
        if not i % dispstep:
            print('write to {} : {}/{}'.format( tfrecord_filename, i+1, len(data_addrs)))
            sys.stdout.flush()

        # 讀取圖像
        image = load_image(addrs[i])
        label = data_labels[i]

        # 建立樣本資料 'example'
        feature  = {LABEL_NAME: _int64_feature(label),
                    IMAGE_NAME: _bytes_feature(tf.compat.as_bytes(image. tobytes()))}
        features = tf.train.Features(feature=feature)
        example = tf.train.Example(features=features)

        # 序列寫入資料集 example 到 TFRecord 檔案中
        writer.write(example.SerializeToString())

    # 關閉檔案
    writer.close()
    sys.stdout.flush()

# =============================================================================
#                                                                       script
# =============================================================================

# 從檔名是否包含 'dwon' 與否, 來將資料打上 0 或 1 的標籤
# -----------------------------------------------------------------------------

# 讀取訓練資料路徑位址
addrs  = glob.glob(DATA_PATH)

# 0 : 下一個 bar close 下跌
# 1 : 下一個 bar close 上漲
labels = [0 if ZERO_LABEL in addr else 1 for addr in addrs]

# 打亂資料順序 ( 由於我們是時間序列資料, 所以切訓練與測試還是先不要打亂 )
# -----------------------------------------------------------------------------
if SHUFFLE_DATA:
    c = list(zip(addrs, labels))
    shuffle(c)
    addrs, labels = zip(*c)

# 將資料根據比例分為訓練集, 驗證集與測試集
# -----------------------------------------------------------------------------

train_addrs  = addrs[0:int(TRAIN_DATA_RATIO*len(addrs))]
train_labels = labels[0:int(TRAIN_DATA_RATIO*len(labels))]
create_tfrecord(TRAIN_FILENAME, train_addrs, train_labels, dispstep=DISPLAY_STEP)

test_start_ratio = TRAIN_DATA_RATIO + VAL_DATA_RATIO       # 測試集開始比例位置
test_end_ratio   = test_start_ratio + TEST_DATA_RATIO      # 測試集結束比例位置

val_addrs    = addrs[int(TRAIN_DATA_RATIO*len(addrs)):int(test_start_ratio*len(addrs))]
val_labels   = labels[int(TRAIN_DATA_RATIO*len(addrs)):int(test_start_ratio*len(addrs))]
create_tfrecord(VAL_FILENAME, val_addrs, val_labels, dispstep=DISPLAY_STEP)

test_addrs   = addrs[int(test_start_ratio*len(addrs)):int(test_end_ratio*len(addrs))]
test_labels  = labels[int(test_start_ratio*len(labels)):int(test_end_ratio*len(addrs))]
create_tfrecord(TEST_FILENAME, test_addrs, test_labels, dispstep=DISPLAY_STEP)

print('TFRecords created suceefully.')
# =============================================================================
#                                                                         note
# =============================================================================

#   tf.python_io.TFRecordWriter
#     Tensorflow 提供的高效率的寫入檔案的函數, 透過 TFRecordWriter 建立 writer
#     來將資料寫進檔案, 主要是應對大量資料讀寫時會浪費記憶體而設計的, 另外也是針對
#     TFRecord 類型資料提供寫入檔案的方法, 對應的還有讀取格式

#   tf.compat.as_bytes
#     將 String 轉換成 bytestream 同時使用 UTF-8 做編碼, 使用這個函數前需要先用
#      tobytes 將圖片的 RGB 矩陣透過 numpy 轉換成 bytes

#   tf.train.Feature
#     這邊的特徵(feature) 指的是已經用指定格式表示的字典結構資料,
#     這些字典結構之後會幫助我們在 TensorFlow 執行時去對應到相對的要求輸入
#     支援整數, 福點數和二進制, 在這邊我們使用的是二進制的方式構建 feature

#  tf.train.Example
#     這邊是將特徵 (feature) 包裝成樣本 (example) 多包含了 protocol buffer
#     簡單來說就是對會使用到的資料, 建立對應的接口函數與說明資料的內容格式
#     主要目的是提供要使用 TFRecord 檔案的時候, 能更快速的進入資料並使用內容
#     protocal buffer 基本上是一種比起 JSON, XML 更好的格式, 也受到編碼保護

#  SerializeToString
#     將 bytestream 的資料寫入檔案中, 成為被編碼過後的 TFRecord 的資料
#     隨意打開可能會是一堆類似 3330 0900 0000 0000 30cb 2a4a 這種東西