CLN/INT: remove Index as a sub-class of NDArray

make ``Index`` now subclass ``PandasObject/IndexOpsMixin`` rather than ``ndarray``
should allow much easier new Index classes (e.g. #7640)

This doesn't change the public API at all, and provides compat (with exception of a very minor ``DatetimeIndex`` comparison with ndarrays)

closes #5080, preserves pickle compat for >= 0.8.0
back compat for pickles is now way simpler

This removed pickle compat for < 0.8.0 (well I think it does but can't really prove it). maybe can add it back if strong interest.

ToDo:

- docs
  - [x] release note
  - [x] warnings in io.rst about this change
  - [x] pickle compat now > 0.8.0
  - [x] fixes ``.repeat`` on MultiIndex (broken in master)
  - [x] minor API compat issue with comparisons of ``DatetimeIndex`` with ``NaT`` vs ndarrays
- [x] merge with searchsorted issues/PR ( #6712, #7447, #6469)
- [x] tests fixed (FIXMES), just a few left
- [x] perf
- [ ] json completely broken ATM
- [x] #7796 FIXME (PeriodIndex not supported in HDF), not really a big deal
- [x] #7439 since ``Index`` now doesn't have implicit ops (aside from ``__sub__/__add__``, these need to be added in (e.g. ``__mul__,__div__,__truediv__``).
- [x] bool(Index/MultiIndex) : https://github.com/pydata/pandas/issues/7897 (will address this later)
I don't think use of `__array_prepare__` is necessary. Numpy will call `gt` instead of `lt` if you set a higher array priority for the second argument:

```python
class ArrayLike(object):
    def __init__(self, array, priority):
        self.array = array
        self.__array_priority__ = priority
    
    def __array__(self):
        return self.array
        
    def __lt__(self, other):
        print 'subclass used lt'
        return self.array < other

    def __le__(self, other):
        print 'subclass used le'
        return self.array <= other

    def __eq__(self, other):
        print 'subclass used eq'
        return self.array == other

    def __ne__(self, other):
        print 'subclass used ne'
        return self.array != other

    def __gt__(self, other):
        print 'subclass used gt'
        return self.array > other

    def __ge__(self, other):
        print 'subclass used ge'
        return self.array >= other
```
Examples:
```
In [3]: np.array([0, 0]) < ArrayLike(np.array([1, 1]), priority=None)
Out[3]: array([ True,  True], dtype=bool)

In [4]: np.array([0, 0]) <= ArrayLike(np.array([1, 1]), priority=2)
subclass used ge
Out[4]: array([ True,  True], dtype=bool)

In [5]: 0 <= ArrayLike(np.array([1, 1]), priority=2)
subclass used ge
Out[5]: array([ True,  True], dtype=bool)
```