MAINT: pchanial's improvement of input validation in sparse's __mul__

Retry of @pchanial's https://github.com/scipy/scipy/pull/4066, this time with tests and in its own PR branch.  Also changed `raise NotImplemented` to `raise NotImplementedError`.
Looking at other functions, I see that `__pow__` uses the following solution:
```python
    def __pow__(self, other):
        if self.shape[0] != self.shape[1]:
            raise TypeError('matrix is not square')

        if isintlike(other):
            other = int(other)
            if other < 0:
                raise ValueError('exponent must be >= 0')

            if other == 0:
                from .construct import eye
                return eye(self.shape[0], dtype=self.dtype)
            elif other == 1:
                return self.copy()
            else:
                tmp = self.__pow__(other//2)
                if (other % 2):
                    return self * tmp * tmp
                else:
                    return tmp * tmp
        elif isscalarlike(other):
            raise ValueError('exponent must be an integer')
        else:
            raise NotImplementedError
```
Does this suggest that I should raise a `TypeError` or raise a `NotImplementedError` or a `ValueError` or should `__pow__` also be changed in this PR before it is merged?