from __future__ import print_function, division

from .matexpr import MatrixExpr, ShapeError, Identity
from sympy.core.sympify import _sympify
from sympy.core.compatibility import range
from sympy.matrices import MatrixBase
from sympy.core import S


class MatPow(MatrixExpr):

    def __new__(cls, base, exp):
        base = _sympify(base)
        if not base.is_Matrix:
            raise TypeError("Function parameter should be a matrix")
        exp = _sympify(exp)
        return super(MatPow, cls).__new__(cls, base, exp)

    @property
    def base(self):
        return self.args[0]

    @property
    def exp(self):
        return self.args[1]

    @property
    def shape(self):
        return self.base.shape

    def _entry(self, i, j):
        if self.exp.is_zero:
            if not self.base.is_square:
                raise ShapeError("Power of non-square matrix %s" % self.base)
            T = Identity(self.base.shape[0])
        elif self.exp is S.One:
            T = self.base
        elif isinstance(self.base, MatrixBase):
            # e.g., ImmutableMatrix**S.Half not covered by MatMul cases below
            T = self.base**self.exp
        elif self.exp.is_Integer and self.exp.is_positive:
            # Make an explicit MatMul out of the MatPow
            T = MatMul(*[self.base for k in range(self.exp)])
        #elif self.exp.is_Integer and self.exp.is_negative:
        #    # Note: possible future improvement: in principle we can take
        #    # positive powers of the inverse, but carefully avoid recursion,
        #    # perhaps by adding `_entry` to Inverse (as it is our subclass).
        #    T = self.base.inverse()
        #    T = MatMul(*[T for k in range(-self.exp)])
        else:
            raise NotImplementedError(("(%d, %d) entry" % (int(i), int(j))) +
                    "of matrix power either not defined or not implemented")
        return T._entry(i, j)


    def doit(self, **kwargs):
        deep = kwargs.get('deep', True)
        if deep:
            args = [arg.doit(**kwargs) for arg in self.args]
        else:
            args = self.args
        base = args[0]
        exp = args[1]
        if isinstance(base, MatrixBase) and exp.is_number:
            if exp is S.One:
                return base
            return base**exp
        # Note: just evaluate cases we know, return unevaluated on others.
        # E.g., MatrixSymbol('x', n, m) to power 0 is not an error.
        if exp.is_zero and base.is_square:
            return Identity(base.shape[0])
        elif exp is S.One:
            return base
        return MatPow(base, exp)


from .matmul import MatMul

@@ -753,40 +753,32 @@
     
-if
+A =
  self.
-exp.is_zero:%0A   
+doit()%0A
         
  if 
@@ -777,380 +777,223 @@
     
- 
 if 
-not self.base.is_square:%0A                raise ShapeError(%22Power of non-square matrix %25s%22 %25 self.base)%0A            T = Identity(self.base.shape%5B0%5D)%0A        elif self.exp is S.One:%0A            T = self.base%0A        elif isinstance(self.base, MatrixBase):%0A            # e.g., ImmutableMatrix**S.Half not covered by MatMul cases below%0A            T = self.base**self.exp%0A
+isinstance(A, MatPow):%0A            # We still have a MatPow, make an explicit MatMul out of it.%0A            if not A.base.is_square:%0A                raise ShapeError(%22Power of non-square matrix %25s%22 %25 A.base)%0A    
     
@@ -997,28 +997,25 @@
        elif 
-self
+A
 .exp.is_Inte
@@ -1014,36 +1014,33 @@
 .is_Integer and 
-self
+A
 .exp.is_positive
@@ -1057,65 +1057,13 @@
     
-# Make an explicit MatMul out of the MatPow%0A            T
+    A
  = M
@@ -1070,20 +1070,17 @@
 atMul(*%5B
-self
+A
 .base fo
@@ -1092,20 +1092,17 @@
 n range(
-self
+A
 .exp)%5D)%0A
@@ -1113,18 +1113,19 @@
     
+  
+  
 #elif 
-self
+A
 .exp
@@ -1166,25 +1166,24 @@
 ve:%0A        
-#
     # Note: 
@@ -1240,25 +1240,24 @@
 ake%0A        
-#
     # positi
@@ -1317,25 +1317,24 @@
 on,%0A        
-#
     # perhap
@@ -1402,26 +1402,38 @@
     
-#
    
+ #
  T = 
-self.base
+A.base.as_explicit()
 .inv
@@ -1451,14 +1451,15 @@
     
-#
     
-T
+# A
  = M
@@ -1484,20 +1484,17 @@
  range(-
-self
+A
 .exp)%5D)%0A
@@ -1493,38 +1493,46 @@
 .exp)%5D)%0A        
+    
 else:%0A
+    
             rais
@@ -1594,10 +1594,8 @@
 j)))
- +
 %0A   
@@ -1611,17 +1611,20 @@
         
-%22
++ %22 
 of matri
@@ -1686,17 +1686,17 @@
  return 
-T
+A
 ._entry(

as_explicit rewrite in terms of doit