diff --git a/README.md b/README.md
index 9b1c6f07..28d2dc3d 100644
--- a/README.md
+++ b/README.md
@@ -214,6 +214,14 @@ j.empty();    // false
 j.type();     // json::value_t::array
 j.clear();    // the array is empty again
 
+// convenience type checkers
+j.is_null();
+j.is_boolean();
+j.is_number();
+j.is_object();
+j.is_array();
+j.is_string();
+
 // comparison
 j == "[\"foo\", 1, true]"_json;  // true
 
diff --git a/json.gif b/json.gif
new file mode 100644
index 00000000..7e52693c
Binary files /dev/null and b/json.gif differ
diff --git a/src/json.hpp b/src/json.hpp
index 14517db8..66756d90 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -7,6 +7,26 @@
 @see https://github.com/nlohmann/json
 */
 
+/*!
+@defgroup container Container
+@brief C++ Container concept
+
+A Container is an object used to store other objects and taking care of the
+management of the memory used by the objects it contains.
+
+@see http://en.cppreference.com/w/cpp/concept/Container
+
+@defgroup reversiblecontainer Reversible Container
+@brief C++ Reversible Container concept
+@ingroup container
+
+A ReversibleContainer is a Container that has iterators that meet the
+requirements of either BidirectionalIterator or RandomAccessIterator. Such
+iterators allow a ReversibleContainer to be iterated over in reverse.
+
+@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
+*/
+
 #ifndef _NLOHMANN_JSON
 #define _NLOHMANN_JSON
 
@@ -27,6 +47,7 @@
 #include <vector>
 
 /*!
+@brief namespace for Niels Lohmann
 @see https://github.com/nlohmann
 */
 namespace nlohmann
@@ -75,30 +96,76 @@ class basic_json
     class iterator;
     class const_iterator;
 
-    /// the type of elements in a basic_json container
+    /*!
+    @brief the type of elements in a basic_json container
+    @ingroup container
+    */
     using value_type = basic_json;
-    /// the type of an element reference
+
+    /*!
+    @brief the type of an element reference
+    @ingroup container
+    */
     using reference = basic_json&;
-    /// the type of an element const reference
+
+    /*!
+    @brief the type of an element const reference
+    @ingroup container
+    */
     using const_reference = const basic_json&;
+
+    /*!
+    @brief a type to represent differences between iterators
+    @ingroup container
+    */
+    using difference_type = std::ptrdiff_t;
+
+    /*!
+    @brief a type to represent container sizes
+    @ingroup container
+    */
+    using size_type = std::size_t;
+
+    /// the allocator type
+    using allocator_type = Allocator<basic_json>;
+
     /// the type of an element pointer
     using pointer = basic_json*;
     /// the type of an element const pointer
     using const_pointer = const basic_json*;
-    /// a type to represent differences between iterators
-    using difference_type = std::ptrdiff_t;
-    /// a type to represent container sizes
-    using size_type = std::size_t;
-    /// an iterator for a basic_json container
+
+    /*!
+    @brief an iterator for a basic_json container
+    @ingroup container
+    */
     using iterator = basic_json::iterator;
-    /// a const iterator for a basic_json container
+
+    /*!
+    @brief a const iterator for a basic_json container
+    @ingroup container
+    */
     using const_iterator = basic_json::const_iterator;
-    /// a reverse iterator for a basic_json container
+
+    /*!
+    @brief a reverse iterator for a basic_json container
+    @ingroup reversiblecontainer
+    */
     using reverse_iterator = std::reverse_iterator<iterator>;
-    /// a const reverse iterator for a basic_json container
+
+    /*!
+    @brief a const reverse iterator for a basic_json container
+    @ingroup reversiblecontainer
+    */
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
 
+    /// returns the allocator associated with the container
+    inline allocator_type get_allocator() const
+    {
+        return allocator_type();
+    }
+
+
     ///////////////////////////
     // JSON value data types //
     ///////////////////////////
@@ -166,6 +233,85 @@ class basic_json
         number_float    ///< number value (floating-point)
     };
 
+    /*!
+    @brief comparison operator for JSON value types
+
+    Returns an ordering that is similar to Python:
+    - order: null < boolean < number < object < array < string
+    - furthermore, each type is not smaller than itself
+    */
+    friend bool operator<(const value_t lhs, const value_t rhs)
+    {
+        // no type is smaller than itself
+        if (lhs == rhs)
+        {
+            return false;
+        }
+
+        switch (lhs)
+        {
+            case (value_t::null):
+            {
+                // nulls are smaller than all other types
+                return true;
+            }
+
+            case (value_t::boolean):
+            {
+                // only nulls are smaller than booleans
+                return (rhs != value_t::null);
+            }
+
+            case (value_t::number_float):
+            case (value_t::number_integer):
+            {
+                switch (rhs)
+                {
+                    // numbers are smaller than objects, arrays, and string
+                    case (value_t::object):
+                    case (value_t::array):
+                    case (value_t::string):
+                    {
+                        return true;
+                    }
+                    default:
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            case (value_t::object):
+            {
+                switch (rhs)
+                {
+                    // objects are smaller than arrays and string
+                    case (value_t::array):
+                    case (value_t::string):
+                    {
+                        return true;
+                    }
+                    default:
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            case (value_t::array):
+            {
+                // arrays are smaller than strings
+                return (rhs == value_t::string);
+            }
+
+            default:
+            {
+                // a string is not smaller than any other types
+                return false;
+            }
+        }
+    }
+
 
     //////////////////
     // constructors //
@@ -226,10 +372,11 @@ class basic_json
         }
     }
 
-    /// create a null object (implicitly)
-    inline basic_json() noexcept
-        : m_type(value_t::null)
-    {}
+    /*!
+    @brief create a null object (implicitly)
+    @ingroup container
+    */
+    inline basic_json() noexcept = default;
 
     /// create a null object (explicitly)
     inline basic_json(std::nullptr_t) noexcept
@@ -424,11 +571,15 @@ class basic_json
         return basic_json(l, false, value_t::object);
     }
 
+
     ///////////////////////////////////////
     // other constructors and destructor //
     ///////////////////////////////////////
 
-    /// copy constructor
+    /*!
+    @brief copy constructor
+    @ingroup container
+    */
     inline basic_json(const basic_json& other)
         : m_type(other.m_type)
     {
@@ -487,7 +638,10 @@ class basic_json
         other.m_value = {};
     }
 
-    /// copy assignment
+    /*!
+    @brief copy assignment
+    @ingroup container
+    */
     inline reference& operator=(basic_json other) noexcept (
         std::is_nothrow_move_constructible<value_t>::value and
         std::is_nothrow_move_assignable<value_t>::value and
@@ -500,7 +654,10 @@ class basic_json
         return *this;
     }
 
-    /// destructor
+    /*!
+    @brief destructor
+    @ingroup container
+    */
     inline ~basic_json() noexcept
     {
         switch (m_type)
@@ -577,6 +734,42 @@ class basic_json
         return m_type;
     }
 
+    // return whether value is null
+    inline bool is_null() const noexcept
+    {
+        return m_type == value_t::null;
+    }
+
+    // return whether value is boolean
+    inline bool is_boolean() const noexcept
+    {
+        return m_type == value_t::boolean;
+    }
+
+    // return whether value is number
+    inline bool is_number() const noexcept
+    {
+        return (m_type == value_t::number_integer) or (m_type == value_t::number_float);
+    }
+
+    // return whether value is object
+    inline bool is_object() const noexcept
+    {
+        return m_type == value_t::object;
+    }
+
+    // return whether value is array
+    inline bool is_array() const noexcept
+    {
+        return m_type == value_t::array;
+    }
+
+    // return whether value is string
+    inline bool is_string() const noexcept
+    {
+        return m_type == value_t::string;
+    }
+
     /// return the type of the object (implicit)
     operator value_t() const noexcept
     {
@@ -871,7 +1064,10 @@ class basic_json
     // iterators //
     ///////////////
 
-    /// returns an iterator to the beginning of the container
+    /*!
+    @brief returns an iterator to the first element
+    @ingroup container
+    */
     inline iterator begin() noexcept
     {
         iterator result(this);
@@ -879,7 +1075,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the beginning of the container
+    /*!
+    @brief returns a const iterator to the first element
+    @ingroup container
+    */
     inline const_iterator begin() const noexcept
     {
         const_iterator result(this);
@@ -887,7 +1086,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the beginning of the container
+    /*!
+    @brief returns a const iterator to the first element
+    @ingroup container
+    */
     inline const_iterator cbegin() const noexcept
     {
         const_iterator result(this);
@@ -895,7 +1097,10 @@ class basic_json
         return result;
     }
 
-    /// returns an iterator to the end of the container
+    /*!
+    @brief returns an iterator to one past the last element
+    @ingroup container
+    */
     inline iterator end() noexcept
     {
         iterator result(this);
@@ -903,7 +1108,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the end of the container
+    /*!
+    @brief returns a const iterator to one past the last element
+    @ingroup container
+    */
     inline const_iterator end() const noexcept
     {
         const_iterator result(this);
@@ -911,7 +1119,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the end of the container
+    /*!
+    @brief returns a const iterator to one past the last element
+    @ingroup container
+    */
     inline const_iterator cend() const noexcept
     {
         const_iterator result(this);
@@ -919,37 +1130,55 @@ class basic_json
         return result;
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline reverse_iterator rbegin() noexcept
     {
         return reverse_iterator(end());
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a const reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator rbegin() const noexcept
     {
         return const_reverse_iterator(end());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline reverse_iterator rend() noexcept
     {
         return reverse_iterator(begin());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a const reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator rend() const noexcept
     {
         return const_reverse_iterator(begin());
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a const reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator crbegin() const noexcept
     {
         return const_reverse_iterator(cend());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a const reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator crend() const noexcept
     {
         return const_reverse_iterator(cbegin());
@@ -960,7 +1189,10 @@ class basic_json
     // capacity //
     //////////////
 
-    /// checks whether the container is empty
+    /*!
+    @brief checks whether the container is empty
+    @ingroup container
+    */
     inline bool empty() const noexcept
     {
         switch (m_type)
@@ -988,7 +1220,10 @@ class basic_json
         }
     }
 
-    /// returns the number of elements
+    /*!
+    @brief returns the number of elements
+    @ingroup container
+    */
     inline size_type size() const noexcept
     {
         switch (m_type)
@@ -1016,7 +1251,10 @@ class basic_json
         }
     }
 
-    /// returns the maximum possible number of elements
+    /*!
+    @brief returns the maximum possible number of elements
+    @ingroup container
+    */
     inline size_type max_size() const noexcept
     {
         switch (m_type)
@@ -1186,7 +1424,10 @@ class basic_json
         return operator[](value.first);
     }
 
-    /// swaps the contents
+    /*!
+    @brief exchanges the values
+    @ingroup container
+    */
     inline void swap(reference other) noexcept (
         std::is_nothrow_move_constructible<value_t>::value and
         std::is_nothrow_move_assignable<value_t>::value and
@@ -1242,7 +1483,10 @@ class basic_json
     // lexicographical comparison operators //
     //////////////////////////////////////////
 
-    /// comparison: equal
+    /*!
+    @brief comparison: equal
+    @ingroup container
+    */
     friend bool operator==(const_reference lhs, const_reference rhs)
     {
         switch (lhs.type())
@@ -1316,7 +1560,10 @@ class basic_json
         return false;
     }
 
-    /// comparison: not equal
+    /*!
+    @brief comparison: not equal
+    @ingroup container
+    */
     friend bool operator!=(const_reference lhs, const_reference rhs)
     {
         return not (lhs == rhs);
@@ -1393,7 +1640,9 @@ class basic_json
             }
         }
 
-        return false;
+        // We only reach this line if we cannot compare values. In that case,
+        // we compare types.
+        return lhs.type() < rhs.type();
     }
 
     /// comparison: less than or equal
@@ -1743,19 +1992,6 @@ class basic_json
     // iterators //
     ///////////////
 
-    /// values of a generic iterator type of non-container JSON values
-    enum class generic_iterator_value
-    {
-        /// the iterator was not initialized
-        uninitialized,
-        /// the iterator points to the only value
-        begin,
-        /// the iterator points past the only value
-        end,
-        /// the iterator points to an invalid value
-        invalid
-    };
-
     /// an iterator value
     template<typename array_iterator_t, typename object_iterator_t>
     union internal_iterator
@@ -1765,21 +2001,15 @@ class basic_json
         /// iterator for JSON arrays
         array_iterator_t array_iterator;
         /// generic iteraotr for all other value types
-        generic_iterator_value generic_iterator;
+        difference_type generic_iterator;
 
         /// default constructor
-        internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
-        /// constructor for object iterators
-        internal_iterator(object_iterator_t v) : object_iterator(v) {}
-        /// constructor for array iterators
-        internal_iterator(array_iterator_t v) : array_iterator(v) {}
-        /// constructor for generic iterators
-        internal_iterator(generic_iterator_value v) : generic_iterator(v) {}
+        internal_iterator() : generic_iterator(-1) {}
     };
 
   public:
-    /// a bidirectional iterator for the basic_json class
-    class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json>
+    /// a random access iterator for the basic_json class
+    class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
     {
       public:
         /// the type of the values when the iterator is dereferenced
@@ -1813,7 +2043,7 @@ class basic_json
                 }
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::uninitialized;
+                    m_it.generic_iterator = -1;
                     break;
                 }
             }
@@ -1847,13 +2077,13 @@ class basic_json
                 case (basic_json::value_t::null):
                 {
                     // set to end so begin()==end() is true: null is empty
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::begin;
+                    m_it.generic_iterator = 0;
                     break;
                 }
             }
@@ -1878,14 +2108,14 @@ class basic_json
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
             }
         }
 
         /// return a reference to the value pointed to by the iterator
-        inline reference operator*() const
+        inline reference operator*()
         {
             switch (m_object->m_type)
             {
@@ -1906,7 +2136,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return *m_object;
                     }
@@ -1919,7 +2149,7 @@ class basic_json
         }
 
         /// dereference the iterator
-        inline pointer operator->() const
+        inline pointer operator->()
         {
             switch (m_object->m_type)
             {
@@ -1933,9 +2163,14 @@ class basic_json
                     return &*m_it.array_iterator;
                 }
 
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return m_object;
                     }
@@ -1950,7 +2185,7 @@ class basic_json
         /// post-increment (it++)
         inline iterator operator++(int)
         {
-            iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -1968,14 +2203,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator++;
                     break;
                 }
             }
@@ -2002,14 +2230,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    ++m_it.generic_iterator;
                     break;
                 }
             }
@@ -2020,7 +2241,7 @@ class basic_json
         /// post-decrement (it--)
         inline iterator operator--(int)
         {
-            iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2036,22 +2257,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator--;
                     break;
                 }
             }
@@ -2076,22 +2284,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    --m_it.generic_iterator;
                     break;
                 }
             }
@@ -2102,9 +2297,10 @@ class basic_json
         /// comparison: equal
         inline bool operator==(const iterator& other) const
         {
-            if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
             {
-                return false;
+                throw std::domain_error("cannot compare iterators of different containers");
             }
 
             switch (m_object->m_type)
@@ -2132,6 +2328,157 @@ class basic_json
             return not operator==(other);
         }
 
+        /// comparison: smaller
+        inline bool operator<(const iterator& other) const
+        {
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
+            {
+                throw std::domain_error("cannot compare iterators of different containers");
+            }
+
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator< for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return (m_it.array_iterator < other.m_it.array_iterator);
+                }
+
+                default:
+                {
+                    return (m_it.generic_iterator < other.m_it.generic_iterator);
+                }
+            }
+        }
+
+        /// comparison: less than or equal
+        inline bool operator<=(const iterator& other) const
+        {
+            return not other.operator < (*this);
+        }
+
+        /// comparison: greater than
+        inline bool operator>(const iterator& other) const
+        {
+            return not operator<=(other);
+        }
+
+        /// comparison: greater than or equal
+        inline bool operator>=(const iterator& other) const
+        {
+            return not operator<(other);
+        }
+
+        /// add to iterator
+        inline iterator& operator+=(difference_type i)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator+= for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    m_it.array_iterator += i;
+                    break;
+                }
+
+                default:
+                {
+                    m_it.generic_iterator += i;
+                    break;
+                }
+            }
+
+            return *this;
+        }
+
+        /// subtract from iterator
+        inline iterator& operator-=(difference_type i)
+        {
+            return operator+=(-i);
+        }
+
+        /// add to iterator
+        inline iterator operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        /// subtract from iterator
+        inline iterator operator-(difference_type i)
+        {
+            auto result = *this;
+            result -= i;
+            return result;
+        }
+
+        /// return difference
+        inline difference_type operator-(const iterator& other) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator- for object iterators");
+                    return 0;
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return m_it.array_iterator - other.m_it.array_iterator;
+                }
+
+                default:
+                {
+                    return m_it.generic_iterator - other.m_it.generic_iterator;
+                }
+            }
+        }
+
+        /// access to successor
+        inline reference operator[](difference_type n)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator[] for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return *(m_it.array_iterator + n);
+                }
+
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
+                default:
+                {
+                    if (m_it.generic_iterator == -n)
+                    {
+                        return *m_object;
+                    }
+                    else
+                    {
+                        throw std::out_of_range("cannot get value");
+                    }
+                }
+            }
+        }
+
       private:
         /// associated JSON instance
         pointer m_object = nullptr;
@@ -2139,8 +2486,8 @@ class basic_json
         internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
     };
 
-    /// a const bidirectional iterator for the basic_json class
-    class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json>
+    /// a const random access iterator for the basic_json class
+    class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
     {
       public:
         /// the type of the values when the iterator is dereferenced
@@ -2174,7 +2521,7 @@ class basic_json
                 }
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::uninitialized;
+                    m_it.generic_iterator = -1;
                     break;
                 }
             }
@@ -2233,13 +2580,13 @@ class basic_json
                 case (basic_json::value_t::null):
                 {
                     // set to end so begin()==end() is true: null is empty
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::begin;
+                    m_it.generic_iterator = 0;
                     break;
                 }
             }
@@ -2264,7 +2611,7 @@ class basic_json
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
             }
@@ -2292,7 +2639,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return *m_object;
                     }
@@ -2321,7 +2668,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return m_object;
                     }
@@ -2336,7 +2683,7 @@ class basic_json
         /// post-increment (it++)
         inline const_iterator operator++(int)
         {
-            const_iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2354,14 +2701,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator++;
                     break;
                 }
             }
@@ -2388,14 +2728,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    ++m_it.generic_iterator;
                     break;
                 }
             }
@@ -2406,7 +2739,7 @@ class basic_json
         /// post-decrement (it--)
         inline const_iterator operator--(int)
         {
-            const_iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2422,22 +2755,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator--;
                     break;
                 }
             }
@@ -2462,22 +2782,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    --m_it.generic_iterator;
                     break;
                 }
             }
@@ -2488,9 +2795,10 @@ class basic_json
         /// comparison: equal
         inline bool operator==(const const_iterator& other) const
         {
-            if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
             {
-                return false;
+                throw std::domain_error("cannot compare iterators of different containers");
             }
 
             switch (m_object->m_type)
@@ -2518,6 +2826,157 @@ class basic_json
             return not operator==(other);
         }
 
+        /// comparison: smaller
+        inline bool operator<(const const_iterator& other) const
+        {
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
+            {
+                throw std::domain_error("cannot compare iterators of different containers");
+            }
+
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator< for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return (m_it.array_iterator < other.m_it.array_iterator);
+                }
+
+                default:
+                {
+                    return (m_it.generic_iterator < other.m_it.generic_iterator);
+                }
+            }
+        }
+
+        /// comparison: less than or equal
+        inline bool operator<=(const const_iterator& other) const
+        {
+            return not other.operator < (*this);
+        }
+
+        /// comparison: greater than
+        inline bool operator>(const const_iterator& other) const
+        {
+            return not operator<=(other);
+        }
+
+        /// comparison: greater than or equal
+        inline bool operator>=(const const_iterator& other) const
+        {
+            return not operator<(other);
+        }
+
+        /// add to iterator
+        inline const_iterator& operator+=(difference_type i)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator+= for object iterators");
+                    break;
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    m_it.array_iterator += i;
+                    break;
+                }
+
+                default:
+                {
+                    m_it.generic_iterator += i;
+                    break;
+                }
+            }
+
+            return *this;
+        }
+
+        /// subtract from iterator
+        inline const_iterator& operator-=(difference_type i)
+        {
+            return operator+=(-i);
+        }
+
+        /// add to iterator
+        inline const_iterator operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        /// subtract from iterator
+        inline const_iterator operator-(difference_type i)
+        {
+            auto result = *this;
+            result -= i;
+            return result;
+        }
+
+        /// return difference
+        inline difference_type operator-(const const_iterator& other) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator- for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return m_it.array_iterator - other.m_it.array_iterator;
+                }
+
+                default:
+                {
+                    return m_it.generic_iterator - other.m_it.generic_iterator;
+                }
+            }
+        }
+
+        /// access to successor
+        inline reference operator[](difference_type n) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator[] for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return *(m_it.array_iterator + n);
+                }
+
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
+                default:
+                {
+                    if (m_it.generic_iterator == -n)
+                    {
+                        return *m_object;
+                    }
+                    else
+                    {
+                        throw std::out_of_range("cannot get value");
+                    }
+                }
+            }
+        }
+
       private:
         /// associated JSON instance
         pointer m_object = nullptr;
@@ -2577,7 +3036,8 @@ class basic_json
         /*!
         @brief create a string from a Unicode code point
 
-        @param codepoint  the code point (must be in [0x0, 0x10ffff]
+        @param codepoint1  the code point (can be high surrogate)
+        @param codepoint2  the code point (can be low surrogate or 0)
         @return string representation of the code point
         @exception std::out_of_range if code point is >0x10ffff
         @exception std::invalid_argument if the low surrogate is invalid
@@ -3581,6 +4041,9 @@ basic_json_parser_59:
         const lexer_char_t* m_limit = nullptr;
     };
 
+    /*!
+    @brief syntax analysis
+    */
     class parser
     {
       public:
@@ -3816,7 +4279,10 @@ using json = basic_json<>;
 // specialization of std::swap, and std::hash
 namespace std
 {
-/// swaps the values of two JSON objects
+/*!
+@brief exchanges the values of two JSON objects
+@ingroup container
+*/
 template <>
 inline void swap(nlohmann::json& j1,
                  nlohmann::json& j2) noexcept(
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index cff9b0dc..add2e5c1 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -7,6 +7,26 @@
 @see https://github.com/nlohmann/json
 */
 
+/*!
+@defgroup container Container
+@brief C++ Container concept
+
+A Container is an object used to store other objects and taking care of the
+management of the memory used by the objects it contains.
+
+@see http://en.cppreference.com/w/cpp/concept/Container
+
+@defgroup reversiblecontainer Reversible Container
+@brief C++ Reversible Container concept
+@ingroup container
+
+A ReversibleContainer is a Container that has iterators that meet the
+requirements of either BidirectionalIterator or RandomAccessIterator. Such
+iterators allow a ReversibleContainer to be iterated over in reverse.
+
+@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
+*/
+
 #ifndef _NLOHMANN_JSON
 #define _NLOHMANN_JSON
 
@@ -27,6 +47,7 @@
 #include <vector>
 
 /*!
+@brief namespace for Niels Lohmann
 @see https://github.com/nlohmann
 */
 namespace nlohmann
@@ -75,30 +96,76 @@ class basic_json
     class iterator;
     class const_iterator;
 
-    /// the type of elements in a basic_json container
+    /*!
+    @brief the type of elements in a basic_json container
+    @ingroup container
+    */
     using value_type = basic_json;
-    /// the type of an element reference
+
+    /*!
+    @brief the type of an element reference
+    @ingroup container
+    */
     using reference = basic_json&;
-    /// the type of an element const reference
+
+    /*!
+    @brief the type of an element const reference
+    @ingroup container
+    */
     using const_reference = const basic_json&;
+
+    /*!
+    @brief a type to represent differences between iterators
+    @ingroup container
+    */
+    using difference_type = std::ptrdiff_t;
+
+    /*!
+    @brief a type to represent container sizes
+    @ingroup container
+    */
+    using size_type = std::size_t;
+
+    /// the allocator type
+    using allocator_type = Allocator<basic_json>;
+
     /// the type of an element pointer
     using pointer = basic_json*;
     /// the type of an element const pointer
     using const_pointer = const basic_json*;
-    /// a type to represent differences between iterators
-    using difference_type = std::ptrdiff_t;
-    /// a type to represent container sizes
-    using size_type = std::size_t;
-    /// an iterator for a basic_json container
+
+    /*!
+    @brief an iterator for a basic_json container
+    @ingroup container
+    */
     using iterator = basic_json::iterator;
-    /// a const iterator for a basic_json container
+
+    /*!
+    @brief a const iterator for a basic_json container
+    @ingroup container
+    */
     using const_iterator = basic_json::const_iterator;
-    /// a reverse iterator for a basic_json container
+
+    /*!
+    @brief a reverse iterator for a basic_json container
+    @ingroup reversiblecontainer
+    */
     using reverse_iterator = std::reverse_iterator<iterator>;
-    /// a const reverse iterator for a basic_json container
+
+    /*!
+    @brief a const reverse iterator for a basic_json container
+    @ingroup reversiblecontainer
+    */
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
 
+    /// returns the allocator associated with the container
+    inline allocator_type get_allocator() const
+    {
+        return allocator_type();
+    }
+
+
     ///////////////////////////
     // JSON value data types //
     ///////////////////////////
@@ -166,6 +233,85 @@ class basic_json
         number_float    ///< number value (floating-point)
     };
 
+    /*!
+    @brief comparison operator for JSON value types
+
+    Returns an ordering that is similar to Python:
+    - order: null < boolean < number < object < array < string
+    - furthermore, each type is not smaller than itself
+    */
+    friend bool operator<(const value_t lhs, const value_t rhs)
+    {
+        // no type is smaller than itself
+        if (lhs == rhs)
+        {
+            return false;
+        }
+
+        switch (lhs)
+        {
+            case (value_t::null):
+            {
+                // nulls are smaller than all other types
+                return true;
+            }
+
+            case (value_t::boolean):
+            {
+                // only nulls are smaller than booleans
+                return (rhs != value_t::null);
+            }
+
+            case (value_t::number_float):
+            case (value_t::number_integer):
+            {
+                switch (rhs)
+                {
+                    // numbers are smaller than objects, arrays, and string
+                    case (value_t::object):
+                    case (value_t::array):
+                    case (value_t::string):
+                    {
+                        return true;
+                    }
+                    default:
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            case (value_t::object):
+            {
+                switch (rhs)
+                {
+                    // objects are smaller than arrays and string
+                    case (value_t::array):
+                    case (value_t::string):
+                    {
+                        return true;
+                    }
+                    default:
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            case (value_t::array):
+            {
+                // arrays are smaller than strings
+                return (rhs == value_t::string);
+            }
+
+            default:
+            {
+                // a string is not smaller than any other types
+                return false;
+            }
+        }
+    }
+
 
     //////////////////
     // constructors //
@@ -226,10 +372,11 @@ class basic_json
         }
     }
 
-    /// create a null object (implicitly)
-    inline basic_json() noexcept
-        : m_type(value_t::null)
-    {}
+    /*!
+    @brief create a null object (implicitly)
+    @ingroup container
+    */
+    inline basic_json() noexcept = default;
 
     /// create a null object (explicitly)
     inline basic_json(std::nullptr_t) noexcept
@@ -424,11 +571,15 @@ class basic_json
         return basic_json(l, false, value_t::object);
     }
 
+
     ///////////////////////////////////////
     // other constructors and destructor //
     ///////////////////////////////////////
 
-    /// copy constructor
+    /*!
+    @brief copy constructor
+    @ingroup container
+    */
     inline basic_json(const basic_json& other)
         : m_type(other.m_type)
     {
@@ -487,7 +638,10 @@ class basic_json
         other.m_value = {};
     }
 
-    /// copy assignment
+    /*!
+    @brief copy assignment
+    @ingroup container
+    */
     inline reference& operator=(basic_json other) noexcept (
         std::is_nothrow_move_constructible<value_t>::value and
         std::is_nothrow_move_assignable<value_t>::value and
@@ -500,7 +654,10 @@ class basic_json
         return *this;
     }
 
-    /// destructor
+    /*!
+    @brief destructor
+    @ingroup container
+    */
     inline ~basic_json() noexcept
     {
         switch (m_type)
@@ -577,6 +734,42 @@ class basic_json
         return m_type;
     }
 
+    // return whether value is null
+    inline bool is_null() const noexcept
+    {
+        return m_type == value_t::null;
+    }
+
+    // return whether value is boolean
+    inline bool is_boolean() const noexcept
+    {
+        return m_type == value_t::boolean;
+    }
+
+    // return whether value is number
+    inline bool is_number() const noexcept
+    {
+        return (m_type == value_t::number_integer) or (m_type == value_t::number_float);
+    }
+
+    // return whether value is object
+    inline bool is_object() const noexcept
+    {
+        return m_type == value_t::object;
+    }
+
+    // return whether value is array
+    inline bool is_array() const noexcept
+    {
+        return m_type == value_t::array;
+    }
+
+    // return whether value is string
+    inline bool is_string() const noexcept
+    {
+        return m_type == value_t::string;
+    }
+
     /// return the type of the object (implicit)
     operator value_t() const noexcept
     {
@@ -871,7 +1064,10 @@ class basic_json
     // iterators //
     ///////////////
 
-    /// returns an iterator to the beginning of the container
+    /*!
+    @brief returns an iterator to the first element
+    @ingroup container
+    */
     inline iterator begin() noexcept
     {
         iterator result(this);
@@ -879,7 +1075,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the beginning of the container
+    /*!
+    @brief returns a const iterator to the first element
+    @ingroup container
+    */
     inline const_iterator begin() const noexcept
     {
         const_iterator result(this);
@@ -887,7 +1086,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the beginning of the container
+    /*!
+    @brief returns a const iterator to the first element
+    @ingroup container
+    */
     inline const_iterator cbegin() const noexcept
     {
         const_iterator result(this);
@@ -895,7 +1097,10 @@ class basic_json
         return result;
     }
 
-    /// returns an iterator to the end of the container
+    /*!
+    @brief returns an iterator to one past the last element
+    @ingroup container
+    */
     inline iterator end() noexcept
     {
         iterator result(this);
@@ -903,7 +1108,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the end of the container
+    /*!
+    @brief returns a const iterator to one past the last element
+    @ingroup container
+    */
     inline const_iterator end() const noexcept
     {
         const_iterator result(this);
@@ -911,7 +1119,10 @@ class basic_json
         return result;
     }
 
-    /// returns a const iterator to the end of the container
+    /*!
+    @brief returns a const iterator to one past the last element
+    @ingroup container
+    */
     inline const_iterator cend() const noexcept
     {
         const_iterator result(this);
@@ -919,37 +1130,55 @@ class basic_json
         return result;
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline reverse_iterator rbegin() noexcept
     {
         return reverse_iterator(end());
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a const reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator rbegin() const noexcept
     {
         return const_reverse_iterator(end());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline reverse_iterator rend() noexcept
     {
         return reverse_iterator(begin());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a const reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator rend() const noexcept
     {
         return const_reverse_iterator(begin());
     }
 
-    /// returns a reverse iterator to the beginning
+    /*!
+    @brief returns a const reverse iterator to the first element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator crbegin() const noexcept
     {
         return const_reverse_iterator(cend());
     }
 
-    /// returns a reverse iterator to the end
+    /*!
+    @brief returns a const reverse iterator to one past the last element
+    @ingroup reversiblecontainer
+    */
     inline const_reverse_iterator crend() const noexcept
     {
         return const_reverse_iterator(cbegin());
@@ -960,7 +1189,10 @@ class basic_json
     // capacity //
     //////////////
 
-    /// checks whether the container is empty
+    /*!
+    @brief checks whether the container is empty
+    @ingroup container
+    */
     inline bool empty() const noexcept
     {
         switch (m_type)
@@ -988,7 +1220,10 @@ class basic_json
         }
     }
 
-    /// returns the number of elements
+    /*!
+    @brief returns the number of elements
+    @ingroup container
+    */
     inline size_type size() const noexcept
     {
         switch (m_type)
@@ -1016,7 +1251,10 @@ class basic_json
         }
     }
 
-    /// returns the maximum possible number of elements
+    /*!
+    @brief returns the maximum possible number of elements
+    @ingroup container
+    */
     inline size_type max_size() const noexcept
     {
         switch (m_type)
@@ -1186,7 +1424,10 @@ class basic_json
         return operator[](value.first);
     }
 
-    /// swaps the contents
+    /*!
+    @brief exchanges the values
+    @ingroup container
+    */
     inline void swap(reference other) noexcept (
         std::is_nothrow_move_constructible<value_t>::value and
         std::is_nothrow_move_assignable<value_t>::value and
@@ -1242,7 +1483,10 @@ class basic_json
     // lexicographical comparison operators //
     //////////////////////////////////////////
 
-    /// comparison: equal
+    /*!
+    @brief comparison: equal
+    @ingroup container
+    */
     friend bool operator==(const_reference lhs, const_reference rhs)
     {
         switch (lhs.type())
@@ -1316,7 +1560,10 @@ class basic_json
         return false;
     }
 
-    /// comparison: not equal
+    /*!
+    @brief comparison: not equal
+    @ingroup container
+    */
     friend bool operator!=(const_reference lhs, const_reference rhs)
     {
         return not (lhs == rhs);
@@ -1393,7 +1640,9 @@ class basic_json
             }
         }
 
-        return false;
+        // We only reach this line if we cannot compare values. In that case,
+        // we compare types.
+        return lhs.type() < rhs.type();
     }
 
     /// comparison: less than or equal
@@ -1743,19 +1992,6 @@ class basic_json
     // iterators //
     ///////////////
 
-    /// values of a generic iterator type of non-container JSON values
-    enum class generic_iterator_value
-    {
-        /// the iterator was not initialized
-        uninitialized,
-        /// the iterator points to the only value
-        begin,
-        /// the iterator points past the only value
-        end,
-        /// the iterator points to an invalid value
-        invalid
-    };
-
     /// an iterator value
     template<typename array_iterator_t, typename object_iterator_t>
     union internal_iterator
@@ -1765,21 +2001,15 @@ class basic_json
         /// iterator for JSON arrays
         array_iterator_t array_iterator;
         /// generic iteraotr for all other value types
-        generic_iterator_value generic_iterator;
+        difference_type generic_iterator;
 
         /// default constructor
-        internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
-        /// constructor for object iterators
-        internal_iterator(object_iterator_t v) : object_iterator(v) {}
-        /// constructor for array iterators
-        internal_iterator(array_iterator_t v) : array_iterator(v) {}
-        /// constructor for generic iterators
-        internal_iterator(generic_iterator_value v) : generic_iterator(v) {}
+        internal_iterator() : generic_iterator(-1) {}
     };
 
   public:
-    /// a bidirectional iterator for the basic_json class
-    class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json>
+    /// a random access iterator for the basic_json class
+    class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
     {
       public:
         /// the type of the values when the iterator is dereferenced
@@ -1813,7 +2043,7 @@ class basic_json
                 }
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::uninitialized;
+                    m_it.generic_iterator = -1;
                     break;
                 }
             }
@@ -1847,13 +2077,13 @@ class basic_json
                 case (basic_json::value_t::null):
                 {
                     // set to end so begin()==end() is true: null is empty
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::begin;
+                    m_it.generic_iterator = 0;
                     break;
                 }
             }
@@ -1878,14 +2108,14 @@ class basic_json
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
             }
         }
 
         /// return a reference to the value pointed to by the iterator
-        inline reference operator*() const
+        inline reference operator*()
         {
             switch (m_object->m_type)
             {
@@ -1906,7 +2136,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return *m_object;
                     }
@@ -1919,7 +2149,7 @@ class basic_json
         }
 
         /// dereference the iterator
-        inline pointer operator->() const
+        inline pointer operator->()
         {
             switch (m_object->m_type)
             {
@@ -1933,9 +2163,14 @@ class basic_json
                     return &*m_it.array_iterator;
                 }
 
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return m_object;
                     }
@@ -1950,7 +2185,7 @@ class basic_json
         /// post-increment (it++)
         inline iterator operator++(int)
         {
-            iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -1968,14 +2203,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator++;
                     break;
                 }
             }
@@ -2002,14 +2230,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    ++m_it.generic_iterator;
                     break;
                 }
             }
@@ -2020,7 +2241,7 @@ class basic_json
         /// post-decrement (it--)
         inline iterator operator--(int)
         {
-            iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2036,22 +2257,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator--;
                     break;
                 }
             }
@@ -2076,22 +2284,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    --m_it.generic_iterator;
                     break;
                 }
             }
@@ -2102,9 +2297,10 @@ class basic_json
         /// comparison: equal
         inline bool operator==(const iterator& other) const
         {
-            if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
             {
-                return false;
+                throw std::domain_error("cannot compare iterators of different containers");
             }
 
             switch (m_object->m_type)
@@ -2132,6 +2328,157 @@ class basic_json
             return not operator==(other);
         }
 
+        /// comparison: smaller
+        inline bool operator<(const iterator& other) const
+        {
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
+            {
+                throw std::domain_error("cannot compare iterators of different containers");
+            }
+
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator< for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return (m_it.array_iterator < other.m_it.array_iterator);
+                }
+
+                default:
+                {
+                    return (m_it.generic_iterator < other.m_it.generic_iterator);
+                }
+            }
+        }
+
+        /// comparison: less than or equal
+        inline bool operator<=(const iterator& other) const
+        {
+            return not other.operator < (*this);
+        }
+
+        /// comparison: greater than
+        inline bool operator>(const iterator& other) const
+        {
+            return not operator<=(other);
+        }
+
+        /// comparison: greater than or equal
+        inline bool operator>=(const iterator& other) const
+        {
+            return not operator<(other);
+        }
+
+        /// add to iterator
+        inline iterator& operator+=(difference_type i)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator+= for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    m_it.array_iterator += i;
+                    break;
+                }
+
+                default:
+                {
+                    m_it.generic_iterator += i;
+                    break;
+                }
+            }
+
+            return *this;
+        }
+
+        /// subtract from iterator
+        inline iterator& operator-=(difference_type i)
+        {
+            return operator+=(-i);
+        }
+
+        /// add to iterator
+        inline iterator operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        /// subtract from iterator
+        inline iterator operator-(difference_type i)
+        {
+            auto result = *this;
+            result -= i;
+            return result;
+        }
+
+        /// return difference
+        inline difference_type operator-(const iterator& other) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator- for object iterators");
+                    return 0;
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return m_it.array_iterator - other.m_it.array_iterator;
+                }
+
+                default:
+                {
+                    return m_it.generic_iterator - other.m_it.generic_iterator;
+                }
+            }
+        }
+
+        /// access to successor
+        inline reference operator[](difference_type n)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator[] for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return *(m_it.array_iterator + n);
+                }
+
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
+                default:
+                {
+                    if (m_it.generic_iterator == -n)
+                    {
+                        return *m_object;
+                    }
+                    else
+                    {
+                        throw std::out_of_range("cannot get value");
+                    }
+                }
+            }
+        }
+
       private:
         /// associated JSON instance
         pointer m_object = nullptr;
@@ -2139,8 +2486,8 @@ class basic_json
         internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
     };
 
-    /// a const bidirectional iterator for the basic_json class
-    class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json>
+    /// a const random access iterator for the basic_json class
+    class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
     {
       public:
         /// the type of the values when the iterator is dereferenced
@@ -2174,7 +2521,7 @@ class basic_json
                 }
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::uninitialized;
+                    m_it.generic_iterator = -1;
                     break;
                 }
             }
@@ -2233,13 +2580,13 @@ class basic_json
                 case (basic_json::value_t::null):
                 {
                     // set to end so begin()==end() is true: null is empty
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::begin;
+                    m_it.generic_iterator = 0;
                     break;
                 }
             }
@@ -2264,7 +2611,7 @@ class basic_json
 
                 default:
                 {
-                    m_it.generic_iterator = generic_iterator_value::end;
+                    m_it.generic_iterator = 1;
                     break;
                 }
             }
@@ -2292,7 +2639,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return *m_object;
                     }
@@ -2321,7 +2668,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
+                    if (m_it.generic_iterator == 0)
                     {
                         return m_object;
                     }
@@ -2336,7 +2683,7 @@ class basic_json
         /// post-increment (it++)
         inline const_iterator operator++(int)
         {
-            const_iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2354,14 +2701,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator++;
                     break;
                 }
             }
@@ -2388,14 +2728,7 @@ class basic_json
 
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::begin)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::end;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    ++m_it.generic_iterator;
                     break;
                 }
             }
@@ -2406,7 +2739,7 @@ class basic_json
         /// post-decrement (it--)
         inline const_iterator operator--(int)
         {
-            const_iterator result = *this;
+            auto result = *this;
 
             switch (m_object->m_type)
             {
@@ -2422,22 +2755,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    m_it.generic_iterator--;
                     break;
                 }
             }
@@ -2462,22 +2782,9 @@ class basic_json
                     break;
                 }
 
-                case (basic_json::value_t::null):
-                {
-                    m_it.generic_iterator = generic_iterator_value::invalid;
-                    break;
-                }
-
                 default:
                 {
-                    if (m_it.generic_iterator == generic_iterator_value::end)
-                    {
-                        m_it.generic_iterator = generic_iterator_value::begin;
-                    }
-                    else
-                    {
-                        m_it.generic_iterator = generic_iterator_value::invalid;
-                    }
+                    --m_it.generic_iterator;
                     break;
                 }
             }
@@ -2488,9 +2795,10 @@ class basic_json
         /// comparison: equal
         inline bool operator==(const const_iterator& other) const
         {
-            if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
             {
-                return false;
+                throw std::domain_error("cannot compare iterators of different containers");
             }
 
             switch (m_object->m_type)
@@ -2518,6 +2826,157 @@ class basic_json
             return not operator==(other);
         }
 
+        /// comparison: smaller
+        inline bool operator<(const const_iterator& other) const
+        {
+            // if objects are not the same, the comparison is undefined
+            if (m_object != other.m_object)
+            {
+                throw std::domain_error("cannot compare iterators of different containers");
+            }
+
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator< for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return (m_it.array_iterator < other.m_it.array_iterator);
+                }
+
+                default:
+                {
+                    return (m_it.generic_iterator < other.m_it.generic_iterator);
+                }
+            }
+        }
+
+        /// comparison: less than or equal
+        inline bool operator<=(const const_iterator& other) const
+        {
+            return not other.operator < (*this);
+        }
+
+        /// comparison: greater than
+        inline bool operator>(const const_iterator& other) const
+        {
+            return not operator<=(other);
+        }
+
+        /// comparison: greater than or equal
+        inline bool operator>=(const const_iterator& other) const
+        {
+            return not operator<(other);
+        }
+
+        /// add to iterator
+        inline const_iterator& operator+=(difference_type i)
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator+= for object iterators");
+                    break;
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    m_it.array_iterator += i;
+                    break;
+                }
+
+                default:
+                {
+                    m_it.generic_iterator += i;
+                    break;
+                }
+            }
+
+            return *this;
+        }
+
+        /// subtract from iterator
+        inline const_iterator& operator-=(difference_type i)
+        {
+            return operator+=(-i);
+        }
+
+        /// add to iterator
+        inline const_iterator operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        /// subtract from iterator
+        inline const_iterator operator-(difference_type i)
+        {
+            auto result = *this;
+            result -= i;
+            return result;
+        }
+
+        /// return difference
+        inline difference_type operator-(const const_iterator& other) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator- for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return m_it.array_iterator - other.m_it.array_iterator;
+                }
+
+                default:
+                {
+                    return m_it.generic_iterator - other.m_it.generic_iterator;
+                }
+            }
+        }
+
+        /// access to successor
+        inline reference operator[](difference_type n) const
+        {
+            switch (m_object->m_type)
+            {
+                case (basic_json::value_t::object):
+                {
+                    throw std::domain_error("cannot use operator[] for object iterators");
+                }
+
+                case (basic_json::value_t::array):
+                {
+                    return *(m_it.array_iterator + n);
+                }
+
+                case (basic_json::value_t::null):
+                {
+                    throw std::out_of_range("cannot get value");
+                }
+
+                default:
+                {
+                    if (m_it.generic_iterator == -n)
+                    {
+                        return *m_object;
+                    }
+                    else
+                    {
+                        throw std::out_of_range("cannot get value");
+                    }
+                }
+            }
+        }
+
       private:
         /// associated JSON instance
         pointer m_object = nullptr;
@@ -2577,7 +3036,8 @@ class basic_json
         /*!
         @brief create a string from a Unicode code point
 
-        @param codepoint  the code point (must be in [0x0, 0x10ffff]
+        @param codepoint1  the code point (can be high surrogate)
+        @param codepoint2  the code point (can be low surrogate or 0)
         @return string representation of the code point
         @exception std::out_of_range if code point is >0x10ffff
         @exception std::invalid_argument if the low surrogate is invalid
@@ -2930,6 +3390,9 @@ class basic_json
         const lexer_char_t* m_limit = nullptr;
     };
 
+    /*!
+    @brief syntax analysis
+    */
     class parser
     {
       public:
@@ -3165,7 +3628,10 @@ using json = basic_json<>;
 // specialization of std::swap, and std::hash
 namespace std
 {
-/// swaps the values of two JSON objects
+/*!
+@brief exchanges the values of two JSON objects
+@ingroup container
+*/
 template <>
 inline void swap(nlohmann::json& j1,
                  nlohmann::json& j2) noexcept(
diff --git a/test/unit.cpp b/test/unit.cpp
index 19d0f7d5..6d0904bf 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -1050,6 +1050,86 @@ TEST_CASE("other constructors and destructor")
 
 TEST_CASE("object inspection")
 {
+    SECTION("convenience type checker")
+    {
+        SECTION("object")
+        {
+            json j {{"foo", 1}, {"bar", false}};
+            CHECK(not j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(not j.is_number());
+            CHECK(j.is_object());
+            CHECK(not j.is_array());
+            CHECK(not j.is_string());
+        }
+
+        SECTION("array")
+        {
+            json j {"foo", 1, 42.23, false};
+            CHECK(not j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(not j.is_number());
+            CHECK(not j.is_object());
+            CHECK(j.is_array());
+            CHECK(not j.is_string());
+        }
+
+        SECTION("null")
+        {
+            json j(nullptr);
+            CHECK(j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(not j.is_number());
+            CHECK(not j.is_object());
+            CHECK(not j.is_array());
+            CHECK(not j.is_string());
+        }
+
+        SECTION("boolean")
+        {
+            json j(true);
+            CHECK(not j.is_null());
+            CHECK(j.is_boolean());
+            CHECK(not j.is_number());
+            CHECK(not j.is_object());
+            CHECK(not j.is_array());
+            CHECK(not j.is_string());
+        }
+
+        SECTION("string")
+        {
+            json j("Hello world");
+            CHECK(not j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(not j.is_number());
+            CHECK(not j.is_object());
+            CHECK(not j.is_array());
+            CHECK(j.is_string());
+        }
+
+        SECTION("number (integer)")
+        {
+            json j(42);
+            CHECK(not j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(j.is_number());
+            CHECK(not j.is_object());
+            CHECK(not j.is_array());
+            CHECK(not j.is_string());
+        }
+
+        SECTION("number (floating-point)")
+        {
+            json j(42.23);
+            CHECK(not j.is_null());
+            CHECK(not j.is_boolean());
+            CHECK(j.is_number());
+            CHECK(not j.is_object());
+            CHECK(not j.is_array());
+            CHECK(not j.is_string());
+        }
+    }
+
     SECTION("serialization")
     {
         json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
@@ -2366,1135 +2446,1501 @@ TEST_CASE("element access")
 
 TEST_CASE("iterators")
 {
-    SECTION("uninitialized")
+    SECTION("basic behavior")
     {
-        json::iterator it;
-        CHECK(it.m_object == nullptr);
-
-        json::const_iterator cit;
-        CHECK(cit.m_object == nullptr);
-    }
-
-    SECTION("boolean")
-    {
-        json j = true;
-        json j_const(j);
-
-        SECTION("json + begin/end")
+        SECTION("uninitialized")
         {
-            json::iterator it = j.begin();
-            CHECK(it != j.end());
-            CHECK(*it == j);
+            json::iterator it;
+            CHECK(it.m_object == nullptr);
 
-            it++;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            it--;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            --it;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
+            json::const_iterator cit;
+            CHECK(cit.m_object == nullptr);
         }
 
-        SECTION("const json + begin/end")
+        SECTION("boolean")
         {
-            json::const_iterator it = j_const.begin();
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
+            json j = true;
+            json j_const(j);
 
-            it++;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
+            SECTION("json + begin/end")
+            {
+                json::iterator it = j.begin();
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            it--;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
+                it++;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
 
-            ++it;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
+                it--;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            --it;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
+                ++it;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
+
+                --it;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it = j_const.begin();
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                it--;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                --it;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it = j.cbegin();
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                it--;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                --it;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it = j_const.cbegin();
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                it--;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                --it;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it = j.rbegin();
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                it--;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                --it;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j.crbegin();
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                it--;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                --it;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j_const.crbegin();
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                it--;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                --it;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+            }
         }
 
-        SECTION("json + cbegin/cend")
+        SECTION("string")
         {
-            json::const_iterator it = j.cbegin();
-            CHECK(it != j.cend());
-            CHECK(*it == j);
+            json j = "hello world";
+            json j_const(j);
 
-            it++;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
+            SECTION("json + begin/end")
+            {
+                json::iterator it = j.begin();
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            it--;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
+                it++;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
 
-            ++it;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
+                it--;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            --it;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
+                ++it;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
+
+                --it;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it = j_const.begin();
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                it--;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                --it;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it = j.cbegin();
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                it--;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                --it;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it = j_const.cbegin();
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                it--;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                --it;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it = j.rbegin();
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                it--;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                --it;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j.crbegin();
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                it--;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                --it;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j_const.crbegin();
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                it--;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                --it;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+            }
         }
 
-        SECTION("const json + cbegin/cend")
+        SECTION("array")
         {
-            json::const_iterator it = j_const.cbegin();
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
+            json j = {1, 2, 3};
+            json j_const(j);
 
-            it++;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
+            SECTION("json + begin/end")
+            {
+                json::iterator it_begin = j.begin();
+                json::iterator it_end = j.end();
 
-            it--;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
 
-            ++it;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
 
-            --it;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it_begin = j_const.begin();
+                json::const_iterator it_end = j_const.end();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j_const[0]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const[2]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j.cbegin();
+                json::const_iterator it_end = j.cend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j_const.cbegin();
+                json::const_iterator it_end = j_const.cend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it_begin = j.rbegin();
+                json::reverse_iterator it_end = j.rend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it_begin = j.crbegin();
+                json::const_reverse_iterator it_end = j.crend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it_begin = j_const.crbegin();
+                json::const_reverse_iterator it_end = j_const.crend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j[2]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[1]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j[0]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
         }
 
-        SECTION("json + rbegin/rend")
+        SECTION("object")
         {
-            json::reverse_iterator it = j.rbegin();
-            CHECK(it != j.rend());
-            CHECK(*it == j);
+            json j = {{"A", 1}, {"B", 2}, {"C", 3}};
+            json j_const(j);
 
-            it++;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
+            SECTION("json + begin/end")
+            {
+                json::iterator it_begin = j.begin();
+                json::iterator it_end = j.end();
 
-            it--;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j["A"]);
 
-            ++it;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["B"]);
 
-            --it;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["C"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it_begin = j_const.begin();
+                json::const_iterator it_end = j_const.end();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j_const["A"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const["C"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j.cbegin();
+                json::const_iterator it_end = j.cend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j["A"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["C"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j_const.cbegin();
+                json::const_iterator it_end = j_const.cend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j_const["A"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j_const["C"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it_begin = j.rbegin();
+                json::reverse_iterator it_end = j.rend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j["C"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["A"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it_begin = j.crbegin();
+                json::const_reverse_iterator it_end = j.crend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j["C"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["A"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it_begin = j_const.crbegin();
+                json::const_reverse_iterator it_end = j_const.crend();
+
+                auto it = it_begin;
+                CHECK(it != it_end);
+                CHECK(*it == j["C"]);
+
+                it++;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["B"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it != it_end);
+                CHECK(*it == j["A"]);
+
+                ++it;
+                CHECK(it != it_begin);
+                CHECK(it == it_end);
+            }
         }
 
-        SECTION("json + crbegin/crend")
+        SECTION("number (integer)")
         {
-            json::const_reverse_iterator it = j.crbegin();
-            CHECK(it != j.crend());
-            CHECK(*it == j);
+            json j = 23;
+            json j_const(j);
 
-            it++;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
+            SECTION("json + begin/end")
+            {
+                json::iterator it = j.begin();
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            it--;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
+                it++;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
 
-            ++it;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
+                it--;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            --it;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
+                ++it;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
+
+                --it;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it = j_const.begin();
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                it--;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                --it;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it = j.cbegin();
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                it--;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                --it;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it = j_const.cbegin();
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                it--;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                --it;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it = j.rbegin();
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                it--;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                --it;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j.crbegin();
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                it--;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                --it;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j_const.crbegin();
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                it--;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                --it;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+            }
         }
 
-        SECTION("const json + crbegin/crend")
+        SECTION("number (float)")
         {
-            json::const_reverse_iterator it = j_const.crbegin();
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
+            json j = 23.42;
+            json j_const(j);
 
-            it++;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
+            SECTION("json + begin/end")
+            {
+                json::iterator it = j.begin();
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            it--;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
+                it++;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
 
-            ++it;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
+                it--;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
 
-            --it;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
+                ++it;
+                CHECK(it != j.begin());
+                CHECK(it == j.end());
+
+                --it;
+                CHECK(it == j.begin());
+                CHECK(it != j.end());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it = j_const.begin();
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                it--;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.begin());
+                CHECK(it == j_const.end());
+
+                --it;
+                CHECK(it == j_const.begin());
+                CHECK(it != j_const.end());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it = j.cbegin();
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                it--;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.cbegin());
+                CHECK(it == j.cend());
+
+                --it;
+                CHECK(it == j.cbegin());
+                CHECK(it != j.cend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it = j_const.cbegin();
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                it--;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.cbegin());
+                CHECK(it == j_const.cend());
+
+                --it;
+                CHECK(it == j_const.cbegin());
+                CHECK(it != j_const.cend());
+                CHECK(*it == j_const);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it = j.rbegin();
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                it--;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.rbegin());
+                CHECK(it == j.rend());
+
+                --it;
+                CHECK(it == j.rbegin());
+                CHECK(it != j.rend());
+                CHECK(*it == j);
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j.crbegin();
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                it++;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                it--;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+
+                ++it;
+                CHECK(it != j.crbegin());
+                CHECK(it == j.crend());
+
+                --it;
+                CHECK(it == j.crbegin());
+                CHECK(it != j.crend());
+                CHECK(*it == j);
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j_const.crbegin();
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                it++;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                it--;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+
+                ++it;
+                CHECK(it != j_const.crbegin());
+                CHECK(it == j_const.crend());
+
+                --it;
+                CHECK(it == j_const.crbegin());
+                CHECK(it != j_const.crend());
+                CHECK(*it == j_const);
+            }
+        }
+
+        SECTION("null")
+        {
+            json j = nullptr;
+            json j_const(j);
+
+            SECTION("json + begin/end")
+            {
+                json::iterator it = j.begin();
+                CHECK(it == j.end());
+            }
+
+            SECTION("const json + begin/end")
+            {
+                json::const_iterator it_begin = j_const.begin();
+                json::const_iterator it_end = j_const.end();
+                CHECK(it_begin == it_end);
+            }
+
+            SECTION("json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j.cbegin();
+                json::const_iterator it_end = j.cend();
+                CHECK(it_begin == it_end);
+            }
+
+            SECTION("const json + cbegin/cend")
+            {
+                json::const_iterator it_begin = j_const.cbegin();
+                json::const_iterator it_end = j_const.cend();
+                CHECK(it_begin == it_end);
+            }
+
+            SECTION("json + rbegin/rend")
+            {
+                json::reverse_iterator it = j.rbegin();
+                CHECK(it == j.rend());
+            }
+
+            SECTION("json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j.crbegin();
+                CHECK(it == j.crend());
+            }
+
+            SECTION("const json + crbegin/crend")
+            {
+                json::const_reverse_iterator it = j_const.crbegin();
+                CHECK(it == j_const.crend());
+            }
         }
     }
 
-    SECTION("string")
+    SECTION("iterator comparisons")
     {
-        json j = "hello world";
-        json j_const(j);
+        json j_values = {nullptr, true, 42, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
 
-        SECTION("json + begin/end")
+        for (json& j : j_values)
         {
-            json::iterator it = j.begin();
-            CHECK(it != j.end());
-            CHECK(*it == j);
+            auto it1 = j.begin();
+            auto it2 = j.begin();
+            auto it3 = j.begin();
+            ++it2;
+            ++it3;
+            ++it3;
+            auto it1_c = j.cbegin();
+            auto it2_c = j.cbegin();
+            auto it3_c = j.cbegin();
+            ++it2_c;
+            ++it3_c;
+            ++it3_c;
 
-            it++;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
+            // comparison: equal
+            {
+                CHECK(it1 == it1);
+                CHECK(not (it1 == it2));
+                CHECK(not (it1 == it3));
+                CHECK(not (it2 == it3));
+                CHECK(it1_c == it1_c);
+                CHECK(not (it1_c == it2_c));
+                CHECK(not (it1_c == it3_c));
+                CHECK(not (it2_c == it3_c));
+            }
 
-            it--;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
+            // comparison: not equal
+            {
+                // check definition
+                CHECK( (it1 != it1) == not(it1 == it1) );
+                CHECK( (it1 != it2) == not(it1 == it2) );
+                CHECK( (it1 != it3) == not(it1 == it3) );
+                CHECK( (it2 != it3) == not(it2 == it3) );
+                CHECK( (it1_c != it1_c) == not(it1_c == it1_c) );
+                CHECK( (it1_c != it2_c) == not(it1_c == it2_c) );
+                CHECK( (it1_c != it3_c) == not(it1_c == it3_c) );
+                CHECK( (it2_c != it3_c) == not(it2_c == it3_c) );
+            }
 
-            ++it;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
+            // comparison: smaller
+            {
+                if (j.type() == json::value_t::object)
+                {
+                    CHECK_THROWS_AS(it1 < it1, std::domain_error);
+                    CHECK_THROWS_AS(it1 < it2, std::domain_error);
+                    CHECK_THROWS_AS(it2 < it3, std::domain_error);
+                    CHECK_THROWS_AS(it1 < it3, std::domain_error);
+                    CHECK_THROWS_AS(it1_c < it1_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c < it2_c, std::domain_error);
+                    CHECK_THROWS_AS(it2_c < it3_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c < it3_c, std::domain_error);
+                }
+                else
+                {
+                    CHECK(not (it1 < it1));
+                    CHECK(it1 < it2);
+                    CHECK(it1 < it3);
+                    CHECK(it2 < it3);
+                    CHECK(not (it1_c < it1_c));
+                    CHECK(it1_c < it2_c);
+                    CHECK(it1_c < it3_c);
+                    CHECK(it2_c < it3_c);
+                }
+            }
 
-            --it;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
+            // comparison: less than or equal
+            {
+                if (j.type() == json::value_t::object)
+                {
+                    CHECK_THROWS_AS(it1 <= it1, std::domain_error);
+                    CHECK_THROWS_AS(it1 <= it2, std::domain_error);
+                    CHECK_THROWS_AS(it2 <= it3, std::domain_error);
+                    CHECK_THROWS_AS(it1 <= it3, std::domain_error);
+                    CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error);
+                    CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error);
+                }
+                else
+                {
+                    // check definition
+                    CHECK( (it1 <= it1) == not(it1 < it1) );
+                    CHECK( (it1 <= it2) == not(it2 < it1) );
+                    CHECK( (it1 <= it3) == not(it3 < it1) );
+                    CHECK( (it2 <= it3) == not(it3 < it2) );
+                    CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) );
+                    CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) );
+                    CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) );
+                    CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) );
+                }
+            }
+
+            // comparison: greater than
+            {
+                if (j.type() == json::value_t::object)
+                {
+                    CHECK_THROWS_AS(it1 > it1, std::domain_error);
+                    CHECK_THROWS_AS(it1 > it2, std::domain_error);
+                    CHECK_THROWS_AS(it2 > it3, std::domain_error);
+                    CHECK_THROWS_AS(it1 > it3, std::domain_error);
+                    CHECK_THROWS_AS(it1_c > it1_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c > it2_c, std::domain_error);
+                    CHECK_THROWS_AS(it2_c > it3_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c > it3_c, std::domain_error);
+                }
+                else
+                {
+                    // check definition
+                    CHECK( (it1 > it1) == (it1 < it1) );
+                    CHECK( (it1 > it2) == (it2 < it1) );
+                    CHECK( (it1 > it3) == (it3 < it1) );
+                    CHECK( (it2 > it3) == (it3 < it2) );
+                    CHECK( (it1_c > it1_c) == (it1_c < it1_c) );
+                    CHECK( (it1_c > it2_c) == (it2_c < it1_c) );
+                    CHECK( (it1_c > it3_c) == (it3_c < it1_c) );
+                    CHECK( (it2_c > it3_c) == (it3_c < it2_c) );
+                }
+            }
+
+            // comparison: greater than or equal
+            {
+                if (j.type() == json::value_t::object)
+                {
+                    CHECK_THROWS_AS(it1 >= it1, std::domain_error);
+                    CHECK_THROWS_AS(it1 >= it2, std::domain_error);
+                    CHECK_THROWS_AS(it2 >= it3, std::domain_error);
+                    CHECK_THROWS_AS(it1 >= it3, std::domain_error);
+                    CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error);
+                    CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error);
+                    CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error);
+                }
+                else
+                {
+                    // check definition
+                    CHECK( (it1 >= it1) == not(it1 < it1) );
+                    CHECK( (it1 >= it2) == not(it1 < it2) );
+                    CHECK( (it1 >= it3) == not(it1 < it3) );
+                    CHECK( (it2 >= it3) == not(it2 < it3) );
+                    CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) );
+                    CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) );
+                    CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) );
+                    CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) );
+                }
+            }
         }
 
-        SECTION("const json + begin/end")
+        // check exceptions if different objects are compared
+        for (auto j : j_values)
         {
-            json::const_iterator it = j_const.begin();
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
+            for (auto k : j_values)
+            {
+                if (j != k)
+                {
+                    CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error);
+                    CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error);
 
-            it++;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            it--;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            --it;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it = j.cbegin();
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            it--;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            --it;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it = j_const.cbegin();
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            it--;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            --it;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it = j.rbegin();
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            it--;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            --it;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j.crbegin();
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            it--;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            --it;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j_const.crbegin();
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            it--;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            --it;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
+                    CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error);
+                    CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error);
+                }
+            }
         }
     }
 
-    SECTION("array")
+    SECTION("iterator arithmetic")
     {
-        json j = {1, 2, 3};
-        json j_const(j);
+        json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
+        json j_array = {1, 2, 3, 4, 5, 6};
+        json j_null = nullptr;
+        json j_value = 42;
 
-        SECTION("json + begin/end")
+        SECTION("addition and subtraction")
         {
-            json::iterator it_begin = j.begin();
-            json::iterator it_end = j.end();
+            SECTION("object")
+            {
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it += 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it += 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it + 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it + 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it -= 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it -= 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it - 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it - 1, std::domain_error);
+                }
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it - it, std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it - it, std::domain_error);
+                }
+            }
 
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
+            SECTION("array")
+            {
+                {
+                    auto it = j_array.begin();
+                    it += 3;
+                    CHECK((j_array.begin() + 3) == it);
+                    CHECK((it - 3) == j_array.begin());
+                    CHECK((it - j_array.begin()) == 3);
+                    CHECK(*it == json(4));
+                    it -= 2;
+                    CHECK(*it == json(2));
+                }
+                {
+                    auto it = j_array.cbegin();
+                    it += 3;
+                    CHECK((j_array.cbegin() + 3) == it);
+                    CHECK((it - 3) == j_array.cbegin());
+                    CHECK((it - j_array.cbegin()) == 3);
+                    CHECK(*it == json(4));
+                    it -= 2;
+                    CHECK(*it == json(2));
+                }
+            }
 
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
+            SECTION("null")
+            {
+                {
+                    auto it = j_null.begin();
+                    it += 3;
+                    CHECK((j_null.begin() + 3) == it);
+                    CHECK((it - 3) == j_null.begin());
+                    CHECK((it - j_null.begin()) == 3);
+                    CHECK(it != j_null.end());
+                    it -= 3;
+                    CHECK(it == j_null.end());
+                }
+                {
+                    auto it = j_null.cbegin();
+                    it += 3;
+                    CHECK((j_null.cbegin() + 3) == it);
+                    CHECK((it - 3) == j_null.cbegin());
+                    CHECK((it - j_null.cbegin()) == 3);
+                    CHECK(it != j_null.cend());
+                    it -= 3;
+                    CHECK(it == j_null.cend());
+                }
+            }
 
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
+            SECTION("value")
+            {
+                {
+                    auto it = j_value.begin();
+                    it += 3;
+                    CHECK((j_value.begin() + 3) == it);
+                    CHECK((it - 3) == j_value.begin());
+                    CHECK((it - j_value.begin()) == 3);
+                    CHECK(it != j_value.end());
+                    it -= 3;
+                    CHECK(*it == json(42));
+                }
+                {
+                    auto it = j_value.cbegin();
+                    it += 3;
+                    CHECK((j_value.cbegin() + 3) == it);
+                    CHECK((it - 3) == j_value.cbegin());
+                    CHECK((it - j_value.cbegin()) == 3);
+                    CHECK(it != j_value.cend());
+                    it -= 3;
+                    CHECK(*it == json(42));
+                }
+            }
         }
 
-        SECTION("const json + begin/end")
+        SECTION("subscript operator")
         {
-            json::const_iterator it_begin = j_const.begin();
-            json::const_iterator it_end = j_const.end();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j_const[0]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const[2]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j.cbegin();
-            json::const_iterator it_end = j.cend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j_const.cbegin();
-            json::const_iterator it_end = j_const.cend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it_begin = j.rbegin();
-            json::reverse_iterator it_end = j.rend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it_begin = j.crbegin();
-            json::const_reverse_iterator it_end = j.crend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it_begin = j_const.crbegin();
-            json::const_reverse_iterator it_end = j_const.crend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j[2]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[1]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j[0]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-    }
-
-    SECTION("object")
-    {
-        json j = {{"A", 1}, {"B", 2}, {"C", 3}};
-        json j_const(j);
-
-        SECTION("json + begin/end")
-        {
-            json::iterator it_begin = j.begin();
-            json::iterator it_end = j.end();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j["A"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["C"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("const json + begin/end")
-        {
-            json::const_iterator it_begin = j_const.begin();
-            json::const_iterator it_end = j_const.end();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j_const["A"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const["C"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j.cbegin();
-            json::const_iterator it_end = j.cend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j["A"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["C"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j_const.cbegin();
-            json::const_iterator it_end = j_const.cend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j_const["A"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j_const["C"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it_begin = j.rbegin();
-            json::reverse_iterator it_end = j.rend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j["C"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["A"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it_begin = j.crbegin();
-            json::const_reverse_iterator it_end = j.crend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j["C"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["A"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it_begin = j_const.crbegin();
-            json::const_reverse_iterator it_end = j_const.crend();
-
-            auto it = it_begin;
-            CHECK(it != it_end);
-            CHECK(*it == j["C"]);
-
-            it++;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["B"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it != it_end);
-            CHECK(*it == j["A"]);
-
-            ++it;
-            CHECK(it != it_begin);
-            CHECK(it == it_end);
-        }
-    }
-
-    SECTION("number (integer)")
-    {
-        json j = 23;
-        json j_const(j);
-
-        SECTION("json + begin/end")
-        {
-            json::iterator it = j.begin();
-            CHECK(it != j.end());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            it--;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            --it;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + begin/end")
-        {
-            json::const_iterator it = j_const.begin();
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            it--;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            --it;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it = j.cbegin();
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            it--;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            --it;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it = j_const.cbegin();
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            it--;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            --it;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it = j.rbegin();
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            it--;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            --it;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j.crbegin();
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            it--;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            --it;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j_const.crbegin();
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            it--;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            --it;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-        }
-    }
-
-    SECTION("number (float)")
-    {
-        json j = 23.42;
-        json j_const(j);
-
-        SECTION("json + begin/end")
-        {
-            json::iterator it = j.begin();
-            CHECK(it != j.end());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            it--;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.begin());
-            CHECK(it == j.end());
-
-            --it;
-            CHECK(it == j.begin());
-            CHECK(it != j.end());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + begin/end")
-        {
-            json::const_iterator it = j_const.begin();
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            it--;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.begin());
-            CHECK(it == j_const.end());
-
-            --it;
-            CHECK(it == j_const.begin());
-            CHECK(it != j_const.end());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it = j.cbegin();
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            it--;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.cbegin());
-            CHECK(it == j.cend());
-
-            --it;
-            CHECK(it == j.cbegin());
-            CHECK(it != j.cend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it = j_const.cbegin();
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            it--;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.cbegin());
-            CHECK(it == j_const.cend());
-
-            --it;
-            CHECK(it == j_const.cbegin());
-            CHECK(it != j_const.cend());
-            CHECK(*it == j_const);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it = j.rbegin();
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            it--;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.rbegin());
-            CHECK(it == j.rend());
-
-            --it;
-            CHECK(it == j.rbegin());
-            CHECK(it != j.rend());
-            CHECK(*it == j);
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j.crbegin();
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            it++;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            it--;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-
-            ++it;
-            CHECK(it != j.crbegin());
-            CHECK(it == j.crend());
-
-            --it;
-            CHECK(it == j.crbegin());
-            CHECK(it != j.crend());
-            CHECK(*it == j);
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j_const.crbegin();
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            it++;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            it--;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-
-            ++it;
-            CHECK(it != j_const.crbegin());
-            CHECK(it == j_const.crend());
-
-            --it;
-            CHECK(it == j_const.crbegin());
-            CHECK(it != j_const.crend());
-            CHECK(*it == j_const);
-        }
-    }
-
-    SECTION("null")
-    {
-        json j = nullptr;
-        json j_const(j);
-
-        SECTION("json + begin/end")
-        {
-            json::iterator it = j.begin();
-            CHECK(it == j.end());
-        }
-
-        SECTION("const json + begin/end")
-        {
-            json::const_iterator it_begin = j_const.begin();
-            json::const_iterator it_end = j_const.end();
-            CHECK(it_begin == it_end);
-        }
-
-        SECTION("json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j.cbegin();
-            json::const_iterator it_end = j.cend();
-            CHECK(it_begin == it_end);
-        }
-
-        SECTION("const json + cbegin/cend")
-        {
-            json::const_iterator it_begin = j_const.cbegin();
-            json::const_iterator it_end = j_const.cend();
-            CHECK(it_begin == it_end);
-        }
-
-        SECTION("json + rbegin/rend")
-        {
-            json::reverse_iterator it = j.rbegin();
-            CHECK(it == j.rend());
-        }
-
-        SECTION("json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j.crbegin();
-            CHECK(it == j.crend());
-        }
-
-        SECTION("const json + crbegin/crend")
-        {
-            json::const_reverse_iterator it = j_const.crbegin();
-            CHECK(it == j_const.crend());
+            SECTION("object")
+            {
+                {
+                    auto it = j_object.begin();
+                    CHECK_THROWS_AS(it[0], std::domain_error);
+                    CHECK_THROWS_AS(it[1], std::domain_error);
+                }
+                {
+                    auto it = j_object.cbegin();
+                    CHECK_THROWS_AS(it[0], std::domain_error);
+                    CHECK_THROWS_AS(it[1], std::domain_error);
+                }
+            }
+
+            SECTION("array")
+            {
+                {
+                    auto it = j_array.begin();
+                    CHECK(it[0] == json(1));
+                    CHECK(it[1] == json(2));
+                    CHECK(it[2] == json(3));
+                    CHECK(it[3] == json(4));
+                    CHECK(it[4] == json(5));
+                    CHECK(it[5] == json(6));
+                }
+                {
+                    auto it = j_array.cbegin();
+                    CHECK(it[0] == json(1));
+                    CHECK(it[1] == json(2));
+                    CHECK(it[2] == json(3));
+                    CHECK(it[3] == json(4));
+                    CHECK(it[4] == json(5));
+                    CHECK(it[5] == json(6));
+                }
+            }
+
+            SECTION("null")
+            {
+                {
+                    auto it = j_null.begin();
+                    CHECK_THROWS_AS(it[0], std::out_of_range);
+                    CHECK_THROWS_AS(it[1], std::out_of_range);
+                }
+                {
+                    auto it = j_null.cbegin();
+                    CHECK_THROWS_AS(it[0], std::out_of_range);
+                    CHECK_THROWS_AS(it[1], std::out_of_range);
+                }
+            }
+
+            SECTION("value")
+            {
+                {
+                    auto it = j_value.begin();
+                    CHECK(it[0] == json(42));
+                    CHECK_THROWS_AS(it[1], std::out_of_range);
+                }
+                {
+                    auto it = j_value.cbegin();
+                    CHECK(it[0] == json(42));
+                    CHECK_THROWS_AS(it[1], std::out_of_range);
+                }
+            }
         }
     }
 }
@@ -4356,121 +4802,163 @@ TEST_CASE("modifiers")
 
 TEST_CASE("lexicographical comparison operators")
 {
-    json j_values =
+    SECTION("types")
     {
-        nullptr, nullptr,
-        17, 42,
-        3.14159, 23.42,
-        "foo", "bar",
-        true, false,
-        {1, 2, 3}, {"one", "two", "three"},
-        {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
-    };
-
-    SECTION("comparison: equal")
-    {
-        std::vector<std::vector<bool>> expected =
+        std::vector<json::value_t> j_types =
         {
-            {true, true, false, false, false, false, false, false, false, false, false, false, false, false},
-            {true, true, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, true, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, true, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, true, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, true, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, true, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, true, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, true, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, true, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, true}
+            json::value_t::null,
+            json::value_t::boolean,
+            json::value_t::number_integer,
+            json::value_t::number_float,
+            json::value_t::object,
+            json::value_t::array,
+            json::value_t::string
         };
 
-        for (size_t i = 0; i < j_values.size(); ++i)
+        SECTION("comparison: less")
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
+            std::vector<std::vector<bool>> expected =
             {
-                // check precomputed values
-                CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
+                {false, true, true, true, true, true, true},
+                {false, false, true, true, true, true, true},
+                {false, false, false, false, true, true, true},
+                {false, false, false, false, true, true, true},
+                {false, false, false, false, false, true, true},
+                {false, false, false, false, false, false, true},
+                {false, false, false, false, false, false, false}
+            };
+
+            for (size_t i = 0; i < j_types.size(); ++i)
+            {
+                for (size_t j = 0; j < j_types.size(); ++j)
+                {
+                    CAPTURE(i);
+                    CAPTURE(j);
+                    // check precomputed values
+                    CHECK( (j_types[i] < j_types[j]) == expected[i][j] );
+                }
             }
         }
     }
 
-    SECTION("comparison: not equal")
+    SECTION("values")
     {
-        for (size_t i = 0; i < j_values.size(); ++i)
+        json j_values =
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
-            {
-                // check definition
-                CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) );
-            }
-        }
-    }
-
-    SECTION("comparison: less")
-    {
-        std::vector<std::vector<bool>> expected =
-        {
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, true, false, true, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, true, true, false, true, false, false, false, false, false, false, false, false},
-            {false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-            {false, false, false, false, false, false, false, false, false, false, false, false, true, false}
+            nullptr, nullptr,
+            17, 42,
+            3.14159, 23.42,
+            "foo", "bar",
+            true, false,
+            {1, 2, 3}, {"one", "two", "three"},
+            {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
         };
 
-        for (size_t i = 0; i < j_values.size(); ++i)
+        SECTION("comparison: equal")
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
+            std::vector<std::vector<bool>> expected =
             {
-                // check precomputed values
-                CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
+                {true, true, false, false, false, false, false, false, false, false, false, false, false, false},
+                {true, true, false, false, false, false, false, false, false, false, false, false, false, false},
+                {false, false, true, false, false, false, false, false, false, false, false, false, false, false},
+                {false, false, false, true, false, false, false, false, false, false, false, false, false, false},
+                {false, false, false, false, true, false, false, false, false, false, false, false, false, false},
+                {false, false, false, false, false, true, false, false, false, false, false, false, false, false},
+                {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
+                {false, false, false, false, false, false, false, true, false, false, false, false, false, false},
+                {false, false, false, false, false, false, false, false, true, false, false, false, false, false},
+                {false, false, false, false, false, false, false, false, false, true, false, false, false, false},
+                {false, false, false, false, false, false, false, false, false, false, true, false, false, false},
+                {false, false, false, false, false, false, false, false, false, false, false, true, false, false},
+                {false, false, false, false, false, false, false, false, false, false, false, false, true, false},
+                {false, false, false, false, false, false, false, false, false, false, false, false, false, true}
+            };
+
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check precomputed values
+                    CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
+                }
             }
         }
-    }
 
-    SECTION("comparison: less than or equal equal")
-    {
-        for (size_t i = 0; i < j_values.size(); ++i)
+        SECTION("comparison: not equal")
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
+            for (size_t i = 0; i < j_values.size(); ++i)
             {
-                // check definition
-                CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) );
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check definition
+                    CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) );
+                }
             }
         }
-    }
 
-    SECTION("comparison: greater than")
-    {
-        for (size_t i = 0; i < j_values.size(); ++i)
+        SECTION("comparison: less")
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
+            std::vector<std::vector<bool>> expected =
             {
-                // check definition
-                CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
+                {false, false, true, true, true, true, true, true, true, true, true, true, true, true},
+                {false, false, true, true, true, true, true, true, true, true, true, true, true, true},
+                {false, false, false, true, false, true, true, true, false, false, true, true, true, true},
+                {false, false, false, false, false, false, true, true, false, false, true, true, true, true},
+                {false, false, true, true, false, true, true, true, false, false, true, true, true, true},
+                {false, false, false, true, false, false, true, true, false, false, true, true, true, true},
+                {false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+                {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
+                {false, false, true, true, true, true, true, true, false, false, true, true, true, true},
+                {false, false, true, true, true, true, true, true, true, false, true, true, true, true},
+                {false, false, false, false, false, false, true, true, false, false, false, true, false, false},
+                {false, false, false, false, false, false, true, true, false, false, false, false, false, false},
+                {false, false, false, false, false, false, true, true, false, false, true, true, false, false},
+                {false, false, false, false, false, false, true, true, false, false, true, true, true, false}
+            };
+
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check precomputed values
+                    CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
+                }
             }
         }
-    }
 
-    SECTION("comparison: greater than or equal")
-    {
-        for (size_t i = 0; i < j_values.size(); ++i)
+        SECTION("comparison: less than or equal equal")
         {
-            for (size_t j = 0; j < j_values.size(); ++j)
+            for (size_t i = 0; i < j_values.size(); ++i)
             {
-                // check definition
-                CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) );
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check definition
+                    CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) );
+                }
+            }
+        }
+
+        SECTION("comparison: greater than")
+        {
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check definition
+                    CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
+                }
+            }
+        }
+
+        SECTION("comparison: greater than or equal")
+        {
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    // check definition
+                    CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) );
+                }
             }
         }
     }
@@ -4721,20 +5209,20 @@ TEST_CASE("iterator class")
             {
                 json j(json::value_t::null);
                 json::iterator it = j.begin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::iterator it = j.begin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -4772,20 +5260,20 @@ TEST_CASE("iterator class")
             {
                 json j(json::value_t::null);
                 json::iterator it = j.begin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::iterator it = j.begin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -4823,20 +5311,18 @@ TEST_CASE("iterator class")
             {
                 json j(json::value_t::null);
                 json::iterator it = j.end();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
-                it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK(it.m_it.generic_iterator == 1);
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::iterator it = j.end();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -4874,20 +5360,18 @@ TEST_CASE("iterator class")
             {
                 json j(json::value_t::null);
                 json::iterator it = j.end();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
-                --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK(it.m_it.generic_iterator == 1);
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::iterator it = j.end();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -4919,63 +5403,6 @@ TEST_CASE("iterator class")
             }
         }
     }
-
-    SECTION("comparison")
-    {
-        json j_values =
-        {
-            nullptr, nullptr,
-            17, 42,
-            3.14159, 23.42,
-            "foo", "bar",
-            true, false,
-            {1, 2, 3}, {"one", "two", "three"},
-            {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
-        };
-
-        SECTION("comparison: equal")
-        {
-            std::vector<std::vector<bool>> expected =
-            {
-                {true, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, true, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, true, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, true, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, true, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, true, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, true, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, true, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, true, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, true, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, true}
-            };
-
-            for (size_t i = 0; i < j_values.size(); ++i)
-            {
-                for (size_t j = 0; j < j_values.size(); ++j)
-                {
-                    // check precomputed values
-                    CHECK( (j_values[i].begin() == j_values[j].begin()) == expected[i][j] );
-                }
-            }
-        }
-
-        SECTION("comparison: not equal")
-        {
-            for (size_t i = 0; i < j_values.size(); ++i)
-            {
-                for (size_t j = 0; j < j_values.size(); ++j)
-                {
-                    // check definition
-                    CHECK( (j_values[i].begin() != j_values[j].begin()) == not ((j_values[i].begin() ==
-                            j_values[j].begin())) );
-                }
-            }
-        }
-    }
 }
 
 TEST_CASE("const_iterator class")
@@ -5146,20 +5573,20 @@ TEST_CASE("const_iterator class")
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cbegin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::const_iterator it = j.cbegin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it++;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -5197,20 +5624,20 @@ TEST_CASE("const_iterator class")
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cbegin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::const_iterator it = j.cbegin();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 ++it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -5248,20 +5675,18 @@ TEST_CASE("const_iterator class")
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cend();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
-                it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK(it.m_it.generic_iterator == 1);
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::const_iterator it = j.cend();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 it--;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -5299,20 +5724,18 @@ TEST_CASE("const_iterator class")
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cend();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
-                --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK(it.m_it.generic_iterator == 1);
             }
 
             SECTION("number")
             {
                 json j(17);
                 json::const_iterator it = j.cend();
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
+                CHECK(it.m_it.generic_iterator == 1);
                 --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
+                CHECK(it.m_it.generic_iterator == 0);
                 --it;
-                CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
+                CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
             }
 
             SECTION("object")
@@ -5344,63 +5767,6 @@ TEST_CASE("const_iterator class")
             }
         }
     }
-
-    SECTION("comparison")
-    {
-        json j_values =
-        {
-            nullptr, nullptr,
-            17, 42,
-            3.14159, 23.42,
-            "foo", "bar",
-            true, false,
-            {1, 2, 3}, {"one", "two", "three"},
-            {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
-        };
-
-        SECTION("comparison: equal")
-        {
-            std::vector<std::vector<bool>> expected =
-            {
-                {true, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, true, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, true, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, true, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, true, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, true, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, true, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, true, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, true, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, true, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, true}
-            };
-
-            for (size_t i = 0; i < j_values.size(); ++i)
-            {
-                for (size_t j = 0; j < j_values.size(); ++j)
-                {
-                    // check precomputed values
-                    CHECK( (j_values[i].cbegin() == j_values[j].cbegin()) == expected[i][j] );
-                }
-            }
-        }
-
-        SECTION("comparison: not equal")
-        {
-            for (size_t i = 0; i < j_values.size(); ++i)
-            {
-                for (size_t j = 0; j < j_values.size(); ++j)
-                {
-                    // check definition
-                    CHECK( (j_values[i].cbegin() != j_values[j].cbegin()) == not ((j_values[i].cbegin() ==
-                            j_values[j].cbegin())) );
-                }
-            }
-        }
-    }
 }
 
 TEST_CASE("convenience functions")
@@ -6143,3 +6509,326 @@ TEST_CASE("README", "[hide]")
         // etc.
     }
 }
+
+TEST_CASE("algorithms")
+{
+    json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
+    json j_object = {{"one", 1}, {"two", 2}};
+
+    SECTION("non-modifying sequence operations")
+    {
+        SECTION("std::all_of")
+        {
+            CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
+            {
+                return value.size() > 0;
+            }));
+            CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
+            {
+                return value.type() == json::value_t::number_integer;
+            }));
+        }
+
+        SECTION("std::any_of")
+        {
+            CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
+            {
+                return value.is_string() and value.get<std::string>() == "foo";
+            }));
+            CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
+            {
+                return value.get<int>() > 1;
+            }));
+        }
+
+        SECTION("std::none_of")
+        {
+            CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value)
+            {
+                return value.size() == 0;
+            }));
+            CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value)
+            {
+                return value.get<int>() <= 0;
+            }));
+        }
+
+        SECTION("std::for_each")
+        {
+            SECTION("reading")
+            {
+                int sum = 0;
+
+                std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value)
+                {
+                    if (value.is_number())
+                    {
+                        sum += static_cast<int>(value);
+                    }
+                });
+
+                CHECK(sum == 45);
+            }
+
+            SECTION("writing")
+            {
+                auto add17 = [](json & value)
+                {
+                    if (value.is_array())
+                    {
+                        value.push_back(17);
+                    }
+                };
+
+                std::for_each(j_array.begin(), j_array.end(), add17);
+
+                CHECK(j_array[6] == json({1, 2, 3, 17}));
+            }
+        }
+
+        SECTION("std::count")
+        {
+            CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1);
+        }
+
+        SECTION("std::count_if")
+        {
+            CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value)
+            {
+                return (value.is_number());
+            }) == 3);
+            CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&)
+            {
+                return true;
+            }) == 9);
+        }
+
+        SECTION("std::mismatch")
+        {
+            json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"};
+            auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin());
+            CHECK(*res.first == json({{"one", 1}, {"two", 2}}));
+            CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}}));
+        }
+
+        SECTION("std::equal")
+        {
+            SECTION("using operator==")
+            {
+                CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin()));
+                CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin()));
+                CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin()));
+            }
+
+            SECTION("using user-defined comparison")
+            {
+                // compare objects only by size of its elements
+                json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"};
+                CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin()));
+                CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(),
+                                 [](const json & a, const json & b)
+                {
+                    return (a.size() == b.size());
+                }));
+            }
+        }
+
+        SECTION("std::find")
+        {
+            auto it = std::find(j_array.begin(), j_array.end(), json(false));
+            CHECK(std::distance(j_array.begin(), it) == 5);
+        }
+
+        SECTION("std::find_if")
+        {
+            auto it = std::find_if(j_array.begin(), j_array.end(),
+                                   [](const json & value)
+            {
+                return value.is_boolean();
+            });
+            CHECK(std::distance(j_array.begin(), it) == 4);
+        }
+
+        SECTION("std::find_if_not")
+        {
+            auto it = std::find_if_not(j_array.begin(), j_array.end(),
+                                       [](const json & value)
+            {
+                return value.is_number();
+            });
+            CHECK(std::distance(j_array.begin(), it) == 3);
+        }
+
+        SECTION("std::adjacent_find")
+        {
+            CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end());
+            CHECK(std::adjacent_find(j_array.begin(), j_array.end(),
+                                     [](const json & v1, const json & v2)
+            {
+                return v1.type() == v2.type();
+            }) == j_array.begin());
+        }
+    }
+
+    SECTION("modifying sequence operations")
+    {
+        SECTION("std::reverse")
+        {
+            std::reverse(j_array.begin(), j_array.end());
+            CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13}));
+        }
+
+        SECTION("std::rotate")
+        {
+            std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end());
+            CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13}));
+        }
+
+        SECTION("std::partition")
+        {
+            auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v)
+            {
+                return v.is_string();
+            });
+            CHECK(std::distance(j_array.begin(), it) == 2);
+            CHECK(not it[2].is_string());
+        }
+    }
+
+    SECTION("sorting operations")
+    {
+        SECTION("std::sort")
+        {
+            SECTION("with standard comparison")
+            {
+                json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
+                std::sort(j.begin(), j.end());
+                CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
+            }
+
+            SECTION("with user-defined comparison")
+            {
+                json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr};
+                std::sort(j.begin(), j.end(), [](const json & a, const json & b)
+                {
+                    return a.size() < b.size();
+                });
+                CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}}));
+            }
+
+            SECTION("sorting an object")
+            {
+                json j({{"one", 1}, {"two", 2}});
+                CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error);
+            }
+        }
+
+        SECTION("std::partial_sort")
+        {
+            json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
+            std::partial_sort(j.begin(), j.begin() + 4, j.end());
+            CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13}));
+        }
+    }
+
+    SECTION("set operations")
+    {
+        SECTION("std::merge")
+        {
+            {
+                json j1 = {2, 4, 6, 8};
+                json j2 = {1, 2, 3, 5, 7};
+                json j3;
+
+                std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+                CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
+            }
+        }
+
+        SECTION("std::set_difference")
+        {
+            json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
+            json j2 = {1, 2, 3, 5, 7};
+            json j3;
+
+            std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+            CHECK(j3 == json({4, 6, 8}));
+        }
+
+        SECTION("std::set_intersection")
+        {
+            json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
+            json j2 = {1, 2, 3, 5, 7};
+            json j3;
+
+            std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+            CHECK(j3 == json({1, 2, 3, 5, 7}));
+        }
+
+        SECTION("std::set_union")
+        {
+            json j1 = {2, 4, 6, 8};
+            json j2 = {1, 2, 3, 5, 7};
+            json j3;
+
+            std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+            CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8}));
+        }
+
+        SECTION("std::set_symmetric_difference")
+        {
+            json j1 = {2, 4, 6, 8};
+            json j2 = {1, 2, 3, 5, 7};
+            json j3;
+
+            std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+            CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8}));
+        }
+    }
+
+    SECTION("heap operations")
+    {
+        std::make_heap(j_array.begin(), j_array.end());
+        CHECK(std::is_heap(j_array.begin(), j_array.end()));
+        std::sort_heap(j_array.begin(), j_array.end());
+        CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
+    }
+}
+
+TEST_CASE("concepts")
+{
+    SECTION("DefaultConstructible")
+    {
+        CHECK(std::is_nothrow_default_constructible<json>::value);
+    }
+
+    SECTION("MoveConstructible")
+    {
+        CHECK(std::is_nothrow_move_constructible<json>::value);
+    }
+
+    SECTION("CopyConstructible")
+    {
+        CHECK(std::is_copy_constructible<json>::value);
+    }
+
+    SECTION("MoveAssignable")
+    {
+        CHECK(std::is_nothrow_move_assignable<json>::value);
+    }
+
+    SECTION("CopyAssignable")
+    {
+        CHECK(std::is_copy_assignable<json>::value);
+    }
+
+    SECTION("Destructible")
+    {
+        CHECK(std::is_nothrow_destructible<json>::value);
+    }
+
+    SECTION("StandardLayoutType")
+    {
+        CHECK(std::is_standard_layout<json>::value);
+    }
+}