New engine for MultiIndex?
Currently, ``MultiIndex.get_loc()`` and ``MultiIndex.get_indexer()`` both rely on an ``_engine`` which is either a ``MultiIndexObjectEngine`` or a ``MultiIndexHashEngine``: but both of these are thin layers over the flat ``ObjectEngine``. This means that the actual structure of labels and levels is completely discarded (except e.g. for partial indexing, see ``_get_level_indexer()``).

In principle, a completely different scheme could be used:
- first look for the key elements in ``levels``, and find the corresponding code
- then look for the code in the levels

In most cases, the second part should be the computationally expensive one. It would consist in running ``nlevels`` searches in arrays of ``dtype=int`` (the ``.labels``) rather than (as it is now) one search in an ``object`` array in which each element is actually a ``tuple`` of ``nlevels`` elements. My guess is that thanks to vectorization the former should be much faster than the latter.

Moreover (and maybe more importantly), with the current engine fixing a bug such as #18485 is a nightmare. And the same applies to
```
In [2]: (4, True) in pd.MultiIndex.from_tuples([(4, 1)])
Out[2]: True
```
and probably others. This is because even though levels are not mixed, the elements are compared as ``objects``.

One caveat is that the single levels would be very often non-unique, and I'm not sure what is the impact of this with the current implementation of hash tables.
No automatic type casting in complete indexing of a large MultiIndex
#### Code Sample, a copy-pastable example if possible

```python
In [2]: pd.MultiIndex.from_product([[1., 2.], range(5000)]).get_loc((1, 0))
Out[2]: 0

In [3]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1., 0))
Out[3]: 0

In [4]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc(1)
Out[4]: slice(0, 5001, None)

In [5]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1, 0))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-a8f6fcc9f5d9> in <module>()
----> 1 pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1, 0))

/home/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2141             key = _values_from_object(key)
   2142             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2143             return self._engine.get_loc(key)
   2144 
   2145         # -- partial selection or non-unique index

/home/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15856)()
    645         return algos.pad_object(values, other, limit=limit)
    646 
--> 647     cpdef get_loc(self, object val):
    648         if is_definitely_invalid_key(val):
    649             raise TypeError("'{val}' is an invalid key".format(val=val))

/home/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15703)()
    654 
    655         try:
--> 656             return self.mapping.get_item(val)
    657         except TypeError:
    658             raise KeyError(val)

/home/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24621)()
   1446             return False
   1447 
-> 1448     cpdef get_item(self, object key):
   1449         cdef:
   1450             khiter_t k

/home/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24576)()
   1460             return loc
   1461         else:
-> 1462             raise KeyError(key)
   1463 
   1464     cpdef set_item(self, object key, Py_ssize_t val):

KeyError: (1, 0)
```

#### Problem description

Related to #18519 - this is an inherent limit of the current design of the engine for large ``MultiIndex``es, and an [improved](https://github.com/pandas-dev/pandas/issues/18519#issuecomment-347323053) ``MultiIndexEngine`` should solve this too.

#### Expected Output

```python
Out[5]: 0
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: b5f1e716d89487423bb12d6cc4e6da2b39184531
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.22.0.dev0+371.gb5f1e716d
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.3.0
xlsxwriter: 0.9.6
lxml: 4.1.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1


</details>

MultiIndexHashEngine can't manage mixed type tuples
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex.from_product([[True, False], range(1, 10)])

In [3]: mi.get_loc((False, 1))
Out[3]: 9

In [4]: mi = pd.MultiIndex.from_product([[True, False], range(1, 10000)])

In [5]: mi.get_loc((False, 1))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-68f8698878ec> in <module>()
----> 1 mi.get_loc((False, 1))

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2134             key = _values_from_object(key)
   2135             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2136             return self._engine.get_loc(key)
   2137 
   2138         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15854)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15701)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24621)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24468)()

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in _hashed_indexing_key(self, key)
    819         key = tuple([f(k, stringify)
    820                      for k, stringify in zip(key, self._have_mixed_levels)])
--> 821         return hash_tuple(key)
    822 
    823     @Appender(base._shared_docs['duplicated'] % _index_doc_kwargs)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in hash_tuple(val, encoding, hash_key)
    186               for v in val)
    187 
--> 188     h = _combine_hash_arrays(hashes, len(val))[0]
    189 
    190     return h

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in _combine_hash_arrays(arrays, num_items)
     31     """
     32     try:
---> 33         first = next(arrays)
     34     except StopIteration:
     35         return np.array([], dtype=np.uint64)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in <genexpr>(.0)
    184     """
    185     hashes = (_hash_scalar(v, encoding=encoding, hash_key=hash_key)
--> 186               for v in val)
    187 
    188     h = _combine_hash_arrays(hashes, len(val))[0]

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in _hash_scalar(val, encoding, hash_key)
    330 
    331     return hash_array(vals, hash_key=hash_key, encoding=encoding,
--> 332                       categorize=False)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in hash_array(vals, encoding, hash_key, categorize)
    290 
    291         try:
--> 292             vals = hashing.hash_object_array(vals, hash_key, encoding)
    293         except TypeError:
    294             # we have mixed types

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashing.pyx in pandas._libs.hashing.hash_object_array (pandas/_libs/hashing.c:1764)()

ValueError: Does not understand character buffer dtype format string ('?')

In [6]: mi = pd.MultiIndex.from_product([[1, 0], range(1, 10000)])

In [7]: mi.get_loc((1, 1))
Out[7]: 0

```
#### Problem description

The two engines should give the same result.

#### Expected Output

``Out[3]:``

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: f745e52e168790bff06a55928c3491cdea389508
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-3-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.22.0.dev0+241.gf745e52e1.dirty
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: None
xlrd: 1.0.0
xlwt: 1.1.2
xlsxwriter: 0.9.6
lxml: None
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1


</details>

MultiIndex.get_loc misbehaves on NaNs
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex(levels=[[1, 2, 3, 5], [4, 6]], labels=[[3, 1, 2, 0], [1, -1, 0, -1]])

In [3]: mi.get_loc((2, np.nan))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-3-d77b20d4d7a2> in <module>()
----> 1 mi.get_loc((2, np.nan))

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2119             key = _values_from_object(key)
   2120             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2121             return self._engine.get_loc(key)
   2122 
   2123         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexObjectEngine.get_loc (pandas/_libs/index.c:14965)()
    616         return super(MultiIndexObjectEngine, self).get_indexer(values)
    617 
--> 618     cpdef get_loc(self, object val):
    619 
    620         # convert a MI to an ndarray

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexObjectEngine.get_loc (pandas/_libs/index.c:14886)()
    621         if hasattr(val, 'values'):
    622             val = val.values
--> 623         return super(MultiIndexObjectEngine, self).get_loc(val)
    624 
    625 

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5832)()
    137             util.set_value_at(arr, loc, value)
    138 
--> 139     cpdef get_loc(self, object val):
    140         if is_definitely_invalid_key(val):
    141             raise TypeError("'{val}' is an invalid key".format(val=val))

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5678)()
    159 
    160         try:
--> 161             return self.mapping.get_item(val)
    162         except (TypeError, ValueError):
    163             raise KeyError(val)

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:21018)()
   1263                                        sizeof(uint32_t)) # flags
   1264 
-> 1265     cpdef get_item(self, object val):
   1266         cdef khiter_t k
   1267         if val != val or val is None:

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:20972)()
   1271             return self.table.vals[k]
   1272         else:
-> 1273             raise KeyError(val)
   1274 
   1275     cpdef set_item(self, object key, Py_ssize_t val):

KeyError: (2, nan)

In [4]: mi.get_indexer(mi.copy())
Out[4]: array([ 0, -1,  2, -1])

In [5]: mi == mi.copy()
Out[5]: array([ True, False,  True, False], dtype=bool)

```
#### Problem description

I think _this_ is actually the cause for [this example](https://github.com/pandas-dev/pandas/issues/18455#issuecomment-346700652), which is different from the one reported at the top of #18455 .

#### Expected Output

``array([ 0, 1,  2, 3])``

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: b45325e283b16ec8869aaea407de8256fc234f33
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8

pandas: 0.22.0.dev0+201.gb45325e28
pytest: 3.0.6
pip: 9.0.1
setuptools: 33.1.1
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.18.1
pyarrow: None
xarray: None
IPython: 5.2.2
sphinx: None
patsy: 0.4.1+dev
dateutil: 2.6.0
pytz: 2016.10
blosc: None
bottleneck: 1.2.0
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.2.0
xlsxwriter: None
lxml: 3.7.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.8
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None


</details>

No implicit casting of int -> float on full indexing of multiindex
#### Code Sample, a copy-pastable example if possible

```python

In [2]: mi = pd.MultiIndex.from_product([[0., 1.], ['begin', 'end']])

In [3]: mi.get_loc(1)
Out[3]: slice(2, 4, None)

In [4]: mi.get_indexer_for([(1, 'end')])
Out[4]: array([3])

In [5]: mi.get_loc((1, 'end'))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-eb387fc9b362> in <module>()
----> 1 mi.get_loc((1, 'end'))

/home/pietro/nobackup/repo/pandas/pandas/indexes/multi.py in get_loc(self, key, method)
   1962             key = _values_from_object(key)
   1963             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 1964             return self._engine.get_loc(key)
   1965 
   1966         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexEngine.get_loc (pandas/_libs/index.c:13171)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexEngine.get_loc (pandas/_libs/index.c:13018)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:23625)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:23578)()

KeyError: (1, 'end')
```

#### Problem description

We implicitly cast if indexing only one level, and if asking a list of (complete) keys: we should do the same for a single complete key.

#### Expected Output

``` python
In [6]: mi.get_loc((1., 'end'))
Out[6]: 3
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.7.0-1-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.utf8
LOCALE: it_IT.UTF-8

pandas: 0.19.0+783.gcd35d22a0.dirty
pytest: 3.0.6
pip: 9.0.1
setuptools: 33.1.1
Cython: 0.25.2
numpy: 1.12.0
scipy: 0.18.1
xarray: 0.9.1
IPython: 5.1.0.dev
sphinx: 1.4.9
patsy: 0.3.0-dev
dateutil: 2.5.3
pytz: 2016.7
blosc: None
bottleneck: 1.2.0
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.1.2
xlsxwriter: 0.9.6
lxml: 3.7.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.8
s3fs: None
pandas_gbq: None
pandas_datareader: 0.2.1

</details>

MultiIndex (and not flat Index) casts bool to int
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex.from_product([['a', 'b'], [0, 1]])

In [3]: mi.get_loc(('a', True))
Out[3]: 1

In [4]: mi.get_level_values(1).get_loc(True)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/nobackup/repo/pandas/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   2513             try:
-> 2514                 return self._engine.get_loc(key)
   2515             except KeyError:

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5834)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5617)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine._get_loc_duplicates (pandas/_libs/index.c:6064)()

~/nobackup/repo/pandas/pandas/_libs/index_class_helper.pxi in pandas._libs.index.Int64Engine._maybe_get_bool_indexer (pandas/_libs/index.c:19249)()

KeyError: True

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-4-0c9f78315be2> in <module>()
----> 1 mi.get_level_values(1).get_loc(True)

~/nobackup/repo/pandas/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   2514                 return self._engine.get_loc(key)
   2515             except KeyError:
-> 2516                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   2517 
   2518         indexer = self.get_indexer([key], method=method, tolerance=tolerance)

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5834)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5617)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine._get_loc_duplicates (pandas/_libs/index.c:6064)()

~/nobackup/repo/pandas/pandas/_libs/index_class_helper.pxi in pandas._libs.index.Int64Engine._maybe_get_bool_indexer (pandas/_libs/index.c:19249)()

KeyError: True

In [5]: mi.get_indexer([('a', True), ('b', False)])
Out[5]: array([1, 2])

In [6]: mi.get_level_values(1).get_indexer([True, False])
Out[6]: array([-1, -1])

```

#### Problem description

From https://github.com/pandas-dev/pandas/issues/18519#issue-277042995 , just to keep track of it (and add a test in #19074 , which fixes it).

#### Expected Output

``KeyError: True`` in ``Out[3]: 1`` too, ``Out[5]:`` like ``Out[6]:``.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.23.0.dev0+42.g93033151a
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.3.0
xlsxwriter: 0.9.6
lxml: 4.1.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1

</details>

New engine for MultiIndex?
Currently, ``MultiIndex.get_loc()`` and ``MultiIndex.get_indexer()`` both rely on an ``_engine`` which is either a ``MultiIndexObjectEngine`` or a ``MultiIndexHashEngine``: but both of these are thin layers over the flat ``ObjectEngine``. This means that the actual structure of labels and levels is completely discarded (except e.g. for partial indexing, see ``_get_level_indexer()``).

In principle, a completely different scheme could be used:
- first look for the key elements in ``levels``, and find the corresponding code
- then look for the code in the levels

In most cases, the second part should be the computationally expensive one. It would consist in running ``nlevels`` searches in arrays of ``dtype=int`` (the ``.labels``) rather than (as it is now) one search in an ``object`` array in which each element is actually a ``tuple`` of ``nlevels`` elements. My guess is that thanks to vectorization the former should be much faster than the latter.

Moreover (and maybe more importantly), with the current engine fixing a bug such as #18485 is a nightmare. And the same applies to
```
In [2]: (4, True) in pd.MultiIndex.from_tuples([(4, 1)])
Out[2]: True
```
and probably others. This is because even though levels are not mixed, the elements are compared as ``objects``.

One caveat is that the single levels would be very often non-unique, and I'm not sure what is the impact of this with the current implementation of hash tables.
No automatic type casting in complete indexing of a large MultiIndex
#### Code Sample, a copy-pastable example if possible

```python
In [2]: pd.MultiIndex.from_product([[1., 2.], range(5000)]).get_loc((1, 0))
Out[2]: 0

In [3]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1., 0))
Out[3]: 0

In [4]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc(1)
Out[4]: slice(0, 5001, None)

In [5]: pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1, 0))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-a8f6fcc9f5d9> in <module>()
----> 1 pd.MultiIndex.from_product([[1., 2.], range(5001)]).get_loc((1, 0))

/home/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2141             key = _values_from_object(key)
   2142             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2143             return self._engine.get_loc(key)
   2144 
   2145         # -- partial selection or non-unique index

/home/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15856)()
    645         return algos.pad_object(values, other, limit=limit)
    646 
--> 647     cpdef get_loc(self, object val):
    648         if is_definitely_invalid_key(val):
    649             raise TypeError("'{val}' is an invalid key".format(val=val))

/home/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15703)()
    654 
    655         try:
--> 656             return self.mapping.get_item(val)
    657         except TypeError:
    658             raise KeyError(val)

/home/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24621)()
   1446             return False
   1447 
-> 1448     cpdef get_item(self, object key):
   1449         cdef:
   1450             khiter_t k

/home/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24576)()
   1460             return loc
   1461         else:
-> 1462             raise KeyError(key)
   1463 
   1464     cpdef set_item(self, object key, Py_ssize_t val):

KeyError: (1, 0)
```

#### Problem description

Related to #18519 - this is an inherent limit of the current design of the engine for large ``MultiIndex``es, and an [improved](https://github.com/pandas-dev/pandas/issues/18519#issuecomment-347323053) ``MultiIndexEngine`` should solve this too.

#### Expected Output

```python
Out[5]: 0
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: b5f1e716d89487423bb12d6cc4e6da2b39184531
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.22.0.dev0+371.gb5f1e716d
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.3.0
xlsxwriter: 0.9.6
lxml: 4.1.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1


</details>

MultiIndexHashEngine can't manage mixed type tuples
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex.from_product([[True, False], range(1, 10)])

In [3]: mi.get_loc((False, 1))
Out[3]: 9

In [4]: mi = pd.MultiIndex.from_product([[True, False], range(1, 10000)])

In [5]: mi.get_loc((False, 1))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-68f8698878ec> in <module>()
----> 1 mi.get_loc((False, 1))

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2134             key = _values_from_object(key)
   2135             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2136             return self._engine.get_loc(key)
   2137 
   2138         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15854)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexHashEngine.get_loc (pandas/_libs/index.c:15701)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24621)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:24468)()

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in _hashed_indexing_key(self, key)
    819         key = tuple([f(k, stringify)
    820                      for k, stringify in zip(key, self._have_mixed_levels)])
--> 821         return hash_tuple(key)
    822 
    823     @Appender(base._shared_docs['duplicated'] % _index_doc_kwargs)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in hash_tuple(val, encoding, hash_key)
    186               for v in val)
    187 
--> 188     h = _combine_hash_arrays(hashes, len(val))[0]
    189 
    190     return h

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in _combine_hash_arrays(arrays, num_items)
     31     """
     32     try:
---> 33         first = next(arrays)
     34     except StopIteration:
     35         return np.array([], dtype=np.uint64)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in <genexpr>(.0)
    184     """
    185     hashes = (_hash_scalar(v, encoding=encoding, hash_key=hash_key)
--> 186               for v in val)
    187 
    188     h = _combine_hash_arrays(hashes, len(val))[0]

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in _hash_scalar(val, encoding, hash_key)
    330 
    331     return hash_array(vals, hash_key=hash_key, encoding=encoding,
--> 332                       categorize=False)

/home/pietro/nobackup/repo/pandas/pandas/core/util/hashing.py in hash_array(vals, encoding, hash_key, categorize)
    290 
    291         try:
--> 292             vals = hashing.hash_object_array(vals, hash_key, encoding)
    293         except TypeError:
    294             # we have mixed types

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashing.pyx in pandas._libs.hashing.hash_object_array (pandas/_libs/hashing.c:1764)()

ValueError: Does not understand character buffer dtype format string ('?')

In [6]: mi = pd.MultiIndex.from_product([[1, 0], range(1, 10000)])

In [7]: mi.get_loc((1, 1))
Out[7]: 0

```
#### Problem description

The two engines should give the same result.

#### Expected Output

``Out[3]:``

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: f745e52e168790bff06a55928c3491cdea389508
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-3-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.22.0.dev0+241.gf745e52e1.dirty
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: None
xlrd: 1.0.0
xlwt: 1.1.2
xlsxwriter: 0.9.6
lxml: None
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1


</details>

MultiIndex.get_loc misbehaves on NaNs
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex(levels=[[1, 2, 3, 5], [4, 6]], labels=[[3, 1, 2, 0], [1, -1, 0, -1]])

In [3]: mi.get_loc((2, np.nan))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-3-d77b20d4d7a2> in <module>()
----> 1 mi.get_loc((2, np.nan))

/home/pietro/nobackup/repo/pandas/pandas/core/indexes/multi.py in get_loc(self, key, method)
   2119             key = _values_from_object(key)
   2120             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 2121             return self._engine.get_loc(key)
   2122 
   2123         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexObjectEngine.get_loc (pandas/_libs/index.c:14965)()
    616         return super(MultiIndexObjectEngine, self).get_indexer(values)
    617 
--> 618     cpdef get_loc(self, object val):
    619 
    620         # convert a MI to an ndarray

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexObjectEngine.get_loc (pandas/_libs/index.c:14886)()
    621         if hasattr(val, 'values'):
    622             val = val.values
--> 623         return super(MultiIndexObjectEngine, self).get_loc(val)
    624 
    625 

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5832)()
    137             util.set_value_at(arr, loc, value)
    138 
--> 139     cpdef get_loc(self, object val):
    140         if is_definitely_invalid_key(val):
    141             raise TypeError("'{val}' is an invalid key".format(val=val))

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5678)()
    159 
    160         try:
--> 161             return self.mapping.get_item(val)
    162         except (TypeError, ValueError):
    163             raise KeyError(val)

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:21018)()
   1263                                        sizeof(uint32_t)) # flags
   1264 
-> 1265     cpdef get_item(self, object val):
   1266         cdef khiter_t k
   1267         if val != val or val is None:

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:20972)()
   1271             return self.table.vals[k]
   1272         else:
-> 1273             raise KeyError(val)
   1274 
   1275     cpdef set_item(self, object key, Py_ssize_t val):

KeyError: (2, nan)

In [4]: mi.get_indexer(mi.copy())
Out[4]: array([ 0, -1,  2, -1])

In [5]: mi == mi.copy()
Out[5]: array([ True, False,  True, False], dtype=bool)

```
#### Problem description

I think _this_ is actually the cause for [this example](https://github.com/pandas-dev/pandas/issues/18455#issuecomment-346700652), which is different from the one reported at the top of #18455 .

#### Expected Output

``array([ 0, 1,  2, 3])``

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: b45325e283b16ec8869aaea407de8256fc234f33
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8

pandas: 0.22.0.dev0+201.gb45325e28
pytest: 3.0.6
pip: 9.0.1
setuptools: 33.1.1
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.18.1
pyarrow: None
xarray: None
IPython: 5.2.2
sphinx: None
patsy: 0.4.1+dev
dateutil: 2.6.0
pytz: 2016.10
blosc: None
bottleneck: 1.2.0
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.2.0
xlsxwriter: None
lxml: 3.7.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.8
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None


</details>

No implicit casting of int -> float on full indexing of multiindex
#### Code Sample, a copy-pastable example if possible

```python

In [2]: mi = pd.MultiIndex.from_product([[0., 1.], ['begin', 'end']])

In [3]: mi.get_loc(1)
Out[3]: slice(2, 4, None)

In [4]: mi.get_indexer_for([(1, 'end')])
Out[4]: array([3])

In [5]: mi.get_loc((1, 'end'))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-eb387fc9b362> in <module>()
----> 1 mi.get_loc((1, 'end'))

/home/pietro/nobackup/repo/pandas/pandas/indexes/multi.py in get_loc(self, key, method)
   1962             key = _values_from_object(key)
   1963             key = tuple(map(_maybe_str_to_time_stamp, key, self.levels))
-> 1964             return self._engine.get_loc(key)
   1965 
   1966         # -- partial selection or non-unique index

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexEngine.get_loc (pandas/_libs/index.c:13171)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.MultiIndexEngine.get_loc (pandas/_libs/index.c:13018)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:23625)()

/home/pietro/nobackup/repo/pandas/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.MultiIndexHashTable.get_item (pandas/_libs/hashtable.c:23578)()

KeyError: (1, 'end')
```

#### Problem description

We implicitly cast if indexing only one level, and if asking a list of (complete) keys: we should do the same for a single complete key.

#### Expected Output

``` python
In [6]: mi.get_loc((1., 'end'))
Out[6]: 3
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.7.0-1-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.utf8
LOCALE: it_IT.UTF-8

pandas: 0.19.0+783.gcd35d22a0.dirty
pytest: 3.0.6
pip: 9.0.1
setuptools: 33.1.1
Cython: 0.25.2
numpy: 1.12.0
scipy: 0.18.1
xarray: 0.9.1
IPython: 5.1.0.dev
sphinx: 1.4.9
patsy: 0.3.0-dev
dateutil: 2.5.3
pytz: 2016.7
blosc: None
bottleneck: 1.2.0
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.1.2
xlsxwriter: 0.9.6
lxml: 3.7.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.8
s3fs: None
pandas_gbq: None
pandas_datareader: 0.2.1

</details>

MultiIndex (and not flat Index) casts bool to int
#### Code Sample, a copy-pastable example if possible

```python
In [2]: mi = pd.MultiIndex.from_product([['a', 'b'], [0, 1]])

In [3]: mi.get_loc(('a', True))
Out[3]: 1

In [4]: mi.get_level_values(1).get_loc(True)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/nobackup/repo/pandas/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   2513             try:
-> 2514                 return self._engine.get_loc(key)
   2515             except KeyError:

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5834)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5617)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine._get_loc_duplicates (pandas/_libs/index.c:6064)()

~/nobackup/repo/pandas/pandas/_libs/index_class_helper.pxi in pandas._libs.index.Int64Engine._maybe_get_bool_indexer (pandas/_libs/index.c:19249)()

KeyError: True

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-4-0c9f78315be2> in <module>()
----> 1 mi.get_level_values(1).get_loc(True)

~/nobackup/repo/pandas/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   2514                 return self._engine.get_loc(key)
   2515             except KeyError:
-> 2516                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   2517 
   2518         indexer = self.get_indexer([key], method=method, tolerance=tolerance)

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5834)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5617)()

~/nobackup/repo/pandas/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine._get_loc_duplicates (pandas/_libs/index.c:6064)()

~/nobackup/repo/pandas/pandas/_libs/index_class_helper.pxi in pandas._libs.index.Int64Engine._maybe_get_bool_indexer (pandas/_libs/index.c:19249)()

KeyError: True

In [5]: mi.get_indexer([('a', True), ('b', False)])
Out[5]: array([1, 2])

In [6]: mi.get_level_values(1).get_indexer([True, False])
Out[6]: array([-1, -1])

```

#### Problem description

From https://github.com/pandas-dev/pandas/issues/18519#issue-277042995 , just to keep track of it (and add a test in #19074 , which fixes it).

#### Expected Output

``KeyError: True`` in ``Out[3]: 1`` too, ``Out[5]:`` like ``Out[6]:``.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.3.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: it_IT.UTF-8
LOCALE: it_IT.UTF-8

pandas: 0.23.0.dev0+42.g93033151a
pytest: 3.2.3
pip: 9.0.1
setuptools: 36.7.0
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.0dev
tables: 3.3.0
numexpr: 2.6.1
feather: 0.3.1
matplotlib: 2.0.0
openpyxl: 2.3.0
xlrd: 1.0.0
xlwt: 1.3.0
xlsxwriter: 0.9.6
lxml: 4.1.1
bs4: 4.5.3
html5lib: 0.999999999
sqlalchemy: 1.0.15
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.2.1

</details>