diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 00000000..ff8f3817
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+/pdf/
+/html/
diff --git a/doc/Jamfile b/doc/Jamfile
new file mode 100644
index 00000000..65f87059
--- /dev/null
+++ b/doc/Jamfile
@@ -0,0 +1,25 @@
+# Copyright 2017, 2018 Peter Dimov
+# Copyright 2024 Matt Borland
+# Distributed under the Boost Software License, Version 1.0.
+# https://www.boost.org/LICENSE_1_0.txt
+
+import asciidoctor ;
+
+html crypt.html : crypt.adoc
+    :   <use>/boost/boostlook//boostlook
+        <dependency>crypt-docinfo-footer.html
+    ;
+
+install html_ : crypt.html : <location>html ;
+
+pdf crypt.pdf : crypt.adoc ;
+explicit crypt.pdf ;
+
+install pdf_ : crypt.pdf : <location>pdf ;
+explicit pdf_ ;
+
+###############################################################################
+alias boostdoc ;
+explicit boostdoc ;
+alias boostrelease : html_ ;
+explicit boostrelease ;
diff --git a/doc/crypt-docinfo-footer.html b/doc/crypt-docinfo-footer.html
new file mode 100644
index 00000000..622aa5fa
--- /dev/null
+++ b/doc/crypt-docinfo-footer.html
@@ -0,0 +1,6 @@
+<style>
+
+    *:not(pre)>code { background: none; color: #600000; }
+    :not(pre):not([class^=L])>code { background: none; color: #600000; }
+
+</style>
diff --git a/doc/crypt.adoc b/doc/crypt.adoc
new file mode 100644
index 00000000..093e4827
--- /dev/null
+++ b/doc/crypt.adoc
@@ -0,0 +1,30 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+= Crypt: A Modern Cryptographic Module in C++14
+:toc: left
+:toclevels: 4
+:idprefix:
+:listing-caption: Code Example
+:docinfo: private-footer
+:source-highlighter: rouge
+:source-language: c++
+
+:leveloffset: +1
+
+include::crypt/overview.adoc[]
+
+include::crypt/api_reference.adoc[]
+
+include::crypt/md5.adoc[]
+
+include::crypt/config.adoc[]
+
+include::crypt/reference.adoc[]
+
+include::crypt/copyright.adoc[]
+
+:leveloffset: -1
diff --git a/doc/crypt/api_reference.adoc b/doc/crypt/api_reference.adoc
new file mode 100644
index 00000000..f4bb7751
--- /dev/null
+++ b/doc/crypt/api_reference.adoc
@@ -0,0 +1,30 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#api_reference]
+= API Reference
+:idprefix: api_ref_
+
+== Types
+
+- <<array, `array`>>
+
+== Structures and Classes
+
+- <<md5_hasher, `md5_hasher`>>
+- <<from_chars_result, `from_chars_result`>>
+
+== Enums
+
+- None
+
+== Constants
+
+- None
+
+== Macros
+
+See: <<configuration>>
diff --git a/doc/crypt/array.adoc b/doc/crypt/array.adoc
new file mode 100644
index 00000000..3353bcd6
--- /dev/null
+++ b/doc/crypt/array.adoc
@@ -0,0 +1,70 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#array]
+:idprefix: array_
+
+In order for this lab to use `<array>` in a `constexpr` environment we would need to support C+\+17.
+Additionally, CUDA environments do not directly support `std::array`.
+For these reasons we reimplement `<array>` in our namespace that both supports both C++14 and CUDA.
+It is *IMPLICITLY* convertible to `std::array<T, N>` for convenience.
+
+[source, c++]
+----
+template <typename T, boost::crypt::size_t N>
+class array
+{
+public:
+    using reference = T&;
+    using const_reference = const T&;
+    using iterator = T*;
+    using const_iterator = const T*;
+    using size_type = size_t;
+    using difference_type = ptrdiff_t;
+    using value_type = T;
+    using pointer = T*;
+    using const_pointer = const T*;
+
+    T elements[N];
+
+    // Iterators
+    BOOST_CRYPT_GPU_ENABLED constexpr auto begin() noexcept -> iterator;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto begin() const noexcept -> iterator;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto cbegin() const noexcept -> const_iterator;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto end() noexcept -> iterator;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto end() const noexcept -> iterator;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto cend() const noexcept -> const_iterator;
+
+    // Sizing
+    BOOST_CRYPT_GPU_ENABLED constexpr auto size() const noexcept -> size_type;
+    BOOST_CRYPT_GPU_ENABLED constexpr auto max_size() const noexcept -> size_type;
+
+    // Accessors
+    BOOST_CRYPT_GPU_ENABLED constexpr auto operator[](size_type n) noexcept;
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto operator[](size_type n) const noexcept;
+
+    // For at instead of throwing on out of range return the last element since throwing doesn't work on device
+    BOOST_CRYPT_GPU_ENABLED constexpr auto at(size_type n) noexcept -> reference;
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto at(size_type n) const noexcept -> const_reference;
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto front() noexcept -> reference { return elements[0]; }
+    BOOST_CRYPT_GPU_ENABLED constexpr auto front() const noexcept -> const_reference { return elements[0]; }
+    BOOST_CRYPT_GPU_ENABLED constexpr auto back() noexcept -> reference { return elements[N - 1]; }
+    BOOST_CRYPT_GPU_ENABLED constexpr auto back() const noexcept -> const_reference { return elements[N - 1]; }
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto data() noexcept -> pointer { return elements; }
+    BOOST_CRYPT_GPU_ENABLED constexpr auto data() const noexcept -> const_pointer { return elements; }
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto fill(const value_type& v) -> void;
+
+    BOOST_CRYPT_GPU_ENABLED constexpr auto swap(array<value_type, N>& a);
+
+    // Conversion
+    constexpr operator std::array<T, N>() noexcept;
+};
+----
diff --git a/doc/crypt/config.adoc b/doc/crypt/config.adoc
new file mode 100644
index 00000000..f252c371
--- /dev/null
+++ b/doc/crypt/config.adoc
@@ -0,0 +1,19 @@
+////
+Copyright 2023 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#configuration]
+= Configuration Macros
+:idprefix: config_
+
+== User Configurable Macros
+
+The following configuration macros are available:
+
+- None at this time
+
+== Automatic Configuration Macros
+
+- `BOOST_CRYPT_HAS_STRING_VIEW`: This is defined when compiling with at least C++17 and your standard library has a complete implementation of `<string_view>`.
diff --git a/doc/crypt/copyright.adoc b/doc/crypt/copyright.adoc
new file mode 100644
index 00000000..f884e9ce
--- /dev/null
+++ b/doc/crypt/copyright.adoc
@@ -0,0 +1,12 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#copyright]
+= Copyright and License
+:idprefix: license_
+
+This documentation is copyright 2024 Matt Borland and Chris Kormanyos and is distributed under
+the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0].
diff --git a/doc/crypt/md5.adoc b/doc/crypt/md5.adoc
new file mode 100644
index 00000000..63b20fa0
--- /dev/null
+++ b/doc/crypt/md5.adoc
@@ -0,0 +1,115 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#md5]
+:idprefix: md5_
+
+= MD5
+
+This library supports MD5 as described in https://www.ietf.org/rfc/rfc1321.txt[RFC 1321].
+There is a wide range of acceptable inputs for the base md5 function:
+
+== Hashing Functions
+
+[source, c++]
+----
+namespace boost {
+namespace crypt {
+
+uisng return_type = boost::crypt::array<uint8_t, 16>;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char* str) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char* str, size_t len) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const unsigned char* str) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const unsigned char* str, size_t len) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char16_t* str) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char16_t* str, size_t len) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char32_t* str) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char32_t* str, size_t len) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const wchar_t* str) noexcept -> return_type;
+
+BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const wchar_t* str, size_t len) noexcept -> return_type;
+
+inline auto md5(const std::string& str) noexcept -> return_type;
+
+inline auto md5(const std::u16string& str) noexcept -> return_type;
+
+inline auto md5(const std::u32string& str) noexcept -> return_type;
+
+inline auto md5(const std::wstring& str) noexcept -> return_type;
+
+#ifdef BOOST_CRYPT_HAS_STRING_VIEW
+
+inline auto md5(std::string_view str) noexcept -> return_type;
+
+inline auto md5(std::u16string_view str) noexcept -> return_type;
+
+inline auto md5(std::u32string_view str) noexcept -> return_type;
+
+inline auto md5(std::wstring_view str) noexcept -> return_type;
+
+#endif // BOOST_CRYPT_HAS_STRING_VIEW
+
+} //namespace crypt
+} //namespace boost
+----
+
+== File Hashing Functions
+
+We also have the ability to scan files and return the MD5 value:
+
+[source, c++]
+----
+namespace boost {
+namespace crypt {
+
+uisng return_type = boost::crypt::array<uint8_t, 16>;
+
+inline auto md5_file(const char* filepath) noexcept -> return_type;
+
+inline auto md5_file(const std::string& filepath) noexcept -> return_type;
+
+inline auto md5_file(std::string_view filepath) noexcept -> return_type;
+
+} // namespace crypt
+} // namespace boost
+----
+
+== Hashing Object
+
+[#md5_hasher]
+Lastly, there is also the ability to create a MD5 hashing object and feed it bytes as the user parses them.
+This class does not use any dynamic memory allocation.
+
+[source, c++]
+----
+namespace boost {
+namespace crypt {
+
+class md5_hasher
+{
+    init();
+
+    template <typename ByteType>
+    BOOST_CRYPT_GPU_ENABLED constexpr auto process_byte(ByteType byte) noexcept -> void;
+
+    template <typename ForwardIter>
+    BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter buffer, size_t byte_count) noexcept -> void;
+
+    constexpr auto get_digest() noexcept -> boost::crypt::array<boost::crypt::uint8_t, 16>;
+};
+
+} // namespace crypt
+} // namespace boost
+----
diff --git a/doc/crypt/overview.adoc b/doc/crypt/overview.adoc
new file mode 100644
index 00000000..1fab53d8
--- /dev/null
+++ b/doc/crypt/overview.adoc
@@ -0,0 +1,70 @@
+////
+Copyright 2023 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#overview]
+= Overview
+:idprefix: overview_
+
+== Description
+
+Boost.Decimal is an implementation of https://standards.ieee.org/ieee/754/6210/[IEEE 754] and https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2849.pdf[ISO/IEC DTR 24733] Decimal Floating Point numbers.
+The library is header-only, has no dependencies, and requires C++14.
+
+== Motivation
+
+Current C++ floating point types store the significand (often incorrectly referred to as the mantissa) as binary digits.
+Famously this leads to representation errors: https://0.30000000000000004.com.
+Decimal floating point numbers avoid this issue by storing the significand in base-10 (decimal).
+The other major difference between binary and decimal floating point types is that the latter allows for multiple representations of the same number.
+For example 1e5 could also be stored as 0.1e6, 0.01e7, so on and so forth.
+These are referred to as cohorts which binary does not have as there is only one way to represent each number in binary floating point.
+
+== Use Cases
+
+The use case for Decimal Floating Point numbers is where rounding errors are significantly impactful such as finance.
+In applications where integer or fixed-point arithmetic are used to combat this issue Decimal Floating Point numbers can provide a significantly greater range of values.
+For example, while a fixed-point representation that allocates 8 decimal digits and 2 decimal places can represent the numbers 123456.78, 8765.43, 123.00, and so on, a floating-point representation with 8 decimal digits could also represent 1.2345678, 1234567.8, 0.000012345678, 12345678000000000, and so on.
+
+== Supported Compilers
+
+Boost.Decimal is tested natively on Ubuntu (x86_64, s390x, and aarch64), macOS (x86_64, and Apple Silicon), and Windows (x32 and x64);
+as well as emulated PPC64LE and STM32 using QEMU with the following compilers:
+
+* GCC 7 and later
+* Clang 6 and later
+* Visual Studio 2017 and later
+* Intel OneAPI DPC++
+
+Tested on https://github.com/cppalliance/decimal/actions[Github Actions] and https://drone.cpp.al/cppalliance/decimal[Drone].
+Coverage can be found on https://app.codecov.io/gh/cppalliance/decimal[Codecov].
+
+== Basic Usage
+
+[source, c++]
+----
+#include <boost/decimal.hpp>
+#include <iostream>
+#include <iomanip>
+
+int main()
+{
+    using namespace boost::decimal;
+
+    // Outputs 0.30000000000000004
+    std::cout << std::setprecision(17) << 0.1 + 0.2;
+
+    // Construct the two decimal values
+    constexpr decimal64 a {1, -1}; // 1e-1 or 0.1
+    constexpr decimal64 b {2, -1}; // 2e-1 or 0.2
+
+    // Outputs 0.30000000000000000
+    std::cout << a + b << std::endl;
+
+    return 0;
+}
+
+----
+
diff --git a/doc/crypt/reference.adoc b/doc/crypt/reference.adoc
new file mode 100644
index 00000000..87d49fc4
--- /dev/null
+++ b/doc/crypt/reference.adoc
@@ -0,0 +1,15 @@
+////
+Copyright 2024 Matt Borland
+Distributed under the Boost Software License, Version 1.0.
+https://www.boost.org/LICENSE_1_0.txt
+////
+
+[#reference]
+= References
+:idprefix: ref_
+
+The following books, papers and blog posts serve as the basis for the algorithms used in the library:
+
+:linkattrs:
+
+- Ronald L. Rivest, https://www.ietf.org/rfc/rfc1321.txt[RFC 1321:  The MD5 Message-Digest Algorithm], 1992
diff --git a/include/boost/crypt/utility/array.hpp b/include/boost/crypt/utility/array.hpp
index 302aefa1..f13b1ddf 100644
--- a/include/boost/crypt/utility/array.hpp
+++ b/include/boost/crypt/utility/array.hpp
@@ -10,6 +10,10 @@
 #include <boost/crypt/utility/cstdint.hpp>
 #include <boost/crypt/utility/cstddef.hpp>
 
+#ifndef BOOST_CRYPT_BUILD_MODULE
+#include <array>
+#endif
+
 namespace boost {
 namespace crypt {
 
@@ -97,6 +101,17 @@ class array
         a = *this;
         *this = temp;
     }
+
+    constexpr operator std::array<T, N>() noexcept
+    {
+        std::array<T, N> new_array{};
+        for (boost::crypt::size_t i {}; i < N; ++i)
+        {
+            new_array[i] = elements[i];
+        }
+
+        return new_array;
+    }
 };
 
 template <typename ForwardIter, typename T>
diff --git a/test/test_md5.cpp b/test/test_md5.cpp
index ab4429e5..fb9ae419 100644
--- a/test/test_md5.cpp
+++ b/test/test_md5.cpp
@@ -224,7 +224,9 @@ void test_random_values()
         const std::size_t current_str_len {str_len(rng)};
         boost::crypt::generate_random_string(str, current_str_len);
         const auto uuid_res {get_boost_uuid_result(str, current_str_len)};
-        const auto crypt_res {boost::crypt::md5(str, current_str_len)};
+
+        // boost::crypt::array is implicitly convertible to std::array
+        const std::array<std::uint8_t, 16> crypt_res = boost::crypt::md5(str, current_str_len);
 
         for (std::size_t j {}; j < crypt_res.size(); ++j)
         {