Ample gets logical and relational operators, "when" class template.

Complex algorithms require simple logic operations, especially when it comes
to Ample collections. This commit introduces logical and relational
operators into the Ample core, the basic metahandler skeleton for an Ample
collection (i.e the "storage block" constructs) and a few extra assistive
class templates.

Among the additions, the "when" class template handles conditional branching
using a type in the condition, unlike with std::conditional, but it also
encapsulates std::enable_if - like behaviour through partial specializations
with the enable_when and disable_when class templates. The idea is to have
a single construct that changes behaviour according to certain pattern
matching rules implemented using partial specializations, making more complex
expressions easier to write and deploy.
Abstracting odreex::ample::storage_block contained types removal.

The odreex::ample::storage_block class template is to abstract building blocks
for ample type containers, also known as ample collections. The following
except is a comment from the newly inserted <odreex/ample/storage/headtail.hh>
header, which is to abstract element removal by the left / right, front / back
or head / tail directions of the storage_block class template.

When implementing type containers, there are two major operations that have
to be dealt with swiftly, that is "shortening" them in front/back, head/tail
mode and this is what the templates contained here are aiming to do.

Given a storage block like StorageBlock_T and an Ample number like
PopNumber_T, shorten head / tail by that number of elements. In order to do
that, class templates like storage_pop_tail_by and storage_pop_head_by
resort to instantiating a storage_block class template based on the original
storage_block used (i.e. StorageBlock_T) with the number of types "contained"
corrected by a factor equal to the amount of types that were "removed" from
the type "container".

That storage_block class template is used as base type from which a "new"
type (storage_pop_* one) with modified oprt metahandlers to reflect the
size changes is the "result". This is done by introducing a "factor" size
next to the actual size of the type container block (storage_block).

Thus, oprt metahandlers exploit the very nature of the pure, non-strict and
untyped functional language features of C++ template metaprogramming.

The fun part is that the algorithm involved for popping types out of the
storage_block types, is pretty straightforward to read due to the way these
class templates work and the names choosen for the various operations.