Part 3. C Source File

This is the longest post of the group due to it’s nature. Knowledge of C will of course be useful, but really shouldn’t be necessary to understand the purpose of each routine, since there is vebose documentation done using the Doxygen format.

Of particular interest for people interested in the UNICODE conversion capabilities of the source code using iconv(3), you may want to visit the following routines for more detail:

  • Unicode_import_string
  • Unicode_export_string
  • Unicode_save
  • Unicode_load

For those interested in the mmap(2) virtual memory code, you may want to visit the following routine for more details:

  • UnicodeTesseract_new

If you are interested in trying any of this yourself, it would be a good idea to copy and paste the source code in all the posts in this series, and save them to source code files in the same folder. All of the source code and shell script files will be provided, as well as their names used.

With no further ado, here is the source code for the unicode.c file:

/**
 * vim: fileencoding=utf8
 * @file unicode.c - SWIG C Extension Unicode Library
 * @brief Struct Unicode SWIG extended function definitions
 * @version 1.2
 *
 * @note This is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * @note This is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * @note You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "unicode.h"

#ifdef __cplusplus
extern "C" {
#endif

/*! @mainpage Unicode Codepoint Compliant String Library
 *
 * @section description Description
 *
 * This library brings unicode codepoint compliant string functions to
 * script languages that can:
 * @li Construct and destruct **Unicode**, **UnicodeArray** and **UnicodeTesseract** objects
 * @li Get and set **Unicode**, **UnicodeArray** and **UnicodeTesseract** object metadata
 * @li Convert from/to C integral numeric types int, long and long long
 * @li Convert from/to C floating point types float, double and long double
 * @li Convert strings from/to any **iconv(3)** supported encoding
 * @li Count, copy, swap, append, find, extract and replace **Unicode** codepoints
 * @li Compare locale-aware **Unicode** codepoints as strings
 * @li Compare locale-aware **Unicode** codepoints as numbers
 * @li Convert **Unicode** codepoints to uppercase, lowercase or swapcase
 * @li Count, extract, replace, insert, append, delete, sort and locate delimited subvalues
 * @li Extract or replace **Unicode** subvalues using a script language's native arrays
 * @li Get, set, determine character types and case-convert single codepoints
 * @li File load/save **Unicode** codepoints using any **iconv(3)** supported encoding
 * @li Separate virtual memory space support for big data that cannot fit on stack or heap
 *
 * @subsection dependencies Dependencies
 *
 * This **Unicode** library is generated using the SWIG (Simple Wrapper
 * Interface Generator) software version 3.0 (or higher) to generate the
 * interface code used by scripting languages.  It is also dependent on the GCC
 * C compiler version 4.4 (or higher).  Other than this, there are no other
 * library dependencies. This library was developed and tested using Ubuntu
 * 12.04/16.04 x86_64 GNU/Linux.
 *
 * @subsection documentation Documentation
 *
 * The **Doxygen** documentation is for the C functions only, which can be used
 * in both C or C++ programs directly. Scripting language interfaces are
 * documented separately.
 *
 * @subsection implementation Implementation
 *
 * Most functions are passed a pointer to a struct as their first parameter,
 * normally referred to as an object.  SWIG uses the first pointer to any
 * imported C functions similar to how a *this* pointer is used in C++
 * functions.  Internally, Unicode strings are stored in an internal buffer in
 * UTF32LE encoding. Each codepoint is represented internally as a wide
 * character type *wchar_t*.  Since most scripting languages do not support
 * working directly with wide characters, there are conversion functions
 * between **Unicode** objects and native strings that are available.  Any
 * encoding supported by the operating system's **iconv(3)** function can be
 * imported or exported from a **Unicode** object, not just UTF8 or ASCII.
 *
 */

/**
 * @fn "struct Unicode * Unicode_new()"
 * @brief Constructs an empty **Unicode** object
 * @details A new **struct Unicode** object is allocated on the heap and the
 * heap address is returned.  The object is considered to be in an empty state.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_new();
 * @endcode
 *
 * @retval "struct Unicode *" = Pointer to new empty **Unicode** object
 * @exception assert(3) Aborts if calloc(3) returns a null pointer
 */

struct Unicode * Unicode_new() 
{ 
    struct Unicode * l_pouniObject = (struct Unicode *) calloc(1, sizeof(struct Unicode)); 
    assert(l_pouniObject != 0);
    l_pouniObject->m_poszCodepoints = 0;
    l_pouniObject->m_sizCodepoints = 0;
    l_pouniObject->m_sizBytes = 0;
    return l_pouniObject; 
} 

/**
 * @fn "void Unicode_delete(struct Unicode ** u_popouniObject)"
 * @brief Destructs a **Unicode** object
 * @details When finished using a **Unicode** object, you can destruct it using
 * this function. The C library function **free(3)** is used to first
 * deallocate any codepoints on the heap, and then used to deallocate the
 * **struct Unicode** object itself. Note that the parameter is a
 * double-pointer to a **Unicode** object, as the value of the pointer is set
 * to null after calling **free(3)**.
 *
 * @attention Failure to call this on a constructed **Unicode** object can cause a memory leak.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * l_pouniObject = Unicode_from_string("0123456789abcdef", 0, "ASCII");
 * Unicode_delete(&l_pouniObject);
 * @endcode
 *
 * @param[in,out] u_popouniObject = Pointer to pointer of **Unicode** object
 * @retval "void" = None
 * @exception assert(3) Aborts if u_popouniObject contains a null pointer
 * @exception assert(3) Aborts if *u_popouniObject contains a null pointer
 */

void Unicode_delete(struct Unicode ** u_popouniObject)
{ 
    assert(u_popouniObject != 0);
    assert(*u_popouniObject != 0);
    if ((*u_popouniObject)->m_poszCodepoints != 0) free((*u_popouniObject)->m_poszCodepoints);
    free(*u_popouniObject);
    *u_popouniObject = 0;
} 

/**
 * @fn "void Unicode_clear(struct Unicode * u_pouniObject)"
 * @brief Clears the **Unicode** object of any codepoint content
 * @details When finished using the codepoint contents of a **Unicode** object,
 * you can clear it's contents using this function. The C library function
 * **free(3)** is used to deallocate any codepoints on the heap, but the
 * **struct Unicode** object itself is left intact.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * char * l_poszEbcdic = 0;
 * l_pouniObject = Unicode_from_string("0123456789abcdef", 0, "ASCII");
 * l_poszEbcdic = Unicode_export_string(l_pouniObject, 0, "EBCDIC-US");
 * Unicode_clear(l_pouniObject);
 * @endcode
 *
 * @param[in,out] u_pouniObject = The updated **Unicode** object
 * @retval "void" = None
 * @exception None
 */

void Unicode_clear(struct Unicode * u_pouniObject)
{
    if (u_pouniObject != 0) {
        if (u_pouniObject->m_poszCodepoints != 0) free(u_pouniObject->m_poszCodepoints);
        u_pouniObject->m_poszCodepoints = 0;
        u_pouniObject->m_sizCodepoints = 0;
        u_pouniObject->m_sizBytes = 0;
    }
}

/**
 * @fn "int Unicode_empty(const struct Unicode * i_pouniObject)"
 * @brief Returns true if **Unicode** object is empty.
 * @details Returns true if the **Unicode** object has not been allocated, the
 * codepoints buffer has not been allocated, or the number of codepoints or
 * bytes is zero.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * printf("The object is %s\n", Unicode_empty(l_pouniObject) ? "empty" : "not empty");
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "bool" = true (1) if **Unicode** object is empty else false (0)
 * @exception None
 */

int Unicode_empty(const struct Unicode * i_pouniObject)
{
    if (i_pouniObject == 0 || i_pouniObject->m_poszCodepoints == 0 || i_pouniObject->m_sizCodepoints == 0 || i_pouniObject->m_sizBytes == 0)
        return(1);
    return(0);
}

/**
 * @fn "size_t Unicode_codepoints(const struct Unicode * i_pouniObject)"
 * @brief Returns codepoint count in the **Unicode** object
 * @details Returns the number of codepoints stored in the **Unicode** object content.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * size_t l_sizInbytes = 0;
 * size_t l_sizOutcodepoints = 0;
 * l_sizInbytes = Unicode_import_string(l_pouniObject, "João Méroço", 0, "UTF8");
 * l_sizOutcodepoints = Unicode_codepoints(l_pouniObject);
 * @endcode
 *
 * @retval "size_t" = Number of UTF codepoints stored in **Unicode** object,
 * or zero if the **Unicode** object is empty
 * @exception None
 */

size_t Unicode_codepoints(const struct Unicode * i_pouniObject)
{
    if (i_pouniObject == 0 || i_pouniObject->m_poszCodepoints == 0)
        return(0);
    return(i_pouniObject->m_sizCodepoints);
}

/**
 * @fn "size_t Unicode_bytes(const struct Unicode * i_pouniObject)"
 * @brief Returns the byte count of the codepoints in the **Unicode** object
 * @details Returns the number of bytes used to store the codepoints of the
 * **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * size_t l_sizInbytes = 0;
 * size_t l_sizOutbytes = 0;
 * l_sizInbytes = Unicode_import_string(l_pouniObject, "João Méroço", 0, "UTF8");
 * l_sizOutbytes = Unicode_bytes(l_pouniObject);
 * @endcode
 *
 * @retval "size_t" = Number of bytes in **Unicode** object, or zero if the
 * **Unicode** object is empty.
 * @exception None
 */

size_t Unicode_bytes(const struct Unicode * i_pouniObject)
{
    if (i_pouniObject == 0 || i_pouniObject->m_poszCodepoints == 0)
        return(0);
    return(i_pouniObject->m_sizBytes);
}

/**
 * @fn "size_t Unicode_import_string(struct Unicode * u_pouniObject, const char * i_poszString, size_t i_sizMaxbytes, const char * i_poszEncoding)"
 * @brief Converts a null-terminated character string into a **Unicode** object
 * @details Convert a series of 8-bit, 16-bit or 32-bit wide characters stored
 * in a null-terminated **char** string into a **Unicode** object. Input buffer
 * is pointed to by **i_poszString** using a char pointer. The number of 8-bit
 * bytes in the buffer to convert is passed in **i_sizMaxbytes**. The
 * **i_poszEncoding** parameter contains a character encoding supported by
 * the implementation's **iconv(3)** C library function.  The **i_sizMaxbytes**
 * parameter should contain the number of bytes (not characters) to convert,
 * not including any null terminator. No error occurs nor is any conversion
 * performed if **i_poszString** contains a null value or if **i_sizMaxbytes** is
 * negative.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * size_t l_sizInbytes = 0;
 * l_sizInbytes = Unicode_import_string(l_pouniObject, "João Méroço", 0, "UTF8");
 * @endcode
 *
 * @param[in,out] u_pouniObject = The updated **Unicode** object
 * @param[in] i_poszString = Pointer to input char string buffer
 * @param[in] i_sizMaxbytes = Maximum number of input bytes to convert, if zero
 * uses strlen() to determine length
 * @param[in] i_poszEncoding = Null terminated string containing iconv(3)
 * encoding of input
 * @retval "size_t" = Number of input bytes (not characters) successfully
 * converted not including the null terminator. Should be equal to the positive
 * value of **i_sizMaxbytes** if conversion completes successfully.
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if i_poszString is null
 * @exception abort(3) Aborts if i_poszEncoding is null
 * @exception assert(3) Aborts if alloca(3) call returns null
 * @exception assert(3) Aborts if calloc(3) call returns null
 * @exception abort(3) Aborts on iconv_open(3) failure
 * @exception abort(3) Aborts on iconv(3) failure
 * @exception abort(3) Aborts on iconv_close(3) failure
 */

size_t Unicode_import_string(struct Unicode * u_pouniObject, const char * i_poszString, size_t i_sizMaxbytes, const char * i_poszEncoding)
{
    size_t l_sizConverted = 0;
    size_t l_sizOutbytes = 0;
    size_t l_sizOutbytesleft = 0;
    size_t l_sizInbytes = 0;
    size_t l_sizInbytesleft = 0;
    size_t l_sizReturn = 0;
    int l_inReturn = 0;
    const char * l_poszToencoding = "UTF32LE";
    const char * l_poszFromencoding = i_poszEncoding;
    char * l_poszBuffer = 0;
    char * l_poszOutbuf = 0;
    char * l_poszInbuf = 0;
    iconv_t l_ictCd;

    if (u_pouniObject == 0 || i_poszString == 0 || i_poszEncoding == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_poszString = %p, i_poszEncoding = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_poszString, i_poszEncoding);
        abort();
    }
    Unicode_clear(u_pouniObject);
    l_ictCd = iconv_open(l_poszToencoding, l_poszFromencoding);
    if (l_ictCd == (iconv_t) -1) {
        fprintf(stderr, "%s(%d) = iconv_open() = -1, errno = %d, strerror = '%s', l_poszToencoding = '%s', l_poszFromencoding = '%s', i_poszString = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_poszToencoding, l_poszFromencoding, i_poszString);
        abort();
    }
    // worst case UTF32LE output buffer size expansion
    l_sizOutbytes = (i_sizMaxbytes == 0 ? strlen(i_poszString) : i_sizMaxbytes) * sizeof(wchar_t);
    l_sizOutbytesleft = l_sizOutbytes;
    // allocate dynamic buffer for string
    l_poszBuffer = (char *) alloca(l_sizOutbytes + sizeof(wchar_t));
    assert(l_poszBuffer != 0);
    memset(l_poszBuffer, 0, l_sizOutbytes + sizeof(wchar_t));
    l_poszOutbuf = l_poszBuffer;
    l_sizInbytes = i_sizMaxbytes == 0 ? strlen(i_poszString) : i_sizMaxbytes;
    l_sizInbytesleft = l_sizInbytes;
    l_poszInbuf = (char *) i_poszString;
    l_sizReturn = iconv(l_ictCd, &l_poszInbuf, &l_sizInbytesleft, &l_poszOutbuf, &l_sizOutbytesleft);
    if (l_sizReturn == (size_t) -1) {
        fprintf(stderr, "%s(%d) = iconv() = -1, errno = %d, strerror = '%s', l_poszToencoding = '%s', l_poszFromencoding = '%s', l_sizInbytesleft = %lu, l_sizOutbytesleft = %lu, i_poszString = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_poszToencoding, l_poszFromencoding, l_sizInbytesleft, l_sizOutbytesleft, i_poszString);
    }
    l_inReturn = iconv_close(l_ictCd);
    if (l_inReturn == -1) {
        fprintf(stderr, "%s(%d) = iconv_close() = -1, errno = %d, strerror = '%s', l_poszToencoding = '%s', l_poszFromencoding = '%s', i_poszString = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_poszToencoding, l_poszFromencoding, i_poszString);
        abort();
    }
    u_pouniObject->m_poszCodepoints = (char *) calloc(1, l_sizOutbytes - l_sizOutbytesleft + sizeof(wchar_t));
    assert(u_pouniObject->m_poszCodepoints != 0);
    memcpy(u_pouniObject->m_poszCodepoints, l_poszBuffer, l_sizOutbytes - l_sizOutbytesleft);
    u_pouniObject->m_sizCodepoints = (l_sizOutbytes - l_sizOutbytesleft) / sizeof(wchar_t);
    u_pouniObject->m_sizBytes = l_sizOutbytes - l_sizOutbytesleft;
    l_sizConverted = l_sizInbytes - l_sizInbytesleft;
    return(l_sizConverted);
}

/**
 * @fn "char * Unicode_export_string(const struct Unicode * i_pouniObject, size_t i_sizMaxbytes, const char * i_poszEncoding)"
 * @brief Converts **Unicode** object value into a character string value
 * @details Converts **Unicode** object into an 8-bit, 16-bit or 32-bit wide
 * character string. The output buffer is allocated on the heap and returned
 * using a char pointer. The maximum number of bytes the output buffer can hold
 * is passed in **i_sizMaxbytes**. The output encoding is passed in the
 * **i_poszEncoding** parameter.  Any 8-bit, 16-bit or 32-bit wide character
 * encoding can be specified using **i_poszEncoding** as long as it is
 * supported by the implementation's **iconv(3)** C library function. The
 * **i_sizMaxbytes** parameter should contain the number of bytes (not
 * characters) that the output buffer can store INCLUDING any null terminator.
 * It is not an error for the **i_sizMaxbytes** parameter to be zero, but if it
 * is, the current maximum size in bytes of the **Unicode** object is used
 * instead. The return value is a pointer to the heap allocated converted
 * string which is null-terminated at the end.
 *
 * @note A char null byte is added at the end of the string, but if the
 * encoding is not 8-bit, then the calling routine will have to handle any
 * further null terminator bytes at the end of the converted value.
 *
 * @note The return type of (char *) does not imply that the buffer contents
 * pointed to should be interpreted as a null-terminated C string.  The calling
 * routine needs to make sure that the returned buffer contents are
 * re-interpreted as suitable for access to characters from the appropriate
 * character set encoding. This includes alignment on platforms that have tight
 * restrictions on alignment.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * char * l_poszEbcdic = 0;
 * l_pouniObject = Unicode_from_string("0123456789abcdef", 0, "ASCII");
 * l_poszEbcdic = Unicode_export_string(l_pouniObject, 0, "EBCDIC-US");
 * // do string processing...
 * free(l_poszEbcdic);
 * Unicode_delete(&l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @param[in] i_sizMaxbytes = Maximum number of output bytes to convert
 * @param[in] i_poszEncoding = Optional **iconv(3)** encoding of output with no "//" qualifiers
 * @retval "char *" = Pointer to heap allocated null-terminated converted
 * string or heap-allocated zero-length string if **i_pouniObject** is empty.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_poszEncoding is null
 * @exception abort(3) Aborts on iconv_open(3) failure
 * @exception abort(3) Aborts on iconv(3) failure
 * @exception abort(3) Aborts on iconv_close(3) failure
 */

char * Unicode_export_string(const struct Unicode * i_pouniObject, size_t i_sizMaxbytes, const char * i_poszEncoding)
{
    char l_archToencoding[UNICODE_BUFFER_MAX + 1];
    const char * l_poszFromencoding = "UTF32LE";
    size_t l_sizOutbytesleft = 0;
    size_t l_sizInbytesleft = 0;
    size_t l_sizReturn = 0;
    int l_inReturn = 0;
    char * l_poszOutbuf = 0;
    char * l_poszOutbufleft = 0;
    char * l_poszInbuf = 0;
    iconv_t l_ictCd;

    if (i_pouniObject == 0 || i_poszEncoding == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_poszEncoding = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_poszEncoding);
        abort();
    }
    if (Unicode_empty(i_pouniObject)) {
        l_poszOutbuf = (char *) calloc(1, sizeof(char));
        assert(l_poszOutbuf != 0);
        return(l_poszOutbuf);
    }
    sprintf(l_archToencoding, "%s//TRANSLIT", i_poszEncoding);
    l_ictCd = iconv_open(l_archToencoding, l_poszFromencoding);
    if (l_ictCd == (iconv_t) -1) {
        fprintf(stderr, "%s(%d) = iconv_open() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', m_poszCodepoints = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, i_pouniObject->m_poszCodepoints);
        abort();
    }
    l_sizOutbytesleft = i_sizMaxbytes == 0 ? i_pouniObject->m_sizBytes + sizeof(wchar_t) : i_sizMaxbytes;
    l_poszOutbuf = (char *) calloc(1, l_sizOutbytesleft + sizeof(wchar_t));
    assert(l_poszOutbuf != 0);
    l_poszOutbufleft = l_poszOutbuf;
    l_sizInbytesleft = i_pouniObject->m_sizBytes;
    l_poszInbuf = i_pouniObject->m_poszCodepoints;
    l_sizReturn = iconv(l_ictCd, &l_poszInbuf, &l_sizInbytesleft, &l_poszOutbufleft, &l_sizOutbytesleft);
    if (l_sizReturn == (size_t) -1) {
        fprintf(stderr, "%s(%d) = iconv() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', l_sizInbytesleft = %lu, l_sizOutbytesleft = %lu\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, l_sizInbytesleft, l_sizOutbytesleft);
    }
    l_inReturn = iconv_close(l_ictCd);
    if (l_inReturn == -1) {
        fprintf(stderr, "%s(%d) = iconv_close() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', m_poszCodepoints = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, i_pouniObject->m_poszCodepoints);
        abort();
    }
    return(l_poszOutbuf);
}

/**
 * @fn "struct Unicode * Unicode_from_string(const char * i_poszString, size_t i_sizMaxbytes, const char * i_poszEncoding)"
 * @brief Creates a new **Unicode** object from a null-terminated character string
 * @details Creates a **Unicode** object from a series of 8-bit, 16-bit or
 * 32-bit wide characters stored in a **char** null-terminated string. Input
 * buffer is pointed to by **i_poszString** using a **char** pointer. The
 * number of 8-bit bytes in the buffer to convert is passed in
 * **i_sizMaxbytes**. The **i_poszEncoding** parameter contains a character
 * encoding supported by the implementation's **iconv(3)** C library function.
 * The **i_sizMaxbytes** parameter should contain the number of bytes (not
 * characters) to convert, NOT INCLUDING any null terminator. If
 * **i_sizMaxbytes** is zero, then **strlen()** is used on **i_poszString** to
 * determine the appropriate value, which is only valid for 8-bit encodings.
 * If the **i_poszString** pointer is null then an empty **Unicode** object
 * is returned.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * char * l_poszEbcdic = 0;
 * l_pouniObject = Unicode_from_string("0123456789abcdef", 0, "ASCII");
 * l_poszEbcdic = Unicode_export_string(l_pouniObject, 0, "EBCDIC-US");
 * @endcode
 *
 * @param[in] i_poszString = Pointer to input **char** string buffer
 * @param[in] i_sizMaxbytes = Maximum number of input bytes to convert, use **strlen()** if zero
 * @param[in] i_poszEncoding = Null terminated string containing **iconv(3)** encoding of input
 * @retval "struct Unicode *" = Pointer to new **Unicode** object with codepoints from string
 * @exception abort(3) Aborts if i_poszEncoding is null
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_string(const char * i_poszString, size_t i_sizMaxbytes, const char * i_poszEncoding)
{
    struct Unicode * l_pouniObject = 0;

    if (i_poszEncoding == 0) {
        fprintf(stderr, "%s(%d) = i_poszEncoding = %p\n",
            __FILE__, __LINE__, i_poszEncoding);
        abort();
    }
    l_pouniObject = Unicode_new();
    if (i_poszString != 0) {
        Unicode_import_string(l_pouniObject, i_poszString, i_sizMaxbytes, i_poszEncoding);
    }
    return(l_pouniObject);
}

/**
 * @fn "struct Unicode * Unicode_from_int(int i_inValue)"
 * @brief Converts an **int** value into a new **Unicode** object
 * @details The passed parameter **l_inValue** containing a signed integer
 * value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * int l_inNumber = 10000;
 * l_pouniObject = Unicode_from_int(l_inNumber);
 * @endcode
 *
 * @param[in] i_inValue = Contains a signed integer value
 * @retval "struct Unicode *" = The new **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_int(int i_inValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%d", i_inValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "struct Unicode * Unicode_from_long(long i_loValue)"
 * @brief Converts a **long** integer value into a new **Unicode** object
 * @details The passed parameter **l_loValue** containing a signed **long** integer
 * value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * long l_loNumber = 1000000000L;
 * l_pouniObject = Unicode_from_long(l_loNumber);
 * @endcode
 *
 * @param[in] i_loValue = Contains a signed **long** integer value
 * @retval "struct Unicode *" The new **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_long(long i_loValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%ld", i_loValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "struct Unicode * Unicode_from_longlong(long long i_llValue)"
 * @brief Converts a **long long** integer value into a new **Unicode** object
 * @details The passed parameter **l_llValue** containing a signed **long long**
 * integer value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * long long l_llNumber = 1000000000000LL;
 * l_pouniObject = Unicode_from_longlong(l_llNumber);
 * @endcode
 *
 * @param[in] i_llValue = Contains a signed **long long** integer value
 * @retval "struct Unicode *" = The new **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_longlong(long long i_llValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%lld", i_llValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "struct Unicode * Unicode_from_float(float i_flValue)"
 * @brief Converts a **float** decimal value into a new **Unicode** object
 * @details The passed parameter **l_flValue** containing a signed
 * single-precision **float** value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * float l_flNumber = 10000.0F;
 * l_pouniObject = Unicode_from_float(l_flNumber);
 * @endcode
 *
 * @param[in] i_flValue = Contains a signed single-precision **float** value
 * @retval "struct Unicode *" = The updated **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_float(float i_flValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%.7e", i_flValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "struct Unicode * Unicode_from_double(double i_doValue)"
 * @brief Converts a **double** decimal value into a new **Unicode** object
 * @details The passed parameter **l_doValue** containing a signed
 * **double** decimal value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * double l_doNumber = 1.234567890e7;
 * l_pouniObject = Unicode_from_double(l_doNumber);
 * @endcode
 *
 * @param[in] i_doValue = Contains a signed **double** decimal value
 * @retval "struct Unicode *" = The new **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_double(double i_doValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%.15le", i_doValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "struct Unicode * Unicode_from_longdouble(long double i_ldValue)"
 * @brief Converts a long double decimal value into a new **Unicode** object
 * @details The passed parameter **l_ldValue** containing a signed
 * **long double** decimal value is converted into a **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = 0;
 * long double l_ldNumber = 1.234567890987654321e07L;
 * l_pouniObject = Unicode_from_longdouble(l_ldNumber);
 * @endcode
 *
 * @param[in] i_ldValue = Contains a signed **long double** decimal value
 * @retval "struct Unicode *" = The updated **Unicode** object
 * @see Unicode_import_string()
 */

struct Unicode * Unicode_from_longdouble(long double i_ldValue)
{
    char l_archBuffer[UNICODE_BUFFER_MAX + 1];
    struct Unicode * l_pouniObject = 0;

    setlocale(LC_NUMERIC, "C");
    sprintf(l_archBuffer, "%.18Le", i_ldValue);
    setlocale(LC_NUMERIC, "");
    l_pouniObject = Unicode_new();
    Unicode_import_string(l_pouniObject, l_archBuffer, 0, "ASCII");
    return l_pouniObject;
}

/**
 * @fn "int Unicode_to_int(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed integer value
 * @details Converts the current **Unicode** object to a signed integer value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("12345", 0, "ASCII");
 * int l_inNumber = Unicode_to_int(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "int" = Signed **int** value
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

int Unicode_to_int(const struct Unicode * i_pouniObject)
{
    int l_inValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%d", &l_inValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_inValue);
}

/**
 * @fn "long Unicode_to_long(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed **long** integer value
 * @details Converts the current **Unicode** object to a signed **long** integer value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("123456789", 0, "ASCII");
 * long l_loNumber = Unicode_to_long(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "long" = Signed **long** integer value
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

long Unicode_to_long(const struct Unicode * i_pouniObject)
{
    long l_loValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%ld", &l_loValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_loValue);
}

/**
 * @fn "long long Unicode_to_longlong(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed **long long** integer value
 * @details Converts the current **Unicode** object to a signed **long long**
 * integer value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("1234567890987654321", 0, "ASCII");
 * long long l_llNumber = Unicode_to_longlong(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "long long" = Signed **long long** integer value
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

long long Unicode_to_longlong(const struct Unicode * i_pouniObject)
{
    long long l_llValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%lld", &l_llValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_llValue);
}

/**
 * @fn "float Unicode_to_float(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed **float** decimal value
 * @details Converts the current **Unicode** object to a signed **float**
 * decimal value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("123.45", 0, "ASCII");
 * float l_flNumber = Unicode_to_float(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "float" = Signed **float** decimal value or zero on error
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

float Unicode_to_float(const struct Unicode * i_pouniObject)
{
    float l_flValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%e", &l_flValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_flValue);
}

/**
 * @fn "double Unicode_to_double(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed **double** decimal value
 * @details Converts the current **Unicode** object to a signed **double**
 * decimal value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("12345678.90", 0, "ASCII");
 * double l_doNumber = Unicode_to_double(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "double" = Signed **double** decimal value or zero on error
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

double Unicode_to_double(const struct Unicode * i_pouniObject)
{
    double l_doValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%le", &l_doValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_doValue);
}

/**
 * @fn "long double Unicode_to_longdouble(const struct Unicode * i_pouniObject)"
 * @brief Converts **Unicode** object value to signed **long double** decimal value
 * @details Converts the current **Unicode** object to a signed **long double**
 * decimal value
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("1.234567890987654321e99", 0, "ASCII");
 * long double l_ldNumber = Unicode_to_longdouble(l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object
 * @retval "long double" = Signed **long double** decimal value or zero on error
 * @exception abort(3) Aborts if i_pouniObject is null
 * @see Unicode_export_string()
 */

long double Unicode_to_longdouble(const struct Unicode * i_pouniObject)
{
    long double l_ldValue = 0;
    char * l_poszValue = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (!Unicode_empty(i_pouniObject)) {
        l_poszValue = Unicode_export_string(i_pouniObject, 0, "ASCII");
        setlocale(LC_NUMERIC, "C");
        sscanf(l_poszValue, "%Le", &l_ldValue);
        setlocale(LC_NUMERIC, "");
        free(l_poszValue);
    }
    return(l_ldValue);
}

/**
 * @fn "void Unicode_copy(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject)"
 * @brief Deep-copy clone of one **Unicode** object to another **Unicode** object
 * @details The contents of the first parameter **u_pouniObject** are cleared
 * and it's contents are replaced by a deep-copy clone of the codepoints of the
 * second parameter **i_pouniObject**.  No validation is done on the validity
 * of the codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_new();
 * struct Unicode * l_pouniClonable = Unicode_from_string("Data not to be modified", 0, "ASCII");
 * Unicode_copy(l_pouniObject, l_pouniClonable);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to copy destination **Unicode** object
 * @param[in] i_pouniObject = Input pointer to copy source **Unicode** object
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception assert(3) Aborts if calloc(3) call returns null
 * @see Unicode_clear()
 */

void Unicode_copy(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject)
{
    if (u_pouniObject == 0 || i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniObject);
        abort();
    }
    if (u_pouniObject != i_pouniObject) {
        Unicode_clear(u_pouniObject);
        if (!Unicode_empty(i_pouniObject)) {
            u_pouniObject->m_sizCodepoints = i_pouniObject->m_sizCodepoints;
            u_pouniObject->m_sizBytes = i_pouniObject->m_sizBytes;
            u_pouniObject->m_poszCodepoints = (char *) calloc(1, u_pouniObject->m_sizBytes + sizeof(wchar_t));
            assert(u_pouniObject->m_poszCodepoints != 0);
            memcpy(u_pouniObject->m_poszCodepoints, i_pouniObject->m_poszCodepoints, u_pouniObject->m_sizBytes);
        }
    }
}

/**
 * @fn "void Unicode_append(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject)"
 * @brief Append the codepoints of one **Unicode** object content to another
 * @details The codepoints of the first parameter **u_pouniObject** are updated
 * by appending the codepoints of the second parameter **i_pouniObject** to it.
 * No validation is done on the validity of the codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("First Part", 0, "ASCII");
 * struct Unicode * l_pouniAppend = Unicode_from_string("and Second Part", 0, "ASCII");
 * Unicode_append(l_pouniObject, l_pouniAppend);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_pouniObject = Input reference to **Unicode** object
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception assert(3) Aborts if realloc(3) call returns null
 */

void Unicode_append(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject)
{
    size_t l_sizCodepoints = 0;
    size_t l_sizBytes = 0;

    if (u_pouniObject == 0 || i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniObject);
        abort();
    }
    if (Unicode_empty(i_pouniObject))
        return;
    l_sizCodepoints = u_pouniObject->m_sizCodepoints + i_pouniObject->m_sizCodepoints;
    l_sizBytes = u_pouniObject->m_sizBytes + i_pouniObject->m_sizBytes;
    u_pouniObject->m_poszCodepoints = (char *) realloc(u_pouniObject->m_poszCodepoints, l_sizBytes + sizeof(wchar_t));
    assert(u_pouniObject->m_poszCodepoints != 0);
    memcpy(u_pouniObject->m_poszCodepoints + u_pouniObject->m_sizBytes, i_pouniObject->m_poszCodepoints, i_pouniObject->m_sizBytes);
    memset(u_pouniObject->m_poszCodepoints + l_sizBytes, 0, sizeof(wchar_t));
    u_pouniObject->m_sizCodepoints = l_sizCodepoints;
    u_pouniObject->m_sizBytes = l_sizBytes;
}

/**
 * @fn "void Unicode_append_multiple(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject, size_t i_sizCount)"
 * @brief Appends the codepoints of one **Unicode** object to another one or more times
 * @details Appends **Unicode** object codepoints of second parameter
 * **i_pouniObject** onto the existing codepoints of the first parameter
 * **u_pouniObject** for the number of copies passed in the third parameter
 * **i_sizCount**.  No validation is done on the codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_new();
 * struct Unicode * l_pouniHeader = Unicode_from_string(" Report Chapter ", 0, "ASCII");
 * struct Unicode * l_pouniFiller = Unicode_from_string("=", 0, "ASCII");
 * int l_inCount = 40 - Unicode_codepoints(l_pouniHeader);
 * Unicode_append_multiple(l_pouniObject, l_pouniFiller, l_inCount);
 * Unicode_append(l_pouniObject, l_pouniHeader);
 * Unicode_append_multiple(l_pouniObject, l_pouniFiller, l_inCount);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to destination **Unicode** object
 * @param[in] i_pouniObject = Input reference to source **Unicode** object
 * @param[in] i_sizCount = If greater than zero, the number of times to append
 * **i_pouniObject** codepoints to **u_pouniObject** codepoints
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception assert(3) Aborts if realloc(3) call returns null
 */

void Unicode_append_multiple(struct Unicode * u_pouniObject, const struct Unicode * i_pouniObject, size_t i_sizCount)
{
    wchar_t * l_powzCodepoints = 0;
    size_t l_sizCodepoints = 0;
    size_t l_sizBytes = 0;
    size_t l_sizOffset = 0;
    size_t l_sizCount = 0;

    if (u_pouniObject == 0 || i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniObject);
        abort();
    }
    if (Unicode_empty(i_pouniObject) || i_sizCount == 0)
        return;
    l_sizCodepoints = u_pouniObject->m_sizCodepoints + i_pouniObject->m_sizCodepoints * i_sizCount;
    l_sizBytes = u_pouniObject->m_sizBytes + i_pouniObject->m_sizBytes * i_sizCount;
    l_powzCodepoints = (wchar_t *) realloc(u_pouniObject->m_poszCodepoints, l_sizBytes + sizeof(wchar_t));
    assert(l_powzCodepoints != 0);
    l_powzCodepoints[l_sizCodepoints] = 0;
    u_pouniObject->m_poszCodepoints = (char *) l_powzCodepoints;
    for (l_sizCount = 0; l_sizCount < i_sizCount; l_sizCount++) {
        l_sizOffset = u_pouniObject->m_sizBytes + (i_pouniObject->m_sizBytes * l_sizCount);
        memcpy(u_pouniObject->m_poszCodepoints + l_sizOffset, i_pouniObject->m_poszCodepoints, i_pouniObject->m_sizBytes);
    }
    u_pouniObject->m_sizCodepoints = l_sizCodepoints;
    u_pouniObject->m_sizBytes = l_sizBytes;
}

/**
 * @fn "void Unicode_swap(struct Unicode * u_pouniObject, struct Unicode * u_pouniSwap)"
 * @brief Perform shallow-copy swap of two **Unicode** objects
 * @details A shallow-copy swap of the contents of both **Unicode** objects is
 * performed. This means the contents of only the structures are swapped. Any
 * codepoints currently referenced by either **Unicode** object is left alone.
 * Internally, direct struct assignment is used instead of **memcpy(3)** for
 * maximum code portability.
 *
 * #### Example ####
 *
 * @code
 * // move the first 50 objects from l_pounaArray1 to l_pounaArray2
 * struct UnicodeArray * l_pounaArray1 = UnicodeArray_new(101);
 * load_all_101_objects_into_array(l_pounaArray1);
 * struct UnicodeArray * l_pounaArray2 = UnicodeArray_new(50);
 * for (l_sizOffset = 0; l_sizOffset < 50; l_sizOffset++) {
 *     Unicode_swap(&l_pounaArray1->m_pouniObjects[l_sizOffset], &l_pounaArray2->m_pouniObjects[l_sizOffset]);
 * }
 * UnicodeArray_delete(l_pounaArray1);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to first **Unicode** object
 * @param[in,out] u_pouniSwap = Update pointer to second **Unicode** object
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if u_pouniSwap is null
 */

void Unicode_swap(struct Unicode * u_pouniObject, struct Unicode * u_pouniSwap)
{
    struct Unicode l_uniTemp;

    if (u_pouniObject == 0 || u_pouniSwap == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, u_pouniSwap = %p\n",
            __FILE__, __LINE__, u_pouniObject, u_pouniSwap);
        abort();
    }
    if (u_pouniObject != u_pouniSwap) {
        l_uniTemp = *u_pouniObject;
        *u_pouniObject = *u_pouniSwap;
        *u_pouniSwap = l_uniTemp;
    }
}

/**
 * @fn "int Unicode_find(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniFind, int i_inCount)"
 * @brief Finds a string of codepoints inside a **Unicode** object
 * @details Finds the passed input parameter **i_pouniFind** content in the
 * **Unicode** object **i_pouniObject**. When **i_inCount** is non-negative, then a count
 * of N+1 (0-based) matches are made scanning from left to right. If negative,
 * a count of N matches (1-based) are made scanning from right to left.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniPhrase = Unicode_from_string("His name is João Méroço and he arrived yesterday.", 0, "ASCII");
 * struct Unicode * l_pouniArrived = Unicode_from_string("arrived", 0, "ASCII");
 * struct Unicode * l_pouniPeriod = Unicode_from_string(".", 0, "ASCII");
 * int l_inArrived = Unicode_find(l_pouniPhrase, l_pouniArrived, 0);
 * int l_inPeriod = Unicode_find(l_pouniPhrase, l_pouniPeriod, -1);
 * struct Unicode * l_pouniWhen = Unicode_extract(l_pouniPhrase, l_inArrived, l_inPeriod - l_inArrived);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object to search in
 * @param[in] i_pouniFind = Input pointer to **Unicode** object to search for
 * @param[in] i_inCount = Count of number of matches to make
 * @retval "int" = Offset (0-based) of first (left-most) matching codepoint in
 * codepoints (not bytes) in **i_pouniObject** or -1 if not found.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniFind is null
 */

int Unicode_find(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniFind, int i_inCount)
{
    int l_inOffset = 0;
    int l_inEnd = 0;
    int l_inIter = 0;
    wchar_t * l_powzObject = 0;
    wchar_t * l_powzFind = 0;
    int l_inLastcounter = 0;
    int l_inCounter = 0;
    int l_inResult = -1;

    if (i_pouniObject == 0 || i_pouniFind == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniFind = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniFind);
        abort();
    }
    if (Unicode_empty(i_pouniObject) || Unicode_empty(i_pouniFind))
        return(-1);
    l_inOffset = i_inCount >= 0 ? 0 : i_pouniObject->m_sizCodepoints - 1;
    l_inEnd = i_inCount >= 0 ? (int) i_pouniObject->m_sizCodepoints : -1;
    l_inIter = i_inCount >= 0 ? 1 : -1;
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    l_powzFind = (wchar_t *) i_pouniFind->m_poszCodepoints;
    l_inLastcounter = i_inCount >= 0 ? i_inCount + 1 : -i_inCount;
    while (l_inCounter != l_inLastcounter) {
        if (l_inCounter != 0) {
            l_inOffset += l_inIter;
        }
        while (l_inOffset != l_inEnd) {
            if (*(l_powzObject + l_inOffset) == *l_powzFind) {
                if ((l_inResult = wcsncmp(l_powzObject + l_inOffset, l_powzFind, i_pouniFind->m_sizCodepoints)) == 0) {
                    break;
                }
            }
            l_inOffset += l_inIter;
        }
        l_inCounter++;
    }
    return(l_inResult == 0 ? l_inOffset : -1);
}

/**
 * @fn "struct Unicode * Unicode_extract(const struct Unicode * i_pouniObject, size_t i_sizOffset, size_t i_sizCount)"
 * @brief Extracts **Unicode** codepoints from a **Unicode** object
 * @details Extracts **Unicode** codepoints from the input parameter
 * **i_pouniObject** at the zero-based offset passed in the input parameter
 * **i_sizOffset** for a number of codepoints passed in the **i_sizCount**
 * input parameter. The codepoints extracted are returned in a new **Unicode**
 * object.  If the **i_sizOffset** value is outside the current **Unicode**
 * object content then an empty **Unicode** object will be returned. If the
 * **i_sizCount** value is larger than remaining number of codepoints, then the
 * return value will only contain any remaining codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniPhrase = Unicode_from_string("His name is João Méroço and he arrived yesterday.", 0, "ASCII");
 * struct Unicode * l_pouniArrived = Unicode_from_string("arrived", 0, "ASCII");
 * struct Unicode * l_pouniPeriod = Unicode_from_string(".", 0, "ASCII");
 * int l_inArrived = Unicode_find(l_pouniPhrase, l_pouniArrived, 0);
 * int l_inPeriod = Unicode_find(l_pouniPhrase, l_pouniPeriod, -1);
 * struct Unicode * l_pouniWhen = Unicode_extract(l_pouniPhrase, l_inArrived, l_inPeriod - l_inArrived);
 * @endcode
 *
 * @param[in] i_pouniObject = The input **Unicode** object to search
 * @param[in] i_sizOffset = Zero-based offset to start extraction process
 * @param[in] i_sizCount = Maximum number of codepoints to extract
 * @retval "struct Unicode *" = Pointer to **Unicode** object containing extracted codepoints
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception assert(3) Aborts if calloc(3) call returns null
 */

struct Unicode * Unicode_extract(const struct Unicode * i_pouniObject, size_t i_sizOffset, size_t i_sizCount)
{
    struct Unicode * l_pouniExtracted = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    l_pouniExtracted = Unicode_new();
    l_pouniExtracted->m_sizCodepoints = i_pouniObject->m_sizCodepoints - i_sizOffset;
    if (l_pouniExtracted->m_sizCodepoints > i_sizCount) {
        l_pouniExtracted->m_sizCodepoints = i_sizCount;
    }
    l_pouniExtracted->m_sizBytes = l_pouniExtracted->m_sizCodepoints * sizeof(wchar_t);
    l_pouniExtracted->m_poszCodepoints = (char *) calloc(l_pouniExtracted->m_sizCodepoints + 1, sizeof(wchar_t));
    assert(l_pouniExtracted->m_poszCodepoints != 0);
    memcpy(l_pouniExtracted->m_poszCodepoints, i_pouniObject->m_poszCodepoints + i_sizOffset * sizeof(wchar_t), l_pouniExtracted->m_sizBytes);
    return(l_pouniExtracted);
}

/**
 * @fn "void Unicode_replace(struct Unicode * u_pouniObject, const struct Unicode * i_pouniReplace, size_t i_sizOffset, size_t i_sizCount)"
 * @brief Replaces **Unicode** codepoints in one **Unicode** object with codepoints from another
 * @details Replaces **Unicode** codepoints in **u_pouniObject** beginning at the
 * **i_sizOffset** for a length of **i_sizCount** codepoints (not bytes) with the
 * codepoints contained in the **i_pouniReplace** parameter. If the offset is
 * outside the existing number of codepoints in **u_pouniObject** then no
 * codepoints are deleted, and the codepoints in **i_pouniReplace** will simply
 * be appended onto the existing codepoints of the **u_pouniObject** object.
 * If the **i_sizCount** value is greater than the number of remaining
 * codepoints, then all the remaining codepoints after **i_sizOffset** in the
 * **u_pouniObject** parameter will be deleted. In any case, all of the codepoints
 * of **i_pouniReplace** are either inserted or appended to the codepoints of
 * the **u_pouniObject** parameter.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniPhrase = Unicode_from_string("His name is %%NAME%%.", 0, "ASCII");
 * struct Unicode * l_pouniPattern = Unicode_from_string("%%NAME%%", 0, "ASCII");
 * struct Unicode * l_pouniName = Unicode_from_string("João Méroço", 0, "UTF8");
 * int l_inPattern = Unicode_find(l_pouniPhrase, l_pouniPattern, 0);
 * Unicode_replace(l_pouniPhrase, l_pouniName, l_inPattern, Unicode_codepoints(l_pouniPattern));
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to the existing **Unicode** object
 * @param[in] i_pouniReplace = Input pointer to the replacement **Unicode** object
 * @param[in] i_sizOffset = Zero-based offset to start replacement process
 * @param[in] i_sizCount = Count of existing UTF codepoints to replace
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if i_pouniReplace is null
 */

void Unicode_replace(struct Unicode * u_pouniObject, const struct Unicode * i_pouniReplace, size_t i_sizOffset, size_t i_sizCount)
{
    struct Unicode * l_pouniLhs = 0;
    struct Unicode * l_pouniRhs = 0;

    if (u_pouniObject == 0 || i_pouniReplace == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniReplace = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniReplace);
        abort();
    }
    l_pouniLhs = Unicode_extract(u_pouniObject, 0, i_sizOffset);
    l_pouniRhs = Unicode_extract(u_pouniObject, i_sizOffset + i_sizCount, u_pouniObject->m_sizCodepoints);
    Unicode_clear(u_pouniObject);
    Unicode_copy(u_pouniObject, l_pouniLhs);
    Unicode_append(u_pouniObject, i_pouniReplace);
    Unicode_append(u_pouniObject, l_pouniRhs);
    Unicode_delete(&l_pouniLhs);
    Unicode_delete(&l_pouniRhs);
}

/**
 * @fn "int Unicode_compare_ascendingstring(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)"
 * @brief String comparison of **Unicode** objects in ascending order
 * @details This method compares the two passed **Unicode** object parameters
 * as strings lexigraphically assuming internal **Unicode** encoding. If both
 * parameters have the same memory address, the same value, or are both empty
 * then they are considered equal and zero (0) is returned. An empty value is
 * always considered to be less than a non-empty value. If the first parameter
 * is less than the second parameter then negative one (-1) is returned. If the
 * first parameter is greater than the second parameter then one (1) is
 * returned.  Similar in concept to the strcmp() function, but works with
 * **Unicode** codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniAscii = Unicode_from_string("Joao Meroco", 0, "ASCII");
 * struct Unicode * l_pouniUtf8 = Unicode_from_string("João Méroço", 0, "UTF8");
 * int l_inCompare = Unicode_compare_ascendingstring(l_pouniAscii, l_pouniUtf8);
 * @endcode
 *
 * @param[in] i_pouniObject = Input **Unicode** object to be compared
 * @param[in] i_pouniCompare = Input **Unicode** object for comparison
 * @retval "int" = Returns -1, 0, 1 if **i_pouniObject** value is less than,
 * equal to or greater than the **i_pouniCompare** value lexigraphically
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniCompare is null
 */

int Unicode_compare_ascendingstring(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)
{
    size_t l_sizCodepoints = 0;
    int l_inResult = 0;

    if (i_pouniObject == 0 || i_pouniCompare == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniCompare = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniCompare);
        abort();
    }
    if (i_pouniObject == i_pouniCompare
        || i_pouniObject->m_poszCodepoints == i_pouniCompare->m_poszCodepoints
        || (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints == 0))
    {
        return(0);
    }
    if (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints > 0)
    {
        return(-1);
    }
    if (i_pouniObject->m_sizCodepoints > 0 && i_pouniCompare->m_sizCodepoints == 0)
    {
        return(1);
    }
    l_sizCodepoints = i_pouniObject->m_sizCodepoints > i_pouniCompare->m_sizCodepoints ?
        i_pouniObject->m_sizCodepoints : i_pouniCompare->m_sizCodepoints;
    l_inResult = wcsncmp((wchar_t *) i_pouniObject->m_poszCodepoints, (wchar_t *) i_pouniCompare->m_poszCodepoints, l_sizCodepoints);
    if (l_inResult < 0) return(-1);
    if (l_inResult > 0) return(1);
    return(0);
}

/**
 * @fn "int Unicode_compare_descendingstring(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)"
 * @brief String comparison of **Unicode** objects in descending order
 * @details This method compares the two passed **Unicode** object parameters
 * as strings lexigraphically assuming internal **Unicode** encoding. If both
 * parameters have the same memory address, the same value, or are both empty
 * then they are considered equal and zero (0) is returned. An empty value is
 * always considered to be less than a non-empty value. If the first parameter
 * is less than the second parameter then one (1) is returned. If the first
 * parameter is greater than the second parameter then negative one (-1) is
 * returned.  Similar in concept to the strcmp() function, but works with
 * **Unicode** codepoints.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniAscii = Unicode_from_string("Joao Meroco", 0, "ASCII");
 * struct Unicode * l_pouniUtf8 = Unicode_from_string("João Méroço", 0, "UTF8");
 * int l_inCompare = Unicode_compare_descendingstring(l_pouniAscii, l_pouniUtf8);
 * @endcode
 *
 * @param[in] i_pouniObject = Input **Unicode** object to be compared
 * @param[in] i_pouniCompare = Input **Unicode** object for comparison
 * @retval "int" = Returns 1, 0, -1 if **i_pouniObject** value is less than,
 * equal to or greater than the **i_pouniCompare** value lexigraphically
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniCompare is null
 */

int Unicode_compare_descendingstring(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)
{
    size_t l_sizCodepoints = 0;
    int l_inResult = 0;

    if (i_pouniObject == 0 || i_pouniCompare == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniCompare = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniCompare);
        abort();
    }
    if (i_pouniObject == i_pouniCompare
        || i_pouniObject->m_poszCodepoints == i_pouniCompare->m_poszCodepoints
        || (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints == 0))
    {
        return(0);
    }
    if (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints > 0)
    {
        return(1);
    }
    if (i_pouniObject->m_sizCodepoints > 0 && i_pouniCompare->m_sizCodepoints == 0)
    {
        return(-1);
    }
    l_sizCodepoints = i_pouniObject->m_sizCodepoints > i_pouniCompare->m_sizCodepoints ?
        i_pouniObject->m_sizCodepoints : i_pouniCompare->m_sizCodepoints;
    l_inResult = wcsncmp((wchar_t *) i_pouniObject->m_poszCodepoints, (wchar_t *) i_pouniCompare->m_poszCodepoints, l_sizCodepoints);
    if (l_inResult < 0) return(1);
    if (l_inResult > 0) return(-1);
    return(0);
}

/**
 * @fn "int Unicode_compare_ascendingnumeric(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)"
 * @brief Numeric comparison of **Unicode** objects in ascending order
 * @details This method compares the two passed **Unicode** object parameters
 * after converting them to floating point (long double) values. If both
 * parameters have the same memory address, the same value or are both empty
 * then zero (0) is returned. An empty value is always considered to be less
 * than a non-empty value. If the first parameter is less than the second
 * parameter then negative one (-1) is returned. If the first parameter is
 * greater than the second parameter then one (1) is returned.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniLong = Unicode_from_long(1000000L);
 * struct Unicode * l_pouniDouble = Unicode_from_double(1000000.0);
 * int l_inCompare = Unicode_compare_ascendingnumeric(l_pouniLong, l_pouniDouble);
 * @endcode
 *
 * @param[in] i_pouniObject = Input **Unicode** object to be compared
 * @param[in] i_pouniCompare = Input **Unicode** object for comparison
 * @retval "int" = Returns -1, 0, 1 if **i_pouniObject** value is less than,
 * equal to or greater than the **i_pouniCompare** value numerically
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniCompare is null
 */

int Unicode_compare_ascendingnumeric(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)
{
    long double l_ldObject = 0;
    long double l_ldCompare = 0;

    if (i_pouniObject == 0 || i_pouniCompare == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniCompare = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniCompare);
        abort();
    }
    if (i_pouniObject == i_pouniCompare
        || i_pouniObject->m_poszCodepoints == i_pouniCompare->m_poszCodepoints
        || (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints == 0))
    {
        return(0);
    }
    if (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints > 0)
    {
        return(-1);
    }
    if (i_pouniObject->m_sizCodepoints > 0 && i_pouniCompare->m_sizCodepoints == 0)
    {
        return(1);
    }
    l_ldObject = wcstold((wchar_t *) i_pouniObject->m_poszCodepoints, 0);
    l_ldCompare = wcstold((wchar_t *) i_pouniCompare->m_poszCodepoints, 0);
    if (l_ldObject < l_ldCompare) return(-1);
    if (l_ldObject > l_ldCompare) return(1);
    return(0);
}

/**
 * @fn "int Unicode_compare_descendingnumeric(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)"
 * @brief Numeric comparison of **Unicode** objects in descending order
 * @details This method compares the two passed **Unicode** object parameters
 * after converting them to floating point (long double) values. If both
 * parameters have the same memory address, the same value or are both empty
 * then zero (0) is returned. An empty value is always considered to be less
 * than a non-empty value. If the first parameter is less than the second
 * parameter then one (1) is returned. If the first parameter is greater than
 * the second parameter then negative one (-1) is returned.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniLong = Unicode_from_long(1000000L);
 * struct Unicode * l_pouniDouble = Unicode_from_double(1000000.0);
 * int l_inCompare = Unicode_compare_ascendingnumeric(l_pouniLong, l_pouniDouble);
 * @endcode
 *
 * @param[in] i_pouniObject = Input **Unicode** object to be compared
 * @param[in] i_pouniCompare = Input **Unicode** object for comparison
 * @retval "int" = Returns 1, 0, -1 if **i_pouniObject** value is less than,
 * equal to or greater than the **i_pouniCompare** value numerically
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniCompare is null
 */

int Unicode_compare_descendingnumeric(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniCompare)
{
    long double l_ldObject = 0;
    long double l_ldCompare = 0;

    if (i_pouniObject == 0 || i_pouniCompare == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniCompare = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniCompare);
        abort();
    }
    if (i_pouniObject == i_pouniCompare
        || i_pouniObject->m_poszCodepoints == i_pouniCompare->m_poszCodepoints
        || (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints == 0))
    {
        return(0);
    }
    if (i_pouniObject->m_sizCodepoints == 0 && i_pouniCompare->m_sizCodepoints > 0)
    {
        return(1);
    }
    if (i_pouniObject->m_sizCodepoints > 0 && i_pouniCompare->m_sizCodepoints == 0)
    {
        return(-1);
    }
    l_ldObject = wcstold((wchar_t *) i_pouniObject->m_poszCodepoints, 0);
    l_ldCompare = wcstold((wchar_t *) i_pouniCompare->m_poszCodepoints, 0);
    if (l_ldObject < l_ldCompare) return(1);
    if (l_ldObject > l_ldCompare) return(-1);
    return(0);
}

/**
 * @fn "struct Unicode * Unicode_uppercase(const struct Unicode * i_pouniObject)"
 * @brief Constructs new uppercase version of **Unicode** object
 * @details When used with a **Unicode** object the return value is the current
 * **Unicode** object contents in uppercase.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniName = Unicode_from_string("jOÃO mÉROÇO", 0, "UTF8");
 * struct Unicode * l_pouniUpper = Unicode_uppercase(l_pouniName);
 * struct Unicode * l_pouniLower = Unicode_lowercase(l_pouniName);
 * struct Unicode * l_pouniSwap = Unicode_swapcase(l_pouniName);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @retval "struct Unicode *" = Pointer to new **Unicode** object with
 * uppercased codepoints
 * @exception abort(3) Aborts if i_pouniObject is null
 */

struct Unicode * Unicode_uppercase(const struct Unicode * i_pouniObject)
{
    wchar_t * l_powzObject = 0;
    struct Unicode * l_pouniUppercased = 0;
    wchar_t * l_powzUppercased = 0;
    size_t l_sizCount = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    l_pouniUppercased = Unicode_new();
    l_pouniUppercased->m_poszCodepoints = (char *) calloc(i_pouniObject->m_sizCodepoints + 1, sizeof(wchar_t));
    assert(l_pouniUppercased->m_poszCodepoints != 0);
    l_powzUppercased = (wchar_t *) l_pouniUppercased->m_poszCodepoints;
    l_pouniUppercased->m_sizCodepoints = 0;
    l_pouniUppercased->m_sizBytes = 0;
    while (l_sizCount < i_pouniObject->m_sizCodepoints) {
        l_powzUppercased[l_sizCount] = towupper(l_powzObject[l_sizCount]);
        l_sizCount++;
    }
    l_pouniUppercased->m_sizCodepoints = l_sizCount;
    l_pouniUppercased->m_sizBytes = l_sizCount * sizeof(wchar_t);
    return(l_pouniUppercased);
}

/**
 * @fn "struct Unicode * Unicode_lowercase(const struct Unicode * i_pouniObject)"
 * @brief Constructs new lowercase version of **Unicode** object
 * @details When used with a **Unicode** object the return value is the current
 * **Unicode** object contents in lowercase.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniName = Unicode_from_string("jOÃO mÉROÇO", 0, "UTF8");
 * struct Unicode * l_pouniUpper = Unicode_uppercase(l_pouniName);
 * struct Unicode * l_pouniLower = Unicode_lowercase(l_pouniName);
 * struct Unicode * l_pouniSwap = Unicode_swapcase(l_pouniName);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @retval "struct Unicode *" = Pointer to new **Unicode** object with
 * lowercased codepoints
 * @exception abort(3) Aborts if i_pouniObject is null
 */

struct Unicode * Unicode_lowercase(const struct Unicode * i_pouniObject)
{
    wchar_t * l_powzObject = 0;
    struct Unicode * l_pouniLowercased = 0;
    wchar_t * l_powzLowercased = 0;
    size_t l_sizCount = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    l_pouniLowercased = Unicode_new();
    l_pouniLowercased->m_poszCodepoints = (char *) calloc(i_pouniObject->m_sizCodepoints + 1, sizeof(wchar_t));
    assert(l_pouniLowercased->m_poszCodepoints != 0);
    l_powzLowercased = (wchar_t *) l_pouniLowercased->m_poszCodepoints;
    l_pouniLowercased->m_sizCodepoints = 0;
    l_pouniLowercased->m_sizBytes = 0;
    while (l_sizCount < i_pouniObject->m_sizCodepoints) {
        l_powzLowercased[l_sizCount] = towlower(l_powzObject[l_sizCount]);
        l_sizCount++;
    }
    l_pouniLowercased->m_sizCodepoints = l_sizCount;
    l_pouniLowercased->m_sizBytes = l_sizCount * sizeof(wchar_t);
    return(l_pouniLowercased);
}

/**
 * @fn "struct Unicode * Unicode_swapcase(const struct Unicode * i_pouniObject)"
 * @brief Constructs new swapped lettercase version of **Unicode** object
 * @details When used with a **Unicode** object the return value is the current
 * **Unicode** object contents lettercase swapped, with uppercase converted to
 * lowercase, and lowercase converted to uppercase. Codepoints with no case are
 * not affected.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniName = Unicode_from_string("jOÃO mÉROÇO", 0, "UTF8");
 * struct Unicode * l_pouniUpper = Unicode_uppercase(l_pouniName);
 * struct Unicode * l_pouniLower = Unicode_lowercase(l_pouniName);
 * struct Unicode * l_pouniSwap = Unicode_swapcase(l_pouniName);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @retval "struct Unicode *" = Pointer to new **Unicode** object with swapped
 * lettercase codepoints
 * @exception abort(3) Aborts if i_pouniObject is null
 */

struct Unicode * Unicode_swapcase(const struct Unicode * i_pouniObject)
{
    wchar_t * l_powzObject = 0;
    struct Unicode * l_pouniSwapcased = 0;
    wchar_t * l_powzSwapcased = 0;
    size_t l_sizCount = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    l_pouniSwapcased = Unicode_new();
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    l_pouniSwapcased->m_poszCodepoints = (char *) calloc(i_pouniObject->m_sizCodepoints + 1, sizeof(wchar_t));
    assert(l_pouniSwapcased->m_poszCodepoints != 0);
    l_powzSwapcased = (wchar_t *) l_pouniSwapcased->m_poszCodepoints;
    l_pouniSwapcased->m_sizCodepoints = 0;
    l_pouniSwapcased->m_sizBytes = 0;
    while (l_sizCount < i_pouniObject->m_sizCodepoints) {
        if (iswupper(l_powzObject[l_sizCount]))
            l_powzSwapcased[l_sizCount] = towlower(l_powzObject[l_sizCount]);
        else if (iswlower(l_powzObject[l_sizCount]))
            l_powzSwapcased[l_sizCount] = towupper(l_powzObject[l_sizCount]);
        else
            l_powzSwapcased[l_sizCount] = l_powzObject[l_sizCount];
        l_sizCount++;
    }
    l_pouniSwapcased->m_sizCodepoints = l_sizCount;
    l_pouniSwapcased->m_sizBytes = l_sizCount * sizeof(wchar_t);
    return(l_pouniSwapcased);
}

/**
 * @fn "struct Unicode * Unicode_concatenate(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniConcatenate)"
 * @brief Constructs new **Unicode** object from the concatenation of of one
 * **Unicode** object to another
 * @details Constructs and returns a new **Unicode** object whose value is the
 * concatenated content of the **i_pouniObject** parameter with the
 * **i_pouniConcatenate** parameter.  While similar in operation to
 * **Unicode_append()**, the content of the passed input parameters are not
 * modified.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniFirst = Unicode_from_string("João ", 0, "UTF8");
 * struct Unicode * l_pouniLast = Unicode_from_string("Méroço", 0, "UTF8");
 * struct Unicode * l_pouniName = Unicode_concatenate(l_pouniFirst, l_pouniLast);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to lvalue **Unicode** object
 * @param[in] i_pouniConcatenate = Input pointer to rvalue **Unicode** object
 * @retval "struct Unicode *" = Pointer to new **Unicode** object with
 * concatenated codepoints
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniConcatenate is null
 */

struct Unicode * Unicode_concatenate(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniConcatenate)
{
    struct Unicode * l_pouniResult = 0;
    char * l_poszCodepoints = 0;
    size_t l_sizCodepoints = 0;
    size_t l_sizBytes = 0;

    if (i_pouniObject == 0 || i_pouniConcatenate == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniConcatenate = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniConcatenate);
        abort();
    }
    l_pouniResult = Unicode_new();
    l_poszCodepoints = (char *) calloc(i_pouniObject->m_sizCodepoints + i_pouniConcatenate->m_sizCodepoints + 1, sizeof(wchar_t));
    assert(l_poszCodepoints != 0);
    l_sizCodepoints = i_pouniObject->m_sizCodepoints + i_pouniConcatenate->m_sizCodepoints;
    l_sizBytes = i_pouniObject->m_sizBytes + i_pouniConcatenate->m_sizBytes;
    if (i_pouniObject->m_sizBytes != 0)
        memcpy(l_poszCodepoints, i_pouniObject->m_poszCodepoints, i_pouniObject->m_sizBytes);
    memcpy(l_poszCodepoints + i_pouniObject->m_sizBytes, i_pouniConcatenate->m_poszCodepoints, i_pouniConcatenate->m_sizBytes);
    l_pouniResult->m_poszCodepoints = l_poszCodepoints;
    l_pouniResult->m_sizCodepoints = l_sizCodepoints;
    l_pouniResult->m_sizBytes = l_sizBytes;
    return(l_pouniResult);
}

/**
 * @fn "struct UnicodeArray * Unicode_split(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniDelimiters, int i_inTrim)"
 * @brief Splits a **Unicode** object codepoints into tokens using delimiters
 * @details The codepoints of the input parameter **i_pouniObject** are split
 * into tokens based on the delimiter codepoints passed in the input parameter
 * **i_pouniDelimiters**. The return value is a newly constructed
 * **UnicodeArray** object which contains all of the tokens split into
 * individual **Unicode** objects.  Delimiters are treated as single
 * codepoints, and more than one can be specified.  The input parameter
 * **i_inTrim** can be set to the following values. Values of 1 and 3 can be
 * used to prevent zero-length tokens.
 * @li 0 = Each delimiter is significant between tokens
 * @li 1 = Multiple delimiters are trimmed to one delimiter between tokens
 * @li 2 = Each delimiter is significant at the start, end and between tokens
 * @li 3 = Mulitple delimiters are trimmed to one delimiter at start, end and between tokens
 *
 * @note For **i_inTrim** modes of 1 and 3 only the first (leftmost) delimiter
 * in **i_pouniObject** will be kept and any other additional delimiters after
 * it will be trimmed when multiple codepoints are passed in **i_pouniDelimiters**.
 *
 * @note The codepoints are scanned from left-to-right, and the left-most part
 * will be returned in the lowest-indexed **UnicodeArray** element and so on.
 * The **Unicode** objects returned in the **UnicodeArray** object return
 * pointers to the actual codepoints stored in the **i_pouniObject** input
 * parameter. This means that no heap allocations are used to copy the data.
 * So you should not use **Unicode_clear()** or **Unicode_delete()** on the
 * **Unicode** objects contained inside the **UnicodeArray** object. Just call
 * the **UnicodeArray_delete()** function to delete the **UnicodeArray**
 * object. When deleting the orginal content that was split, use the
 * **Unicode_delete()** function after **UnicodeArray_delete()** so that the
 * pointers in the **Unicode** objects do not point to unallocated memory.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniObject = Unicode_from_string("The quick brown fox jumps over the cow.", 0, "ASCII");
 * struct Unicode * l_pouniDelimiters = Unicode_from_string(" .", 0, "ASCII");
 * struct UnicodeArray * l_pounaArray = Unicode_split(i_pouniObject, l_pouniDelimiters, 1);
 * int l_inIndex = 0;
 * while (l_inIndex < l_pounaArray->m_sizObjects) {
 *     printf("%d: %s\n", Unicode_export_string(&l_pounaArray->m_pouniObjects[l_inIndex], 0, "ASCII"));
 *     l_inIndex++;
 * }
 * UnicodeArray_delete(&l_pounaArray);  // deletes array of objects pointing to original content
 * Unicode_delete(&l_pouniObject);      // deletes the original content
 * Unicode_delete(&l_pouniDelimiters);  // deletes the delimiters object
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_pouniDelimiters = One or more codepoints used as delimiters
 * @param[in] i_inTrim = Delimiter processing mode value
 * @retval "struct UnicodeArray *" = Pointer to **UnicodeArray** object
 * containing a heap-allocated array of **Unicode** objects pointing to content
 * inside **i_pouniObject**
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniDelimiters is null
 * @exception assert(3) Aborts if **UnicodeArray_new()** call returns null
 */

struct UnicodeArray * Unicode_split(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniDelimiters, int i_inTrim)
{
    wchar_t * l_powzObjectbeg = 0;
    wchar_t * l_powzObjectend = 0;
    wchar_t * l_powzDelimitersbeg = 0;
    wchar_t * l_powzDelimitersend = 0;
    wchar_t * l_powzTokenbeg = 0;
    wchar_t * l_powzTokenend = 0;
    wchar_t * l_powzObjectpos = 0;
    wchar_t * l_powzDelimiterspos = 0;
    int l_inIsdelimiter = 0;
    int l_inWasdelimiter = 0;
    int l_inIntoken = 0;
    size_t l_sizTokens = 0;
    struct UnicodeArray * l_pounaTokens = 0;

    if (i_pouniObject == 0 || i_pouniDelimiters == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniDelimiters = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniDelimiters);
        abort();
    }
    if (Unicode_empty(i_pouniObject)) {
        l_pounaTokens = UnicodeArray_new(0);
        assert(l_pounaTokens != 0);
        return(l_pounaTokens);
    }
    if (Unicode_empty(i_pouniDelimiters)) {
        l_pounaTokens = UnicodeArray_new(1);
        assert(l_pounaTokens != 0);
        l_pounaTokens->m_pouniObjects[0] = *i_pouniObject;
        l_pounaTokens->m_sizObjects = 1;
        return(l_pounaTokens);
    }
    // compute number of tokens "l_sizTokens"
    l_powzObjectbeg = (wchar_t *) i_pouniObject->m_poszCodepoints;
    l_powzObjectend = (wchar_t *) i_pouniObject->m_poszCodepoints + i_pouniObject->m_sizCodepoints;
    l_powzDelimitersbeg = (wchar_t *) i_pouniDelimiters->m_poszCodepoints;
    l_powzDelimitersend = (wchar_t *) i_pouniDelimiters->m_poszCodepoints + i_pouniDelimiters->m_sizCodepoints;
    l_inWasdelimiter = 0;
    l_inIntoken = 0;
    for (l_powzObjectpos = l_powzObjectbeg; l_powzObjectpos <= l_powzObjectend; l_powzObjectpos++) {
        l_inIsdelimiter = 0;
        for (l_powzDelimiterspos = l_powzDelimitersbeg; l_powzDelimiterspos < l_powzDelimitersend; l_powzDelimiterspos++) {
            if (*l_powzObjectpos == *l_powzDelimiterspos) {
                l_inIsdelimiter = 1;
                break;
            }
        }
        // trim extraneous delimiters only between tokens
        if (i_inTrim == 1 && l_inIsdelimiter == 1 && (l_inWasdelimiter == 1 || l_powzObjectpos == l_powzObjectbeg) && l_powzObjectpos < l_powzObjectend) {
            continue;
        }
        // delimiter or end of text found indicating end of token
        if ((i_inTrim == 0 || (i_inTrim == 1 && l_inWasdelimiter == 0)) && (l_inIsdelimiter == 1 || l_powzObjectpos == l_powzObjectend)) {
            l_sizTokens++;
            l_inWasdelimiter = 1;
            continue;
        }
        // trim extraneous delimiters before, after and between tokens
        if (i_inTrim == 3 && l_inIsdelimiter == 1 && l_inWasdelimiter == 1) {
            continue;
        }
        // delimiter found indicating end of token (non-delimited tokens are ignored)
        if ((i_inTrim == 2 || i_inTrim == 3) && l_inIntoken == 1 && l_inIsdelimiter == 1) {
            l_sizTokens++;
            l_inWasdelimiter = 1;
            continue;
        }
        // delimiter found indicating beginning of token (non-delimited tokens are ignored)
        if ((i_inTrim == 2 || i_inTrim == 3) && l_inIsdelimiter == 1) {
            l_inIntoken = 1;
            l_inWasdelimiter = 1;
            continue;
        }
        // indicate "last" codepoint found is non-delimiter
        if (l_inIsdelimiter == 0) {
            l_inWasdelimiter = 0;
        }
    }
    // allocate memory for the returned UnicodeArray object
    l_pounaTokens = UnicodeArray_new(l_sizTokens);
    assert(l_pounaTokens != 0);
    // calculate each token's pointer, codepoints and bytes inside Unicode object and store in UnicodeArray object
    l_sizTokens = 0;
    l_powzTokenbeg = 0;
    l_powzTokenend = 0;
    l_inWasdelimiter = 0;
    l_inIntoken = 0;
    for (l_powzObjectpos = l_powzObjectbeg; l_powzObjectpos <= l_powzObjectend; l_powzObjectpos++) {
        l_inIsdelimiter = 0;
        for (l_powzDelimiterspos = l_powzDelimitersbeg; l_powzDelimiterspos < l_powzDelimitersend; l_powzDelimiterspos++) {
            if (*l_powzObjectpos == *l_powzDelimiterspos) {
                l_inIsdelimiter = 1;
                break;
            }
        }
        // trim extraneous delimiters only between tokens
        if (i_inTrim == 1 && l_inIsdelimiter == 1 && (l_inWasdelimiter == 1 || l_powzObjectpos == l_powzObjectbeg) && l_powzObjectpos < l_powzObjectend) {
            continue;
        }
        // delimiter or end of text found indicating end of token
        if ((i_inTrim == 0 || (i_inTrim == 1 && l_inWasdelimiter == 0)) && (l_inIsdelimiter == 1 || l_powzObjectpos == l_powzObjectend)) {
            if (l_powzTokenbeg == 0 && l_powzTokenend == 0)
                l_powzTokenbeg = l_powzObjectpos;
            if (l_powzTokenbeg != 0 && l_powzTokenend == 0)
                l_powzTokenend = l_powzObjectpos;
            if (l_powzTokenbeg != 0 && l_powzTokenend != 0) {
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_poszCodepoints = (char *) l_powzTokenbeg;
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_sizCodepoints = l_powzTokenend - l_powzTokenbeg;
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_sizBytes = (l_powzTokenend - l_powzTokenbeg) * sizeof(wchar_t);
                l_pounaTokens->m_sizObjects = ++l_sizTokens;
                l_powzTokenbeg = 0;
                l_powzTokenend = 0;
            }
            l_inWasdelimiter = 1;
            continue;
        }
        // delimiter found indicating beginning of token
        if ((i_inTrim == 0 || i_inTrim == 1) && l_inIsdelimiter == 0 && l_powzTokenbeg == 0 && l_powzObjectpos < l_powzObjectend) {
            l_powzTokenbeg = l_powzObjectpos;
            l_inWasdelimiter = 0;
            continue;
        }
        // trim extraneous delimiters before, after and between tokens
        if (i_inTrim == 3 && l_inIsdelimiter == 1 && l_inWasdelimiter == 1) {
            continue;
        }
        // delimiter found indicating end of token (non-delimited tokens are ignored)
        if ((i_inTrim == 2 || i_inTrim == 3) && l_inIntoken == 1 && l_inIsdelimiter == 1) {
            if (l_powzTokenbeg == 0 && l_powzTokenend == 0)
                l_powzTokenbeg = l_powzObjectpos;
            if (l_powzTokenbeg != 0 && l_powzTokenend == 0)
                l_powzTokenend = l_powzObjectpos;
            if (l_powzTokenbeg != 0 && l_powzTokenend != 0) {
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_poszCodepoints = (char *) l_powzTokenbeg;
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_sizCodepoints = l_powzTokenend - l_powzTokenbeg;
                l_pounaTokens->m_pouniObjects[l_sizTokens].m_sizBytes = (l_powzTokenend - l_powzTokenbeg) * sizeof(wchar_t);
                l_pounaTokens->m_sizObjects = ++l_sizTokens;
                l_powzTokenbeg = 0;
                l_powzTokenend = 0;
            }
            l_inWasdelimiter = 1;
            continue;
        }
        // delimiter found indicating beginning of token (non-delimited tokens are ignored)
        if ((i_inTrim == 2 || i_inTrim == 3) && l_inIsdelimiter == 1) {
            l_inIntoken = 1;
            l_inWasdelimiter = 1;
            continue;
        }
        // non-delimiter found indicating beginning of token content (non-delimited tokens are ignored)
        if ((i_inTrim == 2 || i_inTrim == 3) && l_inIntoken == 1 && l_inIsdelimiter == 0 && l_powzTokenbeg == 0) {
            l_inWasdelimiter = 0;
            l_powzTokenbeg = l_powzObjectpos;
            continue;
        }
        // non-delimiter found
        if (l_inIsdelimiter == 0) {
            l_inWasdelimiter = 0;
        }
    }
    // return UnicodeArray object containing array of Unicode objects
    return(l_pounaTokens);
}

/**
 * @fn "struct Unicode * Unicode_join(struct UnicodeArray * i_pounaObject, const struct Unicode * i_pouniDelimiter, int i_inTrim)"
 * @brief Joins **UnicodeArray** object elements together separated by a delimiter
 * @details The codepoints of all the **Unicode** objects passed in the
 * **UnicodeArray** parameter are concatentated together and separated by the
 * delimiter codepoints passed in the **i_pouniDelimiter** parameter.  The
 * **Unicode** objects are all appended left-to-right.  The input parameter
 * **i_inTrim** can be set to the following values. Values of 1 and 3 can be
 * used to prevent zero-length tokens.
 * @li 0 = A delimiter will be put between all tokens
 * @li 1 = A delimiter will be put between all non-empty tokens
 * @li 2 = A delimiter will be put at the start, end and between all tokens
 * @li 3 = A delimiter will be put at the start, end and between all non-empty tokens
 *
 * If the **i_pouniDelimiter** parameter is empty, then the **Unicode**
 * objects' codepoints are simply concatenated together.  It is the
 * responsibility of the calling routine to free the memory of the returned
 * **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * struct UnicodeArray * l_pounaArray = UnicodeArray_new(101);
 * load_all_101_objects_into_array(l_pounaArray);
 * struct Unicode * l_pouniDelimiter = Unicode_from_string("|", 0, "ASCII");
 * struct Unicode * l_pouniObject = Unicode_join(l_pounaArray, l_pouniDelimiter, 1);
 * UnicodeArray_delete(&l_pounaArray);
 * Unicode_delete(&l_pouniDelimiter);
 * @endcode
 *
 * @param[in] i_pounaObject = Input pointer to a **UnicodeArray** object
 * @param[in] i_pouniDelimiter = Input delimiter **Unicode** codepoints
 * @param[in] i_inTrim = If non-zero ignores emtpy **Unicode** objects in **i_pounaObject**
 * @retval "struct Unicode *" = Heap allocated **Unicode** object with final value
 * of all **Unicode** objects concatenated together with the passed delimiter
 * @exception abort(3) Aborts if i_pounaObject is null
 * @exception abort(3) Aborts if i_pouniDelimiter is null
 * @exception assert(3) Aborts if calloc(3) call returns null
 */

struct Unicode * Unicode_join(struct UnicodeArray * i_pounaObject, const struct Unicode * i_pouniDelimiter, int i_inTrim)
{
    size_t l_sizCount = 0;
    size_t l_sizBytes = 0;
    size_t l_sizOffset = 0;
    struct Unicode * l_pouniObject = 0;

    if (i_pounaObject == 0 || i_pouniDelimiter == 0) {
        fprintf(stderr, "%s(%d) = i_pounaObject = %p, i_pouniDelimiter = %p\n",
            __FILE__, __LINE__, i_pounaObject, i_pouniDelimiter);
        abort();
    }
    // calculate space for initial delimiter
    if (i_inTrim == 2 || i_inTrim == 3) {
        l_sizBytes = i_pouniDelimiter->m_sizBytes;
    }
    // calculate total bytes used for content of Unicode objects plus delimiters
    for (l_sizCount = 0; l_sizCount < i_pounaObject->m_sizObjects; l_sizCount++) {
        if ((i_inTrim == 1 || i_inTrim == 3) && i_pounaObject->m_pouniObjects[l_sizCount].m_sizCodepoints == 0) {
            continue;
        }
        if (l_sizCount > 0) l_sizBytes += i_pouniDelimiter->m_sizBytes;
        l_sizBytes += i_pounaObject->m_pouniObjects[l_sizCount].m_sizBytes;
    }
    // calculate space for final delimiter
    if (i_inTrim == 2 || i_inTrim == 3) {
        l_sizBytes += i_pouniDelimiter->m_sizBytes;
    }
    // make sure size in bytes is on a **whcar_t** alignment
    if ((l_sizBytes % sizeof(wchar_t)) != 0) {
        l_sizBytes = (l_sizBytes + sizeof(wchar_t)) / sizeof(wchar_t) * sizeof(wchar_t);
    }
    // create new Unicode object to return joined results in
    l_pouniObject = Unicode_new();
    l_pouniObject->m_poszCodepoints = (char *) calloc(l_sizBytes + sizeof(wchar_t), sizeof(char));
    assert(l_pouniObject->m_poszCodepoints != 0);
    l_pouniObject->m_sizBytes = l_sizBytes;
    l_pouniObject->m_sizCodepoints = l_sizBytes / sizeof(wchar_t);
    // copy content for initial delimiter
    if (i_inTrim == 2 || i_inTrim == 3) {
        l_sizBytes = i_pouniDelimiter->m_sizBytes;
        memcpy(l_pouniObject->m_poszCodepoints + l_sizOffset, i_pouniDelimiter->m_poszCodepoints, l_sizBytes);
        l_sizOffset += l_sizBytes;
    }
    // copy content from UnicodeArray object
    for (l_sizCount = 0; l_sizCount < i_pounaObject->m_sizObjects; l_sizCount++) {
        if ((i_inTrim == 1 || i_inTrim == 3) && i_pounaObject->m_pouniObjects[l_sizCount].m_sizCodepoints == 0) {
            continue;
        }
        if (l_sizCount > 0) {
            l_sizBytes = i_pouniDelimiter->m_sizBytes;
            memcpy(l_pouniObject->m_poszCodepoints + l_sizOffset, i_pouniDelimiter->m_poszCodepoints, l_sizBytes);
            l_sizOffset += l_sizBytes;
        }
        l_sizBytes = i_pounaObject->m_pouniObjects[l_sizCount].m_sizBytes;
        memcpy(l_pouniObject->m_poszCodepoints + l_sizOffset, i_pounaObject->m_pouniObjects[l_sizCount].m_poszCodepoints, l_sizBytes);
        l_sizOffset += l_sizBytes;
    }
    // copy content for final delimiter
    if (i_inTrim == 2 || i_inTrim == 3) {
        l_sizBytes = i_pouniDelimiter->m_sizBytes;
        memcpy(l_pouniObject->m_poszCodepoints + l_sizOffset, i_pouniDelimiter->m_poszCodepoints, l_sizBytes);
        l_sizOffset += l_sizBytes;
    }
    return(l_pouniObject);
}

/**
 * @fn "char ** Unicode_from_array(const struct UnicodeArray * i_pounaObject)"
 * @brief Extracts dynamic array of C strings from **UnicodeArray** object
 * @details Extracts all **Unicode** objects contained in the **UnicodeArray**
 * object passed in the **i_pounaObject** input parameter. The return value is
 * a dynamic array of C strings that contain UTF8 encoded codepoints, with each
 * element corresponding to the same indexed element in the **UnicodeArray**
 * object.
 *
 * @note The returned dynamic array and all of it's C string elements are
 * allocated on the heap, so the calling routine must use **free(3)** to
 * deallocate the memory used by the C strings and then the dynamic array,
 * else a memory leak will occur.
 *
 * #### Example ####
 *
 * @code
 * struct Unicode * l_pouniDelimiters = Unicode_from_string(" ,*", 0, "ASCII");
 * struct Unicode * l_pouniObject = Unicode_from_string("The quick, brown fox jumps *over* the cow", 0, "ASCII");
 * struct UnicodeArray * l_pounaArray = Unicode_split(i_pouniObject, l_pouniDelimiters, 1);
 * char ** l_poposzWords = Unicode_from_array(l_pounaObject);
 * int l_inOffset = 0;
 * assert(strcmp(l_poposzWords[0], "The") == 0);
 * assert(strcmp(l_poposzWords[1], "quick") == 0);
 * assert(strcmp(l_poposzWords[2], "brown") == 0);
 * assert(strcmp(l_poposzWords[3], "fox") == 0);
 * assert(strcmp(l_poposzWords[4], "jumps") == 0);
 * assert(strcmp(l_poposzWords[5], "over") == 0);
 * assert(strcmp(l_poposzWords[6], "the") == 0);
 * assert(strcmp(l_poposzWords[7], "cow") == 0);
 * for (l_inOffset = 0; l_popoWords[l_inOffset] != 0; l_inOffset++) {
 *     free(l_poposzWords[l_inOffset];
 * }
 * free(l_poposzWords);
 * UnicodeArray_delete(&l_pounaObject);
 * Unicode_delete(&l_pouniObject);
 * Unicode_delete(&l_pouniDelimiters);
 * @endcode
 *
 * @param[in] i_pounaObject = Input pointer to **UnicodeArray** object
 * @retval "char **" = Pointer to dynamic heap-allocated array of pointers to
 * null-terminated C strings, with the end of list indicated by a null pointer.
 * @exception abort(3) Aborts if i_pounaObject is null
 * @exception abort(3) Aborts if calloc(3) returns a null pointer
 */

char ** Unicode_from_array(const struct UnicodeArray * i_pounaObject)
{
    char ** l_poposzStrings = 0;
    size_t l_sizCount = 0;
    size_t l_sizOffset = 0;

    if (i_pounaObject == 0) {
        fprintf(stderr, "%s(%d) = i_pounaObject = %p\n",
            __FILE__, __LINE__, i_pounaObject);
        abort();
    }
    l_sizCount = i_pounaObject->m_sizObjects;
    l_poposzStrings = (char **) calloc(l_sizCount + 1, sizeof(char *));
    assert(l_poposzStrings != 0);
    for (l_sizOffset = 0; l_sizOffset < l_sizCount; l_sizOffset++) {
        l_poposzStrings[l_sizOffset] = Unicode_export_string(&i_pounaObject->m_pouniObjects[l_sizOffset], 0, "UTF8");
    }
    return(l_poposzStrings);
}

/**
 * @fn "void Unicode_to_array(struct UnicodeArray * u_pounaObject, const char ** i_poposzValues)"
 * @brief Replaces **UnicodeArray** elements with dynamic C string array elements
 * @details The passed **UnicodeArray** object heap memory is deallocated, but
 * any existing **Unicode** member objects will not be deallocated and is
 * considered the responsibility of the calling routine. New **UnicodeArray**
 * object memory is then allocated on the heap.  The passed dynamic C string
 * array elements are then converted into new **Unicode** objects that are
 * referenced using the same element offsets inside the **UnicodeArray** object
 * as they are in the dynamic C string array. The **UnicodeArray** will be
 * dynamically resized as necessary to contain the new **Unicode** elements.
 * All of the returned **Unicode** object elements are newly allocated on the
 * heap. None of the passed dynamic C string array or it's elements are
 * modified or deallocated.
 *
 * @note This method does not deallocate any **Unicode** objects. It is the
 * responsibility of the calling routine to deallocate any existing **Unicode**
 * objects in the passed **UnicodeArray** object, as well as any newly returned
 * **Unicode** object allocated on the heap by this method.
 *
 * @note This routine calls the **UnicodeArray_delete()** method to delete the
 * old **UnicodeArray** object contents, and calls **UnicodeArray_new()** to
 * allocate new memory on the heap for it. Since it is being updated, the value
 * of the passed **UnicodeArray** object pointer is almost guaranteed to
 * change.
 *
 * @note It is important to keep in mind that **UnicodeArray** objects are
 * considered to be frame-like containers, and do not own their **Unicode**
 * object elements. It is the responsibility of the calling routine to take
 * ownership of any existing and new **Unicode** objects and deallocate them
 * when appropriate, or a memory leak may occur.
 *
 * #### Example ####
 *
 * @code
 * char * l_poposzStrings[] = { "The", "quick", "brown", "fox", "jumps", "over", "the", "cow", 0 };
 * struct UnicodeArray * l_pounaObject = UnicodeArray_new(1);
 * struct Unicode * l_pouniObject = 0;
 * int l_inOffset = 0;
 * Unicode_to_array(l_pounaObject, l_poposzStrings);
 * assert(l_pounaObject->m_sizObjects == 8);
 * for (l_inOffset = 0; l_inOffset < l_pounaObject->m_sizObjects; l_inOffset++) {
 *     l_pouniObject = Unicode_from_string(l_poposzStrings[l_inOffset], 0, "ASCII");
 *     assert(Unicode_compare_ascendingstring(l_pouniObject, &l_pounaObject->m_pouniObjects[l_inOffset]) == 0);
 *     Unicode_delete(&l_pouniObject);
 * }
 * UnicodeArray_delete(&l_pounaObject);
 * @endcode
 *
 * @param[in,out] u_pounaObject = Update pointer to existing **UnicodeArray** object
 * @param[in] i_poposzValues = Pointer to dynamic heap-allocated array of
 * pointers to null-terminated C strings, with the end of list indicated by a
 * null pointer
 * @retval "void" = None
 * @exception abort(3) Aborts if **u_pounaObject** or **i_poposzValues** is null
 * @exception assert(3) Aborts if UnicodeArray_new() call returns null
 */

void Unicode_to_array(struct UnicodeArray * u_pounaObject, const char ** i_poposzValues)
{
    struct Unicode * l_pouniObject = 0;
    size_t l_sizOffset = 0;
    size_t l_sizCount = 0;

    if (u_pounaObject == 0 || i_poposzValues == 0) {
        fprintf(stderr, "%s(%d) = u_pounaObject = %p, i_poposzValues = %p\n",
            __FILE__, __LINE__, u_pounaObject, i_poposzValues);
        abort();
    }
    // Count number of string values to be turned into elements
    l_sizCount = 0;
    while (i_poposzValues[l_sizCount] != 0) {
        l_sizCount++;
    }
    // Create new UnicodeArray content holding the strings converted to Unicode objects
    if (u_pounaObject->m_pouniObjects != 0) free(u_pounaObject->m_pouniObjects);
    u_pounaObject->m_pouniObjects = (struct Unicode *) calloc(l_sizCount, sizeof(struct Unicode));
    assert(u_pounaObject->m_pouniObjects != 0);
    u_pounaObject->m_sizObjects = l_sizCount;
    for (l_sizOffset = 0; l_sizOffset < l_sizCount; l_sizOffset++) {
        l_pouniObject = Unicode_from_string(i_poposzValues[l_sizOffset], 0, "UTF8");
        assert(l_pouniObject != 0);
        Unicode_swap(&u_pounaObject->m_pouniObjects[l_sizOffset], l_pouniObject);
    }
}

/**
 * @fn "char ** Unicode_from_subvalues(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS)"
 * @brief Extracts all subvalues at the specified level and splits them into a
 * __char **__ dynamic array of C strings
 * @details Retrieves a subvalue from within the 4-dimensional dynamic array
 * stored within the current **Unicode** object, splits it's contents on the
 * next lower level subvalue, and returns the split tokens as a dynamic
 * null-terminated array of C strings. The implementation of the 4-dimensional
 * dynamic array inside the **Unicode** object is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * extract a specific subvalue at a specific level. A zero value returns all
 * subvalues at a specific level. A negative value will extract a subvalue
 * relative to the last one at the specified level. If a negative value has an
 * absolute value greater than the number of subvalues at the specified level,
 * then the first subvalue will be extracted. It is an error for a non-zero
 * index to follow (to be to the right in the method parameter list) any index
 * with a value of zero. The extracted subvalue is returned.
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode** value
 * using delimiters that are UNICODE safe.
 *
 * @note The calling routine is responsible for freeing the heap-allocated
 * memory that is returned. First, for each non-null list element pointer
 * **free()** should be called on each __char *__ pointer in the list.  Finally
 * **free()** should be called on the __char **__ list pointer.
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_inFS = Level 1 index of level 2 subvalues or zero
 * @param[in] i_inGS = Level 2 index of level 3 subvalues or zero
 * @param[in] i_inRS = Level 3 index of level 4 subvalues or zero
 * @retval "char **" = Pointer to dynamic heap-allocated array of pointers to
 * null-terminated C strings, with the end of list indicated by a null pointer.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if subvalue index is zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 */

char ** Unicode_from_subvalues(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS)
{
    struct Unicode * l_pouniFS = 0;
    struct Unicode * l_pouniGS = 0;
    struct Unicode * l_pouniRS = 0;
    struct Unicode * l_pouniUS = 0;
    struct UnicodeArray * l_pounaResults = 0;
    struct UnicodeArray * l_pounaCount = 0;
    char ** l_poposzResults = 0;
    size_t l_sizCount = 0;
    size_t l_sizOffset = 0;
    size_t l_sizFS = 0;
    size_t l_sizGS = 0;
    size_t l_sizRS = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if ((i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS);
        abort();
    }
    if (Unicode_empty(i_pouniObject)) {
        l_poposzResults = (char **) calloc(1, sizeof(char *));
        assert(l_poposzResults != 0);
        return(l_poposzResults);
    }
    l_pouniFS = Unicode_from_string("\x1C", 1, "ASCII");
    l_pouniGS = Unicode_from_string("\x1D", 1, "ASCII");
    l_pouniRS = Unicode_from_string("\x1E", 1, "ASCII");
    l_pouniUS = Unicode_from_string("\x1F", 1, "ASCII");
    l_pounaResults = Unicode_split(i_pouniObject, l_pouniFS, 2);
    assert(l_pounaResults != 0);
    if (i_inFS != 0) {
        l_sizCount = l_pounaResults->m_sizObjects;
        if (i_inFS > 0) l_sizFS = i_inFS - 1;
        if (i_inFS < 0) l_sizFS = i_inFS + l_sizCount;
        if ((int) l_sizFS < 0) l_sizFS = 0;
        l_pounaCount = l_pounaResults;
        if (l_sizFS >= l_sizCount) {
            l_pounaResults = UnicodeArray_new(0);
            assert(l_pounaResults != 0);
        } else {
            l_pounaResults = Unicode_split(&l_pounaCount->m_pouniObjects[l_sizFS], l_pouniGS, 2);
            assert(l_pounaResults != 0);
        }
        UnicodeArray_delete(&l_pounaCount);
    }
    if (i_inGS != 0) {
        l_sizCount = l_pounaResults->m_sizObjects;
        if (i_inGS > 0) l_sizGS = i_inGS - 1;
        if (i_inGS < 0) l_sizGS = i_inGS + l_sizCount;
        if ((int) l_sizGS < 0) l_sizGS = 0;
        l_pounaCount = l_pounaResults;
        if (l_sizGS >= l_sizCount) {
            l_pounaResults = UnicodeArray_new(0);
            assert(l_pounaResults != 0);
        } else {
            l_pounaResults = Unicode_split(&l_pounaCount->m_pouniObjects[l_sizGS], l_pouniRS, 2);
            assert(l_pounaResults != 0);
        }
        UnicodeArray_delete(&l_pounaCount);
    }
    if (i_inRS != 0) {
        l_sizCount = l_pounaResults->m_sizObjects;
        if (i_inRS > 0) l_sizRS = i_inRS - 1;
        if (i_inRS < 0) l_sizRS = i_inRS + l_sizCount;
        if ((int) l_sizRS < 0) l_sizRS = 0;
        l_pounaCount = l_pounaResults;
        if (l_sizRS >= l_sizCount) {
            l_pounaResults = UnicodeArray_new(0);
            assert(l_pounaResults != 0);
        } else {
            l_pounaResults = Unicode_split(&l_pounaCount->m_pouniObjects[l_sizRS], l_pouniUS, 2);
            assert(l_pounaResults != 0);
        }
        UnicodeArray_delete(&l_pounaCount);
    }
    l_sizCount = l_pounaResults->m_sizObjects;
    l_poposzResults = (char **) calloc(l_sizCount + 1, sizeof(char *));
    assert(l_poposzResults != 0);
    for (l_sizOffset = 0; l_sizOffset < l_sizCount; l_sizOffset++) {
        l_poposzResults[l_sizOffset] = Unicode_export_string(&l_pounaResults->m_pouniObjects[l_sizOffset], 0, "UTF8");
    }
    UnicodeArray_delete(&l_pounaResults);
    Unicode_delete(&l_pouniFS);
    Unicode_delete(&l_pouniGS);
    Unicode_delete(&l_pouniRS);
    Unicode_delete(&l_pouniUS);
    return(l_poposzResults);
}

/**
 * @fn "void Unicode_to_subvalues(struct Unicode * u_pouniObject, const char ** i_poposzValues, int i_inFS, int i_inGS, int i_inRS)"
 * @brief Replaces all of the subvalues at a specified level with C strings
 * @details This replaces all of the subvalues one level below the right-most
 * non-zero passed subvalue index. For example, if **i_inFS** is zero, then all
 * of the level 1 subvalues (i.e. the entire content) of **u_pouniObject** will
 * get replaced by the passed C strings delimited by the level 1 delimiter
 * (FS). But if **i_inFS** is non-zero, then all of the level 2 subvalues within
 * the specified level 1 subvalue will be replaced delimited by the level 2
 * delimiter (GS), and so on.  Any existing subvalues within the 4-dimensional
 * dynamic array contained at the specified level within the passed **Unicode**
 * object will be deleted first. The implementation of the 4-dimensional
 * dynamic array is structured as follows:
 * - Object content contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * replace all lower-level subvalues at that specific index. A zero value will
 * replace subvalues at that specified level. A negative subvalue index value
 * is relative to the last one at the specified level. If a negative value has
 * an absolute value greater than the number of subvalues at the specified
 * level, then the first subvalue will be replaced. It is an error for a
 * non-zero index to follow (to be to the right in the method parameter list)
 * any index with a value of zero.
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode** value
 * using delimiters that are UNICODE safe.
 *
 * #### Example ####
 *
 * @code
 * char * l_poposzStrings[] = { "The", "quick", "brown", "fox", "jumps", "over", "the", "cow", 0 };
 * struct Unicode * l_pouniObject = Unicode_new();
 * Unicode_to_subvalues(l_pouniObject, l_poposzStrings, 0, 0, 0);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_poposzValues = Pointer to dynamic heap-allocated array of
 * pointers to null-terminated C strings, with the end of list indicated by a
 * null pointer
 * @param[in] i_inFS = Level 1 index of level 2 subvalues or zero
 * @param[in] i_inGS = Level 2 index of level 3 subvalues or zero
 * @param[in] i_inRS = Level 3 index of level 4 subvalues or zero
 * @retval "void" = None
 * @exception abort(3) Aborts if **u_pouniObject** or **i_poposzValues** is null
 * @exception abort(3) Aborts if subvalue index is zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero
 * @exception assert(3) Aborts if UnicodeArray_new() call returns null
 * @exception assert(3) Aborts if Unicode_split() call returns null
 */

void Unicode_to_subvalues(struct Unicode * u_pouniObject, const char ** i_poposzValues, int i_inFS, int i_inGS, int i_inRS)
{
    struct UnicodeArray * l_pounaLevel1 = 0;
    struct UnicodeArray * l_pounaLevel2 = 0;
    struct UnicodeArray * l_pounaLevel3 = 0;
    struct UnicodeArray * l_pounaRealloc = 0;
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct Unicode * l_pouniResult = 0;
    struct Unicode * l_pouniValue = 0;
    struct Unicode * l_pouniSubvalues = 0;
    struct Unicode * l_pouniFS = 0;
    struct Unicode * l_pouniGS = 0;
    struct Unicode * l_pouniRS = 0;
    struct Unicode * l_pouniUS = 0;
    size_t l_sizLevel1 = 0;
    size_t l_sizLevel2 = 0;
    size_t l_sizLevel3 = 0;
    size_t l_sizFS = 0;
    size_t l_sizGS = 0;
    size_t l_sizRS = 0;
    size_t l_sizOffset = 0;
    size_t l_sizCount = 0;

    if (u_pouniObject == 0 || i_poposzValues == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_poposzValues = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_poposzValues);
        abort();
    }
    if ((i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS);
        abort();
    }
    // Subvalue level 1 to 4 delimiter characters
    l_pouniFS = Unicode_from_string("\x1C", 1, "ASCII");
    l_pouniGS = Unicode_from_string("\x1D", 1, "ASCII");
    l_pouniRS = Unicode_from_string("\x1E", 1, "ASCII");
    l_pouniUS = Unicode_from_string("\x1F", 1, "ASCII");
    // Count number of string values to be turned into subvalues
    l_sizCount = 0;
    while (i_poposzValues[l_sizCount] != 0) {
        l_sizCount++;
    }
    // Create UnicodeArray object holding the strings converted to Unicode objects
    l_pounaSubvalues = UnicodeArray_new(l_sizCount);
    assert(l_pounaSubvalues != 0);
    for (l_sizOffset = 0; l_sizOffset < l_sizCount; l_sizOffset++) {
        l_pouniValue = Unicode_from_string(i_poposzValues[l_sizOffset], 0, "UTF8");
        assert(l_pouniValue != 0);
        Unicode_swap(&l_pounaSubvalues->m_pouniObjects[l_sizOffset], l_pouniValue);
    }
    // Replace complete contents of object with Level 1 delimited subvalues
    if (i_inFS == 0 && i_inGS == 0 && i_inRS == 0) {
        Unicode_delete(&u_pouniObject);
        u_pouniObject = Unicode_join(l_pounaSubvalues, l_pouniFS, 2);
        goto done;
    }
    // Create temporary Unicode dynamic array of Level 1 subvalues and resize as necessary
    if (i_inFS != 0) {
        l_pounaLevel1 = Unicode_split(u_pouniObject, l_pouniFS, 2);
        assert(l_pounaLevel1 != 0);
        l_sizLevel1 = l_pounaLevel1->m_sizObjects;
        if (i_inFS > 0) l_sizFS = i_inFS - 1;
        if (i_inFS < 0) l_sizFS = i_inFS + l_sizLevel1;
        if ((int) l_sizFS < 0) l_sizFS = 0;
        if (l_sizLevel1 <= l_sizFS) {
            l_pounaRealloc = l_pounaLevel1;
            l_pounaLevel1 = UnicodeArray_new(l_sizFS + 1);
            assert(l_pounaLevel1 != 0);
            for (l_sizOffset = 0; l_sizOffset < l_sizLevel1; l_sizOffset++) {
                Unicode_swap(&l_pounaLevel1->m_pouniObjects[l_sizOffset], &l_pounaRealloc->m_pouniObjects[l_sizOffset]);
            }
            l_sizLevel1 = l_sizFS + 1;
            UnicodeArray_delete(&l_pounaRealloc);
        }
    }
    // Create temporary Unicode dynamic array of Level 2 subvalues and resize as necessary
    if (i_inGS != 0) {
        l_pounaLevel2 = Unicode_split(&l_pounaLevel1->m_pouniObjects[l_sizFS], l_pouniGS, 2);
        assert(l_pounaLevel2 != 0);
        l_sizLevel2 = l_pounaLevel2->m_sizObjects;
        if (i_inGS > 0) l_sizGS = i_inGS - 1;
        if (i_inGS < 0) l_sizGS = i_inGS + l_sizLevel2;
        if ((int) l_sizGS < 0) l_sizGS = 0;
        if (l_sizLevel2 <= l_sizGS) {
            l_pounaRealloc = l_pounaLevel2;
            l_pounaLevel2 = UnicodeArray_new(l_sizGS + 1);
            assert(l_pounaLevel2 != 0);
            for (l_sizOffset = 0; l_sizOffset < l_sizLevel2; l_sizOffset++) {
                Unicode_swap(&l_pounaLevel2->m_pouniObjects[l_sizOffset], &l_pounaRealloc->m_pouniObjects[l_sizOffset]);
            }
            l_sizLevel2 = l_sizGS + 1;
            UnicodeArray_delete(&l_pounaRealloc);
        }
    }
    // Create temporary Unicode dynamic array of Level 3 subvalues and resize as necessary
    if (i_inRS != 0) {
        l_pounaLevel3 = Unicode_split(&l_pounaLevel2->m_pouniObjects[l_sizGS], l_pouniRS, 2);
        assert(l_pounaLevel3 != 0);
        l_sizLevel3 = l_pounaLevel3->m_sizObjects;
        if (i_inRS > 0) l_sizRS = i_inRS - 1;
        if (i_inRS < 0) l_sizRS = i_inRS + l_sizLevel3;
        if ((int) l_sizRS < 0) l_sizRS = 0;
        if (l_sizLevel3 <= l_sizRS) {
            l_pounaRealloc = l_pounaLevel3;
            l_pounaLevel3 = UnicodeArray_new(l_sizRS + 1);
            assert(l_pounaLevel3 != 0);
            for (l_sizOffset = 0; l_sizOffset < l_sizLevel3; l_sizOffset++) {
                Unicode_swap(&l_pounaLevel3->m_pouniObjects[l_sizOffset], &l_pounaRealloc->m_pouniObjects[l_sizOffset]);
            }
            l_sizLevel3 = l_sizRS + 1;
            UnicodeArray_delete(&l_pounaRealloc);
        }
    }
    //
    // Do not attempt to use Unicode_delete() or Unicode_clear() on any of the
    // UnicodeArray objects since Unicode_split() returned references to the
    // codepoints already existing inside u_pouniObject.
    //
    // Assemble C string values as level 1 Unicode object subvalues
    if (i_inFS != 0 && i_inGS == 0 && i_inRS == 0) {
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniGS, 2);
        Unicode_swap(&l_pounaLevel1->m_pouniObjects[l_sizFS], l_pouniSubvalues);
        l_pouniResult = Unicode_join(l_pounaLevel1, l_pouniFS, 2);
        Unicode_swap(u_pouniObject, l_pouniResult);
        UnicodeArray_delete(&l_pounaLevel1);
        goto done;
    }
    // Assemble C string values as level 2 Unicode object subvalues
    if (i_inFS != 0 && i_inGS != 0 && i_inRS == 0) {
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniRS, 2);
        Unicode_swap(&l_pounaLevel2->m_pouniObjects[l_sizGS], l_pouniSubvalues);
        l_pouniSubvalues = Unicode_join(l_pounaLevel2, l_pouniGS, 2);
        Unicode_swap(&l_pounaLevel1->m_pouniObjects[l_sizFS], l_pouniSubvalues);
        l_pouniResult = Unicode_join(l_pounaLevel1, l_pouniFS, 2);
        Unicode_swap(u_pouniObject, l_pouniResult);
        UnicodeArray_delete(&l_pounaLevel2);
        UnicodeArray_delete(&l_pounaLevel1);
        goto done;
    }
    // Assemble C string values as level 3 Unicode object subvalues
    if (i_inFS != 0 && i_inGS != 0 && i_inRS != 0) {
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniUS, 2);
        Unicode_swap(&l_pounaLevel3->m_pouniObjects[l_sizRS], l_pouniSubvalues);
        l_pouniSubvalues = Unicode_join(l_pounaLevel3, l_pouniRS, 2);
        Unicode_swap(&l_pounaLevel2->m_pouniObjects[l_sizGS], l_pouniSubvalues);
        l_pouniSubvalues = Unicode_join(l_pounaLevel2, l_pouniGS, 2);
        Unicode_swap(&l_pounaLevel1->m_pouniObjects[l_sizFS], l_pouniSubvalues);
        l_pouniResult = Unicode_join(l_pounaLevel1, l_pouniFS, 2);
        Unicode_swap(l_pouniResult, u_pouniObject);
        UnicodeArray_delete(&l_pounaLevel3);
        UnicodeArray_delete(&l_pounaLevel2);
        UnicodeArray_delete(&l_pounaLevel1);
        goto done;
    }
done:
    UnicodeArray_delete(&l_pounaSubvalues);
    Unicode_delete(&l_pouniFS);
    Unicode_delete(&l_pouniGS);
    Unicode_delete(&l_pouniRS);
    Unicode_delete(&l_pouniUS);
}

/**
 * @fn "int Unicode_count_subvalues(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS)"
 * @brief Count number of subvalues in a 4-dimensional dynamic array
 * @details Counts the next level of subvalues inside the specified subvalue
 * level of the 4-dimensional dynamic array stored within the **Unicode**
 * object. For example, that means that if the **i_inFS** level 1 index is the
 * only non-zero parameter, then the number of **GS** delimited level 2
 * subvalues in the level 1 subvalue passed in the **i_inFS** parameter. The
 * implementation of the dynamic array is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * The count is of subvalues one level deeper than the last non-zero index
 * specified.  Indicies can have a positive, zero or negative value. A positive
 * value will count subvalues one level deeper than the specified level. A zero
 * index is effectively ignored. A negative index is relative to the last
 * subvalue at the specified level. If a negative index has an absolute value
 * greater than the number of subvalues at the specified level, then the deeper
 * level subvalues contained in the first subvalue will be counted. It is an
 * error for a non-zero index to follow (to be to the right in the method
 * parameter list) any index with a value of zero. The subvalue count, not the
 * number of subvalue delimiters, is returned as the return value.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_count_subvalues(l_pouniObject, 0, 0, 0);@n
 *   Returns count of level 1 subvalues
 * @li Case 2. Unicode_count_subvalues(l_pouniObject, 3, 0, 0);@n
 *   Returns count of level 2 subvalues in third level 1 subvalue
 * @li Case 3. Unicode_count_subvalues(l_pouniObject, 3, 3, 0);@n
 *   Returns count of level 3 subvalues in third level 2 subvalue in third
 *   level 1 subvalue
 * @li Case 4. Unicode_count_subvalues(l_pouniObject, 3, 3, 3);@n
 *   Returns count of level 4 subvalues in third level 3 subvalue in third
 *   level 2 subvalue in third level 1 subvalue
 * @li Case 5. Unicode_count_subvalues(l_pouniObject, -1, 0, 0);@n
 *   Returns count of level 2 subvalues in last level 1 subvalue
 * @li Case 6. Unicode_count_subvalues(l_pouniObject, 0, 3, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode**
 * value using delimiters that are UNICODE safe.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero. Error
 * will cause a **Exception** to be thrown.
 *
 * #### Example ####
 *
 * @code
 * int l_inCount = 0;
 * char * l_poposzStrings[] = { "The", "quick", "brown", "fox", "jumps", "over", "the", "cow", 0 };
 * struct Unicode * l_pouniObject = Unicode_new();
 * Unicode_to_subvalues(l_pouniObject, l_poposzStrings, 1, 0, 0);
 * l_inCount = Unicode_count_subvalues(l_pouniObject, 1, 0, 0);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_inFS = Index of level 1 subvalue, or ignored if zero
 * @param[in] i_inGS = Index of level 2 subvalue, or ignored if zero
 * @param[in] i_inRS = Index of level 3 subvalue, or ignored if zero
 * @retval int = Count of subvalues specified by the passed indicies
 * @exception abort(3) Aborts if u_pouniObject or i_poposzValues is null
 * @exception abort(3) Aborts if subvalue index is zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 */

int Unicode_count_subvalues(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS)
{
    struct UnicodeArray * l_pounaSubvalues = 0;
    int l_arinIndex[4] = { i_inFS, i_inGS, i_inRS, 0 };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    struct Unicode * l_pouniDelimiter = 0;
    struct Unicode l_uniSubvalues;
    int l_inLevel = 0;
    int l_inCount = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if ((i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS);
        abort();
    }
    // No data is modified since we are only counting objects and pointers only point to existing data
    l_uniSubvalues = *i_pouniObject;
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        l_pounaSubvalues = Unicode_split(&l_uniSubvalues, l_pouniDelimiter, 2);
        Unicode_delete(&l_pouniDelimiter);
        if (l_pounaSubvalues->m_sizObjects == 0) {
            l_inCount = 0;
            break;
        }
        // Check for supplied zero index from passed parameters
        if (l_arinIndex[l_inLevel] == 0 ) {
            l_inCount = l_pounaSubvalues->m_sizObjects;
            break;
        }
        // Recompute passed index to 0-based positive array index
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_pounaSubvalues->m_sizObjects;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // Check for offset out of range of existing objects
        if ((size_t) l_arinIndex[l_inLevel] >= l_pounaSubvalues->m_sizObjects) {
            l_inCount = 0;
            break;
        }
        l_uniSubvalues = l_pounaSubvalues->m_pouniObjects[l_arinIndex[l_inLevel]];
        UnicodeArray_delete(&l_pounaSubvalues);
    }
    UnicodeArray_delete(&l_pounaSubvalues);
    return l_inCount;
}

/**
 * @fn "struct Unicode * Unicode_extract_subvalue(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Retrieve a subvalue from dynamic array with up to 4 dimensions
 * @details Retrieves a subvalue from within the 4-dimensional dynamic array
 * stored within the current **Unicode** object. The implementation of the dynamic
 * array is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * extract a specific subvalue at a specific level. A zero value returns all
 * subvalues at a specific level. A negative value will extract a subvalue
 * relative to the last one at the specified level. If a negative value has an
 * absolute value greater than the number of subvalues at the specified level,
 * then the first subvalue will be extracted. It is an error for a non-zero
 * index to follow (to be to the right in the method parameter list) any index
 * with a value of zero. The extracted subvalue is returned.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_extract_subvalue(3, 0, 0, 0);@n
 *   Returns third level 1 subvalue
 * @li Case 2. Unicode_extract_subvalue(3, 3, 0, 0);@n
 *   Returns third level 2 subvalue inside third level 1 subvalue
 * @li Case 3. Unicode_extract_subvalue(3, 3, 3, 0);@n
 *   Returns third level 3 subvalue inside third level 2 subvalue inside third
 *   level 1 subvalue
 * @li Case 4. Unicode_extract_subvalue(3, 3, 3, 3);@n
 *   Returns third level 4 subvalue inside third level 3 subvalue inside third
 *   level 2 subvalue inside third level 1 subvalue
 * @li Case 5. Unicode_extract_subvalue(-1, 0, 0, 0);@n
 *   Returns last level 1 subvalue
 * @li Case 6. Unicode_extract_subvalue(3, -1, 0, 0);@n
 *   Returns last level 2 subvalue  inside third level 1 subvalue
 * @li Case 7. Unicode_extract_subvalue(0, 3, 0, 0);@n
 *   Error! Positive indicies cannot follow zero indicies
 * @li Case 8. Unicode_extract_subvalue(0, 0, 0, 0);@n
 *   Returns entire content of **Unicode** object
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode** value
 * using delimiters that are UNICODE safe.
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_inFS = Index of level 1 subvalue, or zero for all level 1 to level 4 subvalues
 * @param[in] i_inGS = Index of level 2 subvalue, or zero for all level 2 to level 4 subvalues
 * @param[in] i_inRS = Index of level 3 subvalue, or zero for all level 3 to level 4 subvalues
 * @param[in] i_inUS = Index of level 4 subvalue, or zero for all level 4 subvalues
 * @retval "struct Unicode *" = Unicode subvalue that was extracted
 * @exception abort(3) Aborts if **i_pouniObject** is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 */

struct Unicode * Unicode_extract_subvalue(const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    int l_arinIndex[4] = { i_inFS, i_inGS, i_inRS, i_inUS };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    const char * l_poszAlldelimiters = { "\x1c\x1d\x1e\x1f" };
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct Unicode * l_pouniSubvalue = 0;
    struct Unicode * l_pouniDelimiter = 0;
    struct Unicode l_uniSubvalues;
    int l_inLevel = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if ((i_inUS > 0 && (i_inFS == 0 || i_inGS == 0 || i_inRS == 0))
        || (i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS);
        abort();
    }
    // No data is modified since we are only extracting objects and pointers only point to existing data
    l_uniSubvalues = *i_pouniObject;
    // If all passed indicies are zero then just return any value with no subvalues
    if (i_inFS == 0) {
        l_pouniDelimiter = Unicode_from_string(l_poszAlldelimiters, 4, "ASCII");
        l_pounaSubvalues = Unicode_split(&l_uniSubvalues, l_pouniDelimiter, 0);
        Unicode_delete(&l_pouniDelimiter);
        l_pouniSubvalue = Unicode_new();
        if (l_pounaSubvalues->m_sizObjects > 0) {
            Unicode_copy(l_pouniSubvalue, &l_pounaSubvalues->m_pouniObjects[0]);
        }
        UnicodeArray_delete(&l_pounaSubvalues);
        goto done;
    }
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        // Check for supplied zero index from passed parameters
        if (l_arinIndex[l_inLevel] == 0 ) {
            l_pouniSubvalue = Unicode_new();
            Unicode_copy(l_pouniSubvalue, &l_uniSubvalues);
            goto done;
        }
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        l_pounaSubvalues = Unicode_split(&l_uniSubvalues, l_pouniDelimiter, 2);
        Unicode_delete(&l_pouniDelimiter);
        if (l_pounaSubvalues->m_sizObjects == 0) {
            l_pouniSubvalue = Unicode_new();
            UnicodeArray_delete(&l_pounaSubvalues);
            goto done;
        }
        // Recompute passed index to 0-based positive array index
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_pounaSubvalues->m_sizObjects;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // If out of range of existing objects return empty Unicode object
        if ((size_t) l_arinIndex[l_inLevel] >= l_pounaSubvalues->m_sizObjects) {
            l_pouniSubvalue = Unicode_new();
            goto done;
        }
        l_uniSubvalues = l_pounaSubvalues->m_pouniObjects[l_arinIndex[l_inLevel]];
        UnicodeArray_delete(&l_pounaSubvalues);
    }
    l_pouniSubvalue = Unicode_new();
    Unicode_copy(l_pouniSubvalue, &l_uniSubvalues);
done:
    return(l_pouniSubvalue);
}

/**
 * @fn "void Unicode_replace_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniReplace, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Replace a subvalue in a dynamic array with up to 4 dimensions
 * @details Replaces a subvalue within the 4-dimensional dynamic array stored
 * within the **Unicode** object. The implementation of the dynamic array is
 * structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * replace a specific subvalue at a specific level. A zero value is effectively
 * ignored. A negative value will replace a subvalue relative to the last one
 * at the specified level. If a negative value has an absolute value greater
 * than the number of subvalues at the specified level, then the first subvalue
 * will be replaced. It is an error for a non-zero index to follow (to be to
 * the right in the method parameter list) any index with a value of zero. The
 * value of the **i_pouniReplace** input parameter replaces the subvalue
 * specified by the indicies.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 0, 0, 0, 0);@n
 *   Replaces entire object content with l_pouniReplace
 * @li Case 2. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 3, 0, 0, 0);@n
 *   Replaces third level 1 subvalue by l_pouniReplace
 * @li Case 3. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 3, 3, 0, 0);@n
 *   Replaces third level 2 subvalue inside third level 1 subvalue
 * @li Case 4. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 3, 3, 3, 0);@n
 *   Replaces third level 3 subvalue inside third level 2 subvalue inside third
 *   level 1 subvalue
 * @li Case 5. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 3, 3, 3, 3);@n
 *   Replaces third level 4 subvalue inside third level 3 subvalue inside third
 *   level 2 subvalue inside third level 1 subvalue
 * @li Case 6. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, -1, 0, 0, 0);@n
 *   Replaces last level 1 subvalue by l_pouniReplace
 * @li Case 7. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, -3, -3, 0, 0);@n
 *   Replaces third-from-last level 2 subvalue inside third-from-last level 3 subvalue
 * @li Case 8. Unicode_replace_subvalue(l_pouniObject, l_pouniReplace, 0, 3, -3, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple dimensions of subvalues inside of a single
 * **Unicode** value using delimiters that are UNICODE safe.
 *
 * @note Any negative index beyond the first subvalue will be set to the first
 * subvalue at that level. Any index supplied beyond the number of subvalues at
 * that level will cause sufficient empty subvalues to be appended at that
 * level in order to accomodate the specified subvalue index.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_pouniReplace = Unicode subvalue replacement value
 * @param[in] i_inFS = Index of level 1 subvalue, or zero for all level 1 subvalues
 * @param[in] i_inGS = Index of level 2 subvalue, or zero for all level 2 subvalues
 * @param[in] i_inRS = Index of level 3 subvalue, or zero for all level 3 subvalues
 * @param[in] i_inUS = Index of level 4 subvalue, or zero for all level 4 subvalues
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject or i_pouniReplace is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 * @exception assert(3) Aborts if Unicode_split() call returns null
 * @exception assert(3) Aborts if UnicodeArray_new() call returns null
 */

void Unicode_replace_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniReplace, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    struct UnicodeArray * l_arpounaLevel[4] = { 0, 0, 0, 0 };
    int l_arinLevel[4] = { 0, 0, 0, 0 };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    int l_arinParam[4] = { i_inFS, i_inGS, i_inRS, i_inUS };
    int l_arinIndex[4] = { 0, 0, 0, 0 };
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct UnicodeArray * l_pounaRealloc = 0;
    struct Unicode * l_pouniSubvalues = 0;
    struct Unicode * l_pouniDelimiter = 0;
    int l_inLevel = 0;
    int l_inOffset = 0;

    if (u_pouniObject == 0 || i_pouniReplace == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniReplace = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniReplace);
        abort();
    }
    if ((i_inUS > 0 && (i_inFS == 0 || i_inGS == 0 || i_inRS == 0))
        || (i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS);
        abort();
    }
    // If all indicies are zero then replace only the value of the Unicode object but do not affect any subvalues
    if (i_inFS == 0 && i_inGS == 0 && i_inRS == 0 && i_inUS == 0) {
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[0], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_pounaSubvalues = Unicode_split(u_pouniObject, l_pouniDelimiter, 0);
        assert(l_pounaSubvalues != 0);
        if (l_pounaSubvalues->m_sizObjects == 0) {
            l_pouniSubvalues = Unicode_new();
            Unicode_copy(l_pouniSubvalues, i_pouniReplace);
        } else {
            l_pounaSubvalues->m_pouniObjects[0] = *i_pouniReplace;
            l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniDelimiter, 0);
        }
        UnicodeArray_delete(&l_pounaSubvalues);
        goto done;
    }
    // No data is modified for now so l_pouniSubvalues acts as a reference to the existing data inside the update object
    l_pouniSubvalues = Unicode_new();
    *l_pouniSubvalues = *u_pouniObject;
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        // Check for supplied zero index from passed parameters
        if (l_arinParam[l_inLevel] == 0 ) {
            break;
        }
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_arpounaLevel[l_inLevel] = Unicode_split(l_pouniSubvalues, l_pouniDelimiter, 2);
        assert(l_arpounaLevel[l_inLevel] != 0);
        Unicode_delete(&l_pouniDelimiter);
        l_arinLevel[l_inLevel] = l_arpounaLevel[l_inLevel]->m_sizObjects;
        // Recompute passed index to 0-based positive array index
        l_arinIndex[l_inLevel] = l_arinParam[l_inLevel];
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_arinLevel[l_inLevel];
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // if number of elements is less than or equal to 0-based positive offset then add more elements
        if (l_arinLevel[l_inLevel] <= l_arinIndex[l_inLevel]) {
            l_pounaRealloc = UnicodeArray_new(l_arinIndex[l_inLevel] + 1);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel]; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            l_arinLevel[l_inLevel] = l_arinIndex[l_inLevel] + 1;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        *l_pouniSubvalues = l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]];
    }
    // Replace replace object at appropriate level and join into higher subvalue levels
    *l_pouniSubvalues = *i_pouniReplace;
    // Put replace object content within appropriate delimited subvalues
    for (l_inLevel = 3; l_inLevel >= 0; l_inLevel--) {
        if (l_arinParam[l_inLevel] != 0) {
            Unicode_swap(l_pouniSubvalues, &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]]);
            l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
            l_pouniSubvalues = Unicode_join(l_arpounaLevel[l_inLevel], l_pouniDelimiter, 2);
            Unicode_delete(&l_pouniDelimiter);
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
        }
    }
done:
    // Update Unicode object with final result
    Unicode_swap(l_pouniSubvalues, u_pouniObject);
    Unicode_delete(&l_pouniSubvalues);
}

/**
 * @fn "void Unicode_insert_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniInsert, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Insert a subvalue before another subvalue in a dynamic array with up to 4 dimensions
 * @details Inserts a subvalue before another subvalue within the 4-dimensional
 * dynamic array stored within the **Unicode** object. The implementation of the
 * dynamic array is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * insert a specific subvalue at a specific level. A zero value is effectively
 * ignored. A negative value will insert a subvalue relative to the last one at
 * the specified level. If a negative value has an absolute value greater than
 * the number of subvalues at the specified level, then the insertion will be
 * done before the first subvalue.  It is an error for a non-zero index to
 * follow (to be to the right in the method parameter list) any index with a
 * value of zero. The value of the **i_pouniInsert** input parameter inserts the
 * subvalue before the subvalue specified by the indicies.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 0, 0, 0, 0);@n
 *   Prepends subvalue to beginning of existing content
 * @li Case 2. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 3, 0, 0, 0);@n
 *   Inserts subvalue before third level 1 subvalue
 * @li Case 3. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 3, 3, 0, 0);@n
 *   Inserts subvalue before third level 2 subvalue inside third level 1 subvalue
 * @li Case 4. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 3, 3, 3, 0);@n
 *   Inserts subvalue before third level 3 subvalue inside third level 2 subvalue
 *   inside third level 1 subvalue
 * @li Case 5. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 3, 3, 3, 3);@n
 *   Inserts subvalue before third level 4 subvalue inside third level 3 subvalue
 *   inside third level 2 subvalue inside third level 1 subvalue
 * @li Case 6. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, -1, 0, 0, 0);@n
 *   Inserts subvalue before last level 1 subvalue
 * @li Case 7. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, -3, -3, 0, 0);@n
 *   Inserts subvalue before third-from-last level 2 subvalue inside third-from-last
 *   level 3 subvalue
 * @li Case 8. Unicode_insert_subvalue(l_pouniObject, l_pouniInsert, 0, 3, -3, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple dimensions of subvalues inside of a single
 * **Unicode** value using delimiters that are UNICODE safe.
 *
 * @note Any negative index beyond the first subvalue will be set to the first
 * subvalue at that level. Any index supplied beyond the number of subvalues at
 * that level will cause sufficient empty subvalues to be appended at that
 * level in order to accomodate the specified subvalue index.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_pouniInsert = Unicode subvalue insertion value
 * @param[in] i_inFS = Index of level 1 subvalue, or zero for all level 1 subvalues
 * @param[in] i_inGS = Index of level 2 subvalue, or zero for all level 2 subvalues
 * @param[in] i_inRS = Index of level 3 subvalue, or zero for all level 3 subvalues
 * @param[in] i_inUS = Index of level 4 subvalue, or zero for all level 4 subvalues
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject or i_pouniInsert is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 */

void Unicode_insert_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniInsert, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    struct UnicodeArray * l_arpounaLevel[4] = { 0, 0, 0, 0 };
    int l_arinLevel[4] = { 0, 0, 0, 0 };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    int l_arinParam[4] = { i_inFS, i_inGS, i_inRS, i_inUS };
    int l_arinIndex[4] = { 0, 0, 0, 0 };
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct UnicodeArray * l_pounaRealloc = 0;
    struct Unicode * l_pouniValue = 0;
    struct Unicode * l_pouniSubvalues = 0;
    struct Unicode * l_pouniDelimiter = 0;
    int l_inLevel = 0;
    int l_inOffset = 0;
    int l_inInsertlevel = 0;

    if (u_pouniObject == 0 || i_pouniInsert == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniInsert = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniInsert);
        abort();
    }
    if ((i_inUS > 0 && (i_inFS == 0 || i_inGS == 0 || i_inRS == 0))
        || (i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS);
        abort();
    }
    // Determine subvalue level where insertion operation will be performed
    // If all indicies are zero then prepend to existing Unicode object value
    if (i_inUS != 0) {
        l_inInsertlevel = 3;
    } else if (i_inRS != 0) {
        l_inInsertlevel = 2;
    } else if (i_inGS != 0) {
        l_inInsertlevel = 1;
    } else if (i_inFS != 0) {
        l_inInsertlevel = 0;
    } else {
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[0], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_pounaSubvalues = Unicode_split(u_pouniObject, l_pouniDelimiter, 0);
        assert(l_pounaSubvalues != 0);
        if (l_pounaSubvalues->m_sizObjects == 0) {
            l_pouniValue = Unicode_new();
            assert(l_pouniValue != 0);
            Unicode_copy(l_pouniValue, i_pouniInsert);
            UnicodeArray_delete(&l_pounaSubvalues);
            l_pounaSubvalues = UnicodeArray_new(1);
            assert(l_pounaSubvalues != 0);
        } else {
            l_pouniValue = Unicode_new();
            Unicode_copy(l_pouniValue, &l_pounaSubvalues->m_pouniObjects[0]);
            Unicode_replace(l_pouniValue, i_pouniInsert, 0, 0);
        }
        l_pounaSubvalues->m_pouniObjects[0] = *l_pouniValue;
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniDelimiter, 0);
        Unicode_delete(&l_pouniValue);
        Unicode_delete(&l_pouniDelimiter);
        UnicodeArray_delete(&l_pounaSubvalues);
        goto done;
    }
    // No data is modified for now so l_pouniSubvalues acts as a reference to the existing data inside the update object
    l_pouniSubvalues = Unicode_new();
    *l_pouniSubvalues = *u_pouniObject;
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        // Check for supplied zero index from passed parameters
        if (l_arinParam[l_inLevel] == 0 ) {
            break;
        }
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_arpounaLevel[l_inLevel] = Unicode_split(l_pouniSubvalues, l_pouniDelimiter, 2);
        assert(l_arpounaLevel[l_inLevel] != 0);
        Unicode_delete(&l_pouniDelimiter);
        l_arinLevel[l_inLevel] = l_arpounaLevel[l_inLevel]->m_sizObjects;
        // Recompute passed index to 0-based positive array index
        l_arinIndex[l_inLevel] = l_arinParam[l_inLevel];
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_arinLevel[l_inLevel];
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // if number of elements is less than or equal to 0-based positive offset then add more elements
        if (l_arinLevel[l_inLevel] <= l_arinIndex[l_inLevel]) {
            l_pounaRealloc = UnicodeArray_new(l_arinIndex[l_inLevel] + 1);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel]; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            l_arinLevel[l_inLevel] = l_arinIndex[l_inLevel] + 1;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        // If insert level then insert subvalue into Unicode object array
        if (l_inLevel == l_inInsertlevel) {
            l_arinLevel[l_inLevel]++;
            l_pounaRealloc = UnicodeArray_new(l_arinLevel[l_inLevel]);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel] - 1; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            for (l_inOffset = l_arinLevel[l_inLevel] - 1; l_inOffset > l_arinIndex[l_inLevel]; l_inOffset--) {
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_poszCodepoints = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_poszCodepoints;
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_sizCodepoints = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_sizCodepoints;
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_sizBytes = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_sizBytes;
            }
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel]].m_poszCodepoints = i_pouniInsert->m_poszCodepoints;
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel]].m_sizCodepoints = i_pouniInsert->m_sizCodepoints;
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel]].m_sizBytes = i_pouniInsert->m_sizBytes;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        *l_pouniSubvalues = l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]];
    }
    // Put insert object content within appropriate delimited subvalues
    for (l_inLevel = 3; l_inLevel >= 0; l_inLevel--) {
        if (l_arinParam[l_inLevel] != 0) {
            if (l_inLevel != l_inInsertlevel) {
                Unicode_swap(l_pouniSubvalues, &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]]);
            }
            l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
            assert(l_pouniDelimiter != 0);
            l_pouniSubvalues = Unicode_join(l_arpounaLevel[l_inLevel], l_pouniDelimiter, 2);
            Unicode_delete(&l_pouniDelimiter);
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
        }
    }
done:
    // Update Unicode object with final result
    Unicode_swap(l_pouniSubvalues, u_pouniObject);
    Unicode_delete(&l_pouniSubvalues);
}

/**
 * @fn "void Unicode_append_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniAppend, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Append a subvalue after another subvalue in a dynamic array with up to 4 dimensions
 * @details Appends a subvalue after another subvalue within the 4-dimensional
 * dynamic array stored within the **Unicode** object. The implementation of the
 * dynamic array is structured as follows:
 * @li Value contains level 1 subvalues delimited by FS characters
 * @li Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * @li Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * @li Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * append a specific subvalue at a specific level. A zero value is effectively
 * ignored. A negative value will append a subvalue relative to the last one at
 * the specified level. If a negative value has an absolute value greater than
 * the number of subvalues at the specified level, then the appendion will be
 * done after the first subvalue.  It is an error for a non-zero index to
 * follow (to be to the right in the method parameter list) any index with a
 * value of zero. The value of the **i_pouniAppend** input parameter appends the
 * subvalue after the subvalue specified by the indicies.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 0, 0, 0, 0);@n
 *   Appends subvalue to end of existing content
 * @li Case 2. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 3, 0, 0, 0);@n
 *   Appends subvalue after third level 1 subvalue
 * @li Case 3. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 3, 3, 0, 0);@n
 *   Appends subvalue after third level 2 subvalue inside third level 1 subvalue
 * @li Case 4. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 3, 3, 3, 0);@n
 *   Appends subvalue after third level 3 subvalue inside third level 2 subvalue
 *   inside third level 1 subvalue
 * @li Case 5. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 3, 3, 3, 3);@n
 *   Appends subvalue after third level 4 subvalue inside third level 3 subvalue
 *   inside third level 2 subvalue inside third level 1 subvalue
 * @li Case 6. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, -1, 0, 0, 0);@n
 *   Appends subvalue after last level 1 subvalue
 * @li Case 7. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, -3, -3, 0, 0);@n
 *   Appends subvalue after third-from-last level 2 subvalue inside third-from-last
 *   level 3 subvalue
 * @li Case 8. Unicode_append_subvalue(l_pouniObject, l_pouniAppend, 0, 3, -3, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple dimensions of subvalues inside of a single
 * **Unicode** value using delimiters that are UNICODE safe.
 *
 * @note Any negative index beyond the first subvalue will bet set to the first
 * subvalue at that level. Any index supplied beyond the number of subvalues at
 * that level will cause sufficient empty subvalues to be appended at that
 * level in order to accomodate the specified subvalue index.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_pouniAppend = Unicode subvalue append value
 * @param[in] i_inFS = Index of level 1 subvalue, or zero for all level 1 subvalues
 * @param[in] i_inGS = Index of level 2 subvalue, or zero for all level 2 subvalues
 * @param[in] i_inRS = Index of level 3 subvalue, or zero for all level 3 subvalues
 * @param[in] i_inUS = Index of level 4 subvalue, or zero for all level 4 subvalues
 * @retval "void" = None
 * @exception abort(3) Aborts if **u_pouniObject** or **i_pouniAppend** is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 */

void Unicode_append_subvalue(struct Unicode * u_pouniObject, const struct Unicode * i_pouniAppend, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    struct UnicodeArray * l_arpounaLevel[4] = { 0, 0, 0, 0 };
    int l_arinLevel[4] = { 0, 0, 0, 0 };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    int l_arinParam[4] = { i_inFS, i_inGS, i_inRS, i_inUS };
    int l_arinIndex[4] = { 0, 0, 0, 0 };
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct UnicodeArray * l_pounaRealloc = 0;
    struct Unicode * l_pouniValue = 0;
    struct Unicode * l_pouniSubvalues = 0;
    struct Unicode * l_pouniDelimiter = 0;
    int l_inLevel = 0;
    int l_inOffset = 0;
    int l_inAppendlevel = 0;

    if (u_pouniObject == 0 || i_pouniAppend == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p, i_pouniAppend = %p\n",
            __FILE__, __LINE__, u_pouniObject, i_pouniAppend);
        abort();
    }
    if ((i_inUS > 0 && (i_inFS == 0 || i_inGS == 0 || i_inRS == 0))
        || (i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS);
        abort();
    }
    // Determine subvalue level where append operation will be performed
    // If all indicies are zero then append content to existing Unicode object
    if (i_inUS != 0) {
        l_inAppendlevel = 3;
    } else if (i_inRS != 0) {
        l_inAppendlevel = 2;
    } else if (i_inGS != 0) {
        l_inAppendlevel = 1;
    } else if (i_inFS != 0) {
        l_inAppendlevel = 0;
    } else {
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[0], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_pounaSubvalues = Unicode_split(u_pouniObject, l_pouniDelimiter, 0);
        assert(l_pounaSubvalues != 0);
        if (l_pounaSubvalues->m_sizObjects == 0) {
            l_pouniValue = Unicode_new();
            assert(l_pouniValue != 0);
            Unicode_copy(l_pouniValue, i_pouniAppend);
            UnicodeArray_delete(&l_pounaSubvalues);
            l_pounaSubvalues = UnicodeArray_new(1);
            assert(l_pounaSubvalues != 0);
        } else {
            l_pouniValue = Unicode_new();
            Unicode_copy(l_pouniValue, &l_pounaSubvalues->m_pouniObjects[0]);
            Unicode_append(l_pouniValue, i_pouniAppend);
        }
        l_pounaSubvalues->m_pouniObjects[0] = *l_pouniValue;
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniDelimiter, 0);
        Unicode_delete(&l_pouniValue);
        Unicode_delete(&l_pouniDelimiter);
        UnicodeArray_delete(&l_pounaSubvalues);
        goto done;
    }
    // No data is modified for now so l_pouniSubvalues acts as a reference to the existing data inside the update object
    l_pouniSubvalues = Unicode_new();
    *l_pouniSubvalues = *u_pouniObject;
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        // Check for supplied zero index from passed parameters
        if (l_arinParam[l_inLevel] == 0 ) {
            break;
        }
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_arpounaLevel[l_inLevel] = Unicode_split(l_pouniSubvalues, l_pouniDelimiter, 2);
        assert(l_arpounaLevel[l_inLevel] != 0);
        Unicode_delete(&l_pouniDelimiter);
        l_arinLevel[l_inLevel] = l_arpounaLevel[l_inLevel]->m_sizObjects;
        // Recompute passed index to 0-based positive array index
        l_arinIndex[l_inLevel] = l_arinParam[l_inLevel];
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_arinLevel[l_inLevel];
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // if number of elements is less than or equal to 0-based positive offset then add more elements
        if (l_arinLevel[l_inLevel] <= l_arinIndex[l_inLevel]) {
            l_pounaRealloc = UnicodeArray_new(l_arinIndex[l_inLevel] + 1);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel]; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            l_arinLevel[l_inLevel] = l_arinIndex[l_inLevel] + 1;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        // If append level then append subvalue into Unicode object array
        if (l_inLevel == l_inAppendlevel) {
            l_arinLevel[l_inLevel]++;
            l_pounaRealloc = UnicodeArray_new(l_arinLevel[l_inLevel]);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel] - 1; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            for (l_inOffset = l_arinLevel[l_inLevel] - 1; l_inOffset > l_arinIndex[l_inLevel] + 1; l_inOffset--) {
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_poszCodepoints = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_poszCodepoints;
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_sizCodepoints = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_sizCodepoints;
                l_pounaRealloc->m_pouniObjects[l_inOffset].m_sizBytes = l_pounaRealloc->m_pouniObjects[l_inOffset - 1].m_sizBytes;
            }
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel] + 1].m_poszCodepoints = i_pouniAppend->m_poszCodepoints;
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel] + 1].m_sizCodepoints = i_pouniAppend->m_sizCodepoints;
            l_pounaRealloc->m_pouniObjects[l_arinIndex[l_inLevel] + 1].m_sizBytes = i_pouniAppend->m_sizBytes;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        *l_pouniSubvalues = l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]];
    }
    // Put append object content within appropriate delimited subvalues
    for (l_inLevel = 3; l_inLevel >= 0; l_inLevel--) {
        if (l_arinParam[l_inLevel] != 0) {
            if (l_inLevel != l_inAppendlevel) {
                Unicode_swap(l_pouniSubvalues, &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]]);
            }
            l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
            assert(l_pouniDelimiter != 0);
            l_pouniSubvalues = Unicode_join(l_arpounaLevel[l_inLevel], l_pouniDelimiter, 2);
            Unicode_delete(&l_pouniDelimiter);
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
        }
    }
done:
    // Update Unicode object with final result
    Unicode_swap(l_pouniSubvalues, u_pouniObject);
    Unicode_delete(&l_pouniSubvalues);
}

/**
 * @fn "void Unicode_delete_subvalue(struct Unicode * u_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Delete a subvalue in a dynamic array with up to 4 dimensions
 * @details Deletes a subvalue within the 4-dimensional dynamic array stored
 * within the **Unicode** object. The implementation of the dynamic array is
 * structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive, zero or negative value. A positive value will
 * delete a specific subvalue at a specific level. A zero value is effectively
 * ignored. A negative value will delete a subvalue relative to the last one at
 * the specified level. If a negative value has an absolute value greater than
 * the number of subvalues at the specified level, then the deletion will be
 * done to the specified subvalue.  It is an error for a non-zero index to
 * follow (to be to the right in the method parameter list) any index with a
 * value of zero.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_delete_subvalue(l_pouniObject, 0, 0, 0, 0);@n
 *   Deletes content of existing Unicode object like Unicode_clear()
 * @li Case 2. Unicode_delete_subvalue(l_pouniObject, 3, 0, 0, 0);@n
 *   Deletes third level 1 subvalue
 * @li Case 3. Unicode_delete_subvalue(l_pouniObject, 3, 3, 0, 0);@n
 *   Deletes third level 2 subvalue inside third level 1 subvalue
 * @li Case 4. Unicode_delete_subvalue(l_pouniObject, 3, 3, 3, 0);@n
 *   Deletes third level 3 subvalue inside third level 2 subvalue inside third
 *   level 1 subvalue
 * @li Case 5. Unicode_delete_subvalue(l_pouniObject, 3, 3, 3, 3);@n
 *   Deletes third level 4 subvalue inside third level 3 subvalue inside third
 *   level 2 subvalue inside third level 1 subvalue
 * @li Case 6. Unicode_delete_subvalue(l_pouniObject, -1, 0, 0, 0);@n
 *   Deletes last level 1 subvalue
 * @li Case 7. Unicode_delete_subvalue(l_pouniObject, -3, -3, 0, 0);@n
 *   Deletes third-from-last level 2 subvalue inside third-from-last level 3
 *   subvalue
 * @li Case 8. Unicode_delete_subvalue(l_pouniObject, 0, 3, -3, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple dimensions of subvalues inside of a single
 * **Unicode** value using delimiters that are UNICODE safe.
 *
 * @note Any negative index beyond the first subvalue will be set to the first
 * subvalue at that level. Any index supplied beyond the number of subvalues at
 * that level will be ignored.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 *
 * @param[in,out] u_pouniObject = Update pointer to existing **Unicode** object
 * @param[in] i_inFS = Index of level 1 subvalue, or zero for all level 1 subvalues
 * @param[in] i_inGS = Index of level 2 subvalue, or zero for all level 2 subvalues
 * @param[in] i_inRS = Index of level 3 subvalue, or zero for all level 3 subvalues
 * @param[in] i_inUS = Index of level 4 subvalue, or zero for all level 4 subvalues
 * @retval "void" = None
 * @exception abort(3) Aborts if **u_pouniObject** is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 */

void Unicode_delete_subvalue(struct Unicode * u_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    struct UnicodeArray * l_arpounaLevel[4] = { 0, 0, 0, 0 };
    int l_arinLevel[4] = { 0, 0, 0, 0 };
    const char * l_arposzDelimiters[4] = { "\x1c", "\x1d", "\x1e", "\x1f" };
    int l_arinParam[4] = { i_inFS, i_inGS, i_inRS, i_inUS };
    int l_arinIndex[4] = { 0, 0, 0, 0 };
    struct UnicodeArray * l_pounaSubvalues = 0;
    struct UnicodeArray * l_pounaRealloc = 0;
    struct Unicode * l_pouniSubvalues = 0;
    struct Unicode * l_pouniDelimiter = 0;
    int l_inLevel = 0;
    int l_inOffset = 0;
    int l_inDeletelevel = 0;

    if (u_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject);
        abort();
    }
    if ((i_inUS > 0 && (i_inFS == 0 || i_inGS == 0 || i_inRS == 0))
        || (i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS);
        abort();
    }
    // Determine subvalue level where deletion operation will be performed
    // If all indicies are zero then delete value and ignore subvalues
    if (i_inUS != 0) {
        l_inDeletelevel = 3;
    } else if (i_inRS != 0) {
        l_inDeletelevel = 2;
    } else if (i_inGS != 0) {
        l_inDeletelevel = 1;
    } else if (i_inFS != 0) {
        l_inDeletelevel = 0;
    } else {
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[0], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_pounaSubvalues = Unicode_split(u_pouniObject, l_pouniDelimiter, 0);
        assert(l_pounaSubvalues != 0);
        if (l_pounaSubvalues->m_sizObjects > 0) {
            l_pounaSubvalues->m_pouniObjects[0].m_poszCodepoints = 0;
            l_pounaSubvalues->m_pouniObjects[0].m_sizCodepoints = 0;
            l_pounaSubvalues->m_pouniObjects[0].m_sizBytes = 0;
        }
        l_pouniSubvalues = Unicode_join(l_pounaSubvalues, l_pouniDelimiter, 0);
        Unicode_delete(&l_pouniDelimiter);
        UnicodeArray_delete(&l_pounaSubvalues);
        goto done;
    }
    // No data is modified for now so l_pouniSubvalues acts as a reference to the existing data inside the update object
    l_pouniSubvalues = u_pouniObject;
    // Loop through each level of subvalue to refine the returned subvalue
    for (l_inLevel = 0; l_inLevel < 4; l_inLevel++) {
        // Check for supplied zero index from passed parameters
        if (l_arinParam[l_inLevel] == 0 ) {
            break;
        }
        l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
        assert(l_pouniDelimiter != 0);
        l_arpounaLevel[l_inLevel] = Unicode_split(l_pouniSubvalues, l_pouniDelimiter, 2);
        assert(l_arpounaLevel[l_inLevel] != 0);
        Unicode_delete(&l_pouniDelimiter);
        l_arinLevel[l_inLevel] = l_arpounaLevel[l_inLevel]->m_sizObjects;
        // Recompute passed index to 0-based positive array index
        l_arinIndex[l_inLevel] = l_arinParam[l_inLevel];
        if (l_arinIndex[l_inLevel] > 0) l_arinIndex[l_inLevel]--;
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] += l_arinLevel[l_inLevel];
        if (l_arinIndex[l_inLevel] < 0) l_arinIndex[l_inLevel] = 0;
        // if number of elements is less than or equal to 0-based positive offset then add more elements
        if (l_arinLevel[l_inLevel] <= l_arinIndex[l_inLevel]) {
            l_pounaRealloc = UnicodeArray_new(l_arinIndex[l_inLevel] + 1);
            assert(l_pounaRealloc != 0);
            for (l_inOffset = 0; l_inOffset < l_arinLevel[l_inLevel]; l_inOffset++) {
                Unicode_swap(&l_pounaRealloc->m_pouniObjects[l_inOffset], &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset]);
            }
            l_arinLevel[l_inLevel] = l_arinIndex[l_inLevel] + 1;
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
            l_arpounaLevel[l_inLevel] = l_pounaRealloc;
            l_pounaRealloc = 0;
        }
        // if delete level then delete subvalue in Unicode object array
        if (l_inLevel == l_inDeletelevel) {
            for (l_inOffset = l_arinIndex[l_inLevel]; l_inOffset < l_arinLevel[l_inLevel] - 1; l_inOffset++) {
                l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset] = l_arpounaLevel[l_inLevel]->m_pouniObjects[l_inOffset + 1];
            }
            l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinLevel[l_inLevel] - 1].m_poszCodepoints = 0;
            l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinLevel[l_inLevel] - 1].m_sizCodepoints = 0;
            l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinLevel[l_inLevel] - 1].m_sizBytes = 0;
            l_arpounaLevel[l_inLevel]->m_sizObjects--;
        }
        l_pouniSubvalues = &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]];
    }
    // Put delete object content within appropriate delimited subvalues
    for (l_inLevel = 3; l_inLevel >= 0; l_inLevel--) {
        if (l_arinParam[l_inLevel] != 0) {
            if (l_inLevel != l_inDeletelevel) {
                Unicode_swap(l_pouniSubvalues, &l_arpounaLevel[l_inLevel]->m_pouniObjects[l_arinIndex[l_inLevel]]);
            }
            l_pouniDelimiter = Unicode_from_string(l_arposzDelimiters[l_inLevel], 1, "ASCII");
            assert(l_pouniDelimiter != 0);
            l_pouniSubvalues = Unicode_join(l_arpounaLevel[l_inLevel], l_pouniDelimiter, 2);
            Unicode_delete(&l_pouniDelimiter);
            UnicodeArray_delete(&l_arpounaLevel[l_inLevel]);
        }
    }
done:
    // Update Unicode object with final result
    Unicode_swap(l_pouniSubvalues, u_pouniObject);
    Unicode_delete(&l_pouniSubvalues);
}

/**
 * @fn "void Unicode_sort_subvalues(struct Unicode * u_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inSort)"
 * @brief Sort the subvalues at a specified level in a 4-dimensional dynamic array
 * @details Sorts the next deeper level of subvalues of the specified level
 * inside the 4-dimensional dynamic array stored within the **Unicode** object.
 * For example, that means that if the **i_inFS** level 1 index is the only
 * non-zero parameter, then the **GS** delimited level 2 subvalues inside the
 * level 1 subvalue will be sorted. The implementation of the dynamic array is
 * structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * The sort is performed on the subvalues one level deeper than the last
 * non-zero index specified.  Indicies can have a positive, zero or negative
 * value. A positive value will sort the subvalues one level deeper than the
 * specified level. A zero index is effectively ignored. A negative index is
 * relative to the last subvalue at the specified level. If a negative index
 * has an absolute value greater than the number of subvalues at the specified
 * level, then the deeper level subvalues contained in the first subvalue will
 * be sorted. It is an error for a non-zero index to follow (to be to the right
 * in the method parameter list) any index with a value of zero.
 *
 * The **i_inSort** parameter is required and can have a value from 0 (default)
 * to 3. Depending on it's value, the sorting will be performed using the
 * following comparison functions:
 * @li i_inSort == 0 - Unicode_compare_ascendingstring(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 1 - Unicode_compare_descendingstring(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 2 - Unicode_compare_ascendingnumeric(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 3 - Unicode_compare_descendingnumeric(i_pouniObject, i_pouniCompare)
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode**
 * value using delimiters that are UNICODE safe.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero. Error
 * will cause a **Exception** to be thrown.
 *
 * @note When using the **Unicode_compare_ascendingnumeric()** or the
 * **Unicode_compare_descendingnumeric()** comparison functions each subvalue
 * is converted to a long double value and only those codepoints that are
 * considered part of the numeric value are used. For example, "1000-gt1" would
 * be equal to "1000.0" in the comparison.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_sort_subvalues(l_pouniObject, 0, 0, 0, 0);@n
 *   Returns sort of level 1 subvalues
 * @li Case 2. Unicode_sort_subvalues(l_pouniObject, 3, 0, 0, 0);@n
 *   Returns sort of level 2 subvalues in third level 1 subvalue
 * @li Case 3. Unicode_sort_subvalues(l_pouniObject, 3, 3, 0, 0);@n
 *   Returns sort of level 3 subvalues in third level 2 subvalue in third
 *   level 1 subvalue
 * @li Case 4. Unicode_sort_subvalues(l_pouniObject, 3, 3, 3, 0);@n
 *   Returns sort of level 4 subvalues in third level 3 subvalue in third
 *   level 2 subvalue in third level 1 subvalue
 * @li Case 5. Unicode_sort_subvalues(l_pouniObject, -1, 0, 0, 0);@n
 *   Returns sort of level 2 subvalues in last level 1 subvalue
 * @li Case 6. Unicode_sort_subvalues(l_pouniObject, 0, 3, 0, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @param[in,out] u_pouniObject = Update pointer to **Unicode** object
 * @param[in] i_inFS = Index of level 1 subvalue, or ignored if zero
 * @param[in] i_inGS = Index of level 2 subvalue, or ignored if zero
 * @param[in] i_inRS = Index of level 3 subvalue, or ignored if zero
 * @param[in] i_inSort = Sort comparison algorithm value from 0 to 3
 * @retval "void" = None
 * @exception abort(3) Aborts if u_pouniObject is null
 * @exception abort(3) Aborts if subvalue index is zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 * @exception assert(3) Aborts if Unicode_from_string() call returns null
 * @exception assert(3) Aborts if Unicode_extract_subvalue() call returns null
 * @exception assert(3) Aborts if Unicode_split() call returns null
 * @exception assert(3) Aborts if Unicode_join() call returns null
 */

void Unicode_sort_subvalues(struct Unicode * u_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inSort)
{
    int (*l_poFunction)(const void *, const void *) = 0;
    struct Unicode * l_pouniDelimiter = 0;
    struct Unicode * l_pouniUnsorted = 0;
    struct Unicode * l_pouniSorted = 0;
    struct UnicodeArray * l_pounaArray = 0;
    
    if (u_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject);
        abort();
    }
    if ((i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS);
        abort();
    }
    switch (i_inSort) {
        case 3: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_descendingnumeric; break;
        case 2: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_ascendingnumeric; break;
        case 1: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_descendingstring; break;
        default: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_ascendingstring; break;
    }
    l_pouniDelimiter = Unicode_from_string(i_inFS == 0 ? "\x1C" : i_inGS == 0 ? "\x1D" : i_inRS == 0 ? "\x1E" : "\x1F", 1, "ASCII");
    assert(l_pouniDelimiter != 0);
    if (i_inFS == 0) {
        l_pouniUnsorted = Unicode_new();
        Unicode_copy(l_pouniUnsorted, u_pouniObject);
    } else {
        l_pouniUnsorted = Unicode_extract_subvalue(u_pouniObject, i_inFS, i_inGS, i_inRS, 0);
    }
    assert(l_pouniUnsorted != 0);
    l_pounaArray = Unicode_split(l_pouniUnsorted, l_pouniDelimiter, 2);
    assert(l_pounaArray != 0);
    qsort(l_pounaArray->m_pouniObjects, l_pounaArray->m_sizObjects, sizeof(struct Unicode), l_poFunction);
    l_pouniSorted = Unicode_join(l_pounaArray, l_pouniDelimiter, 2);
    assert(l_pouniSorted != 0);
    if (i_inFS == 0) {
        Unicode_copy(u_pouniObject, l_pouniSorted);
    } else {
        Unicode_replace_subvalue(u_pouniObject, l_pouniSorted, i_inFS, i_inGS, i_inRS, 0);
    }
    UnicodeArray_delete(&l_pounaArray);
    Unicode_delete(&l_pouniSorted);
    Unicode_delete(&l_pouniUnsorted);
    Unicode_delete(&l_pouniDelimiter);
}

/**
 * @fn "int Unicode_locate_subvalue(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniKey, int i_inFS, int i_inGS, int i_inRS, int i_inSort)"
 * @brief Locate a subvalue at a specified level in a 4-dimensional dynamic array
 * @details Locates the index of the subvalue equal to the codepoints contained
 * in the **i_pouniKey** parameter by searching the next deeper level of subvalues of the specified level
 * inside the 4-dimensional dynamic array stored within the **Unicode** object.
 * For example, that means that if the **i_inFS** level 1 index is the only
 * non-zero parameter, then the **GS** delimited level 2 subvalues inside the
 * level 1 subvalue will be searched. The implementation of the dynamic array is
 * structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * The search is performed on the subvalues one level deeper than the last
 * non-zero index specified.  Indicies can have a positive, zero or negative
 * value. A positive value will sort the subvalues one level deeper than the
 * specified level. A zero index is effectively ignored. A negative index is
 * relative to the last subvalue at the specified level. If a negative index
 * has an absolute value greater than the number of subvalues at the specified
 * level, then the deeper level subvalues contained in the first subvalue will
 * be searched. It is an error for a non-zero index to follow (to be to the right
 * in the method parameter list) any index with a value of zero.
 *
 * The **i_inSort** parameter is required and can have a value from 0 to 4. A
 * value of zero will perform a simple left-to-right scan for the
 * **l_pouniKey** codepoints in the list of subvalues. If the list of subvalues
 * are guaranteed to be sorted in a particular order, using **i_inSort** values
 * 1 to 4 can significantly speed up the search using a binary search
 * algorithm. Depending on the value of **i_inSort**, one of the following
 * comparison functions can be used:
 * @li i_inSort == 0 - Unordered left-to-right value comparison scan
 * @li i_inSort == 1 - Unicode_compare_ascendingstring(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 2 - Unicode_compare_descendingstring(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 3 - Unicode_compare_ascendingnumeric(i_pouniObject, i_pouniCompare)
 * @li i_inSort == 4 - Unicode_compare_descendingnumeric(i_pouniObject, i_pouniCompare)
 *
 * @note The ASCII field delimiters FS, GS, RS and US provide a 4-level deep
 * method for storing multiple subvalues inside of a single **Unicode**
 * value using delimiters that are UNICODE safe.
 *
 * @note It is an error for any subvalue index to be zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero. Error
 * will cause a **Exception** to be thrown.
 *
 * @note When using the **Unicode_compare_ascendingnumeric()** or the
 * **Unicode_compare_descendingnumeric()** comparison functions each subvalue
 * is converted to a long double value and only those codepoints that are
 * considered part of the numeric value are used. For example, "1000-gt1" would
 * be equal to "1000.0" in the comparison.
 *
 * #### Examples ####
 *
 * @li Case 1. Unicode_locate_subvalues(l_pouniObject, 0, 0, 0, 0);@n
 *   Returns index of located level 1 subvalues using left-to-right search
 * @li Case 2. Unicode_locate_subvalues(l_pouniObject, 3, 0, 0, 0);@n
 *   Returns index of located level 2 subvalues in third level 1 subvalue using
 *   left-to-right search
 * @li Case 3. Unicode_locate_subvalues(l_pouniObject, 3, 3, 0, 0);@n
 *   Returns index of located level 3 subvalues in third level 2 subvalue in
 *   third level 1 subvalue using left-to-right search
 * @li Case 4. Unicode_locate_subvalues(l_pouniObject, 3, 3, 3, 0);@n
 *   Returns index of located level 4 subvalues in third level 3 subvalue in
 *   third level 2 subvalue in third level 1 subvalue using left-to-right
 *   search
 * @li Case 5. Unicode_locate_subvalues(l_pouniObject, -1, 0, 0, 0);@n
 *   Returns index of located level 2 subvalues in last level 1 subvalue using
 *   left-to-right search
 * @li Case 6. Unicode_locate_subvalues(l_pouniObject, 0, 3, 0, 0);@n
 *   Error! Non-zero indicies cannot follow zero indicies
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object to be searched
 * @param[in] i_pouniKey = Input pointer to **Unicode** object to search for
 * @param[in] i_inFS = Index of level 1 subvalue, or ignored if zero
 * @param[in] i_inGS = Index of level 2 subvalue, or ignored if zero
 * @param[in] i_inRS = Index of level 3 subvalue, or ignored if zero
 * @param[in] i_inSort = Sort comparison algorithm value from 0 to 4
 * @retval "int" = 1-based index of located subvalue in list of subvalues or
 * zero if not found
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniKey is null
 * @exception abort(3) Aborts if subvalue index is zero if any of the indices
 * following it (to the right in the method parameter list) are non-zero.
 * @exception assert(3) Aborts if Unicode_from_string() call returns null
 * @exception assert(3) Aborts if Unicode_extract_subvalue() call returns null
 * @exception assert(3) Aborts if Unicode_split() call returns null
 */

int Unicode_locate_subvalue(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniKey, int i_inFS, int i_inGS, int i_inRS, int i_inSort)
{
    int (*l_poFunction)(const void *, const void *) = 0;
    struct Unicode * l_pouniDelimiter = 0;
    struct Unicode * l_pouniList = 0;
    struct Unicode * l_pouniLocated = 0;
    struct UnicodeArray * l_pounaArray = 0;
    int l_inIndex = 0;
    
    if (i_pouniObject == 0 || i_pouniKey == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniKey = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniKey);
        abort();
    }
    if ((i_inRS > 0 && (i_inFS == 0 || i_inGS == 0))
        || (i_inGS > 0 && (i_inFS == 0)))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS);
        abort();
    }
    switch (i_inSort) {
        case 4: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_descendingnumeric; break;
        case 3: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_ascendingnumeric; break;
        case 2: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_descendingstring; break;
        case 1: l_poFunction = (int (*)(const void *, const void *)) Unicode_compare_ascendingstring; break;
        default: l_poFunction = 0;
    }
    l_pouniDelimiter = Unicode_from_string(i_inFS == 0 ? "\x1C" : i_inGS == 0 ? "\x1D" : i_inRS == 0 ? "\x1E" : "\x1F", 1, "ASCII");
    assert(l_pouniDelimiter != 0);
    if (i_inFS == 0) {
        l_pouniList = Unicode_new();
        Unicode_copy(l_pouniList, i_pouniObject);
    } else {
        l_pouniList = Unicode_extract_subvalue(i_pouniObject, i_inFS, i_inGS, i_inRS, 0);
    }
    assert(l_pouniList != 0);
    l_pounaArray = Unicode_split(l_pouniList, l_pouniDelimiter, 2);
    assert(l_pounaArray != 0);
    if (l_poFunction == 0) {
        for (l_inIndex = 0; (size_t) l_inIndex < l_pounaArray->m_sizObjects; l_inIndex++) {
            if (Unicode_compare_ascendingstring(i_pouniKey, &l_pounaArray->m_pouniObjects[l_inIndex]) == 0) {
                break;
            }
        }
        l_inIndex = (size_t) l_inIndex == l_pounaArray->m_sizObjects ? 0 : l_inIndex + 1;
    }
    else {
        l_pouniLocated = (struct Unicode *) bsearch(i_pouniKey, l_pounaArray->m_pouniObjects, l_pounaArray->m_sizObjects, sizeof(struct Unicode), l_poFunction);
        l_inIndex = l_pouniLocated == 0 ? 0 : (l_pouniLocated - l_pounaArray->m_pouniObjects) + 1;
    }
    UnicodeArray_delete(&l_pounaArray);
    Unicode_delete(&l_pouniList);
    Unicode_delete(&l_pouniDelimiter);
    return(l_inIndex);
}

/**
 * @fn "struct UnicodeArray * UnicodeArray_new(size_t i_sizElements)"
 * @brief Function that constructs an empty **UnicodeArray** object.
 * @details The heap is used to hold the newly constructed **UnicodeArray** object,
 * with all heap-allocated **Unicode** elements in an empty state.
 *
 * #### Example ####
 *
 * @code
 * // move the first 50 objects from l_pounaArray1 to l_pounaArray2
 * struct UnicodeArray * l_pounaArray1 = UnicodeArray_new(101);
 * load_all_101_objects_into_array(l_pounaArray1);
 * struct UnicodeArray * l_pounaArray2 = UnicodeArray_new(50);
 * for (l_sizOffset = 0; l_sizOffset < 50; l_sizOffset++) {
 *     Unicode_swap(&l_pounaArray1->m_pouniObjects[l_sizOffset], &l_pounaArray2->m_pouniObjects[l_sizOffset]);
 * }
 * UnicodeArray_delete(l_pounaArray1);
 * @endcode
 *
 * @param[in] i_sizElements = Number of heap-allocated **Unicode** objects in the array
 * @retval "struct UnicodeArray *" = Pointer to new **UnicodeArray** object
 * @exception abort(3) Aborts if calloc(3) returns a null pointer
 */

struct UnicodeArray * UnicodeArray_new(size_t i_sizElements)
{
    struct UnicodeArray * l_pounaObject = (struct UnicodeArray *) calloc(1, sizeof(struct UnicodeArray));
    assert(l_pounaObject != 0);
    l_pounaObject->m_pouniObjects = (struct Unicode *) calloc(i_sizElements, sizeof(struct Unicode));
    l_pounaObject->m_sizObjects = i_sizElements;
    return l_pounaObject;
}

/**
 * @fn "void UnicodeArray_delete(struct UnicodeArray ** u_popounaObject)"
 * @brief Function that destructs a **UnicodeArray** object
 * @details The **free()** function is called to release the heap allocations
 * for the **m_pouniObjects** struct member and the **UnicodeArray** object
 * itself.  Responsibility for calling **Unicode_clear()** on the **Unicode**
 * objects themselves is left to the calling routine, which if not done, can
 * lead to memory leaks of any allocated codepoints still on the heap.
 *
 * #### Example ####
 *
 * @code
 * // move the first 50 objects from l_pounaArray1 to l_pounaArray2
 * struct UnicodeArray * l_pounaArray1 = UnicodeArray_new(101);
 * load_all_101_objects_into_array(l_pounaArray1);
 * struct UnicodeArray * l_pounaArray2 = UnicodeArray_new(50);
 * for (l_sizOffset = 0; l_sizOffset < 50; l_sizOffset++) {
 *     Unicode_swap(&l_pounaArray1->m_pouniObjects[l_sizOffset], &l_pounaArray2->m_pouniObjects[l_sizOffset]);
 * }
 * UnicodeArray_delete(l_pounaArray1);
 * @endcode
 *
 * @param[in,out] u_popounaObject = Update pointer to pointer to **UnicodeArray** object
 * @retval "void" = None
 * @exception assert(3) Aborts if u_popounaObject is null
 * @exception assert(3) Aborts if *u_popounaObject is null
 */

void UnicodeArray_delete(struct UnicodeArray ** u_popounaObject)
{
    assert(u_popounaObject != 0);
    assert(*u_popounaObject != 0);
    if ((*u_popounaObject)->m_pouniObjects != 0) {
        free((*u_popounaObject)->m_pouniObjects);
    }
    free(*u_popounaObject);
    *u_popounaObject = 0;
}

/**
 * @fn "long Unicode_get_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Gets a codepoint at a particular offset inside a **Unicode** object
 * @details The codepoint value at the specified 0-based offset passed in the
 * parameter **i_sizOffset** is returned as a **long** value. The numeric value
 * is based upon UTF32LE encoding of the codepoints in the **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * // get UTF numeric value of first codepoint
 * long l_loCodepoint = Unicode_get_codepoint(l_pouniObject, 0);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to get codepoint in **Unicode** object
 * @retval "long" = Long value containing value of UTF32LE encoded codepoint,
 * or zero if **i_sizOffset** is outside the number of codepoints contained in
 * **i_pouniObject**.
 * @exception abort(3) Aborts if i_pouniObject is null
 */

long Unicode_get_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzCodepoints = (wchar_t *) i_pouniObject->m_poszCodepoints;
    long l_loCodepoint = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset < i_pouniObject->m_sizCodepoints) {
        l_loCodepoint = l_powzCodepoints[i_sizOffset];
    }
    return(l_loCodepoint);
}

/**
 * @fn "void Unicode_set_codepoint(struct Unicode * u_pouniObject, size_t i_sizOffset, long i_loCodepoint)"
 * @brief Sets a codepoint at a particular offset inside a **Unicode** object
 * @details The codepoint value placed at the specified 0-based offset passed
 * in the parameter **i_sizOffset** is passed as a **long** value in the
 * parameter **i_loCodepoint**. The numeric value is based upon UTF32LE
 * encoding of the codepoints in the **Unicode** object.
 *
 * #### Example ####
 *
 * @code
 * // set UTF numeric value of first codepoint
 * long l_loCodepoint = (long) U'©';  // U+00A9 COPYRIGHT SIGN
 * Unicode_set_codepoint(l_pouniObject, 0, l_loCodepoint);
 * @endcode
 *
 * @param[in,out] u_pouniObject = Update pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @param[in] i_loCodepoint = Long value of UTF32LE encoded codepoint
 * @retval "void" = None
 * @exception abort(3) Aborts if i_pouniObject is null
 */

void Unicode_set_codepoint(struct Unicode * u_pouniObject, size_t i_sizOffset, long i_loCodepoint)
{
    wchar_t * l_powzCodepoints = (wchar_t *) u_pouniObject->m_poszCodepoints;

    if (u_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pouniObject = %p\n",
            __FILE__, __LINE__, u_pouniObject);
        abort();
    }
    if (i_sizOffset < u_pouniObject->m_sizCodepoints) {
        l_powzCodepoints[i_sizOffset] = i_loCodepoint;
    }
}

/**
 * @fn "int Unicode_find_codepoint (const struct Unicode * i_pouniObject, long i_loCodepoint, int i_inCount)"
 * @brief Find a codepoint and return offset inside a **Unicode** object
 * @details The input parameter **i_loCodepoint** value is searched for inside
 * the current **Unicode** object codepoints. The input parameter **i_inCount**
 * is used to return the offset of the first  match.  If a match is found, a
 * zero-based offset into the current object value in codepoints is returned. A
 * value of negative one (-1) is returned if the length of the string is zero,
 * or if a match cannot be found.
 *
 * #### Example ####
 *
 * @code
 * long l_loCodepoint = (long) U'©';  // U+00A9 COPYRIGHT SIGN
 * // find numeric offset of first left-to-right matching codepoint in object
 * int l_inFirst = Unicode_find_codepoint(l_pouniObject, l_loCodepoint, 0);
 * // find numeric offset of first right-to-left matching codepoint in object
 * int l_inLast = Unicode_find_codepoint(l_pouniObject, l_loCodepoint, -1);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_loCodepoint = Long integer value of UTF-32 codepoint element to
 * search for
 * @param[in] i_inCount = A zero indicates to return the offset of the first
 * match searching left to right in codepoints. A positive value indicates the
 * number of matches to skip first when searching left to right.  A negative
 * value indicates to search right to left and return the match at the absolute
 * value of the **i_inCount** input parameter. For example, a value of -1 will
 * return the offset of the right-most match.
 * @retval "int" = Returns a zero (0) based offset inside the **Unicode** object
 * content in codepoints if a match is found, or negative one (-1) if not.
 * @exception abort(3) Aborts if i_pouniObject is null
 */

int Unicode_find_codepoint (const struct Unicode * i_pouniObject, long i_loCodepoint, int i_inCount)
{
    wchar_t * l_powzCodepoints = (wchar_t *) i_pouniObject->m_poszCodepoints;
    int l_inOffset = -1;
    int l_inCounter = 0;
    int l_inIndex = 0;
    int l_inPos = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    for (l_inIndex = 0; (size_t) l_inIndex < i_pouniObject->m_sizCodepoints; l_inIndex++) {
        l_inPos = i_inCount >= 0 ? l_inIndex : (int) i_pouniObject->m_sizCodepoints - l_inIndex - 1;
        if (i_loCodepoint == (long) l_powzCodepoints[l_inPos]) {
            if ((i_inCount >= 0 && l_inCounter++ == i_inCount)
                || (i_inCount < 0 && --l_inCounter == i_inCount))
            {
                l_inOffset = l_inPos;
                break;
            }
        }
    }
    return l_inOffset;
}

/**
 * @fn "int Unicode_isalnum_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:alnum:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:alnum:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:alnum:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isalnum_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:alnum:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isalnum_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswalnum(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isalpha_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:alpha:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:alpha:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:alpha:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isalpha_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:alpha:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isalpha_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswalpha(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_islower_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:lower:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:lower:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:lower:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_islower_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:lower:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_islower_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswlower(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isupper_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:upper:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:upper:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:upper:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isupper_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:upper:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isupper_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswupper(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isdigit_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:digit:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:digit:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:digit:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isdigit_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:digit:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isdigit_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswdigit(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isxdigit_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:xdigit:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:xdigit:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:xdigit:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isxdigit_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:xdigit:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isxdigit_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswxdigit(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_iscntrl_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:cntrl:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:cntrl:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:cntrl:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_iscntrl_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:cntrl:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_iscntrl_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswcntrl(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isgraph_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:graph:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:graph:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:graph:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isgraph_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:graph:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isgraph_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswgraph(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isspace_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:space:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:space:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:space:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isspace_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:space:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isspace_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswspace(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isblank_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:blank:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:blank:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:blank:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isblank_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:blank:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isblank_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswblank(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_isprint_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:print:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:print:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:print:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_isprint_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:print:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_isprint_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswprint(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_ispunct_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Returns true if a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:punct:]" character class
 * @details The input parameter **i_sizOffset** value is used as the codepoint
 * offset inside the **Unicode** object. The codepoint at that offset is
 * matched against the POSIX "[:punct:]" character class. If it matches, the
 * integer 1 (true) is returned, else 0 (false) is returned.
 *
 * #### Example ####
 *
 * @code
 * // determine if first letter matches the POSIX "[:punct:]" character class
 * size_t l_sizOffset = 0;
 * int l_inMatches = Unicode_ispunct_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "int" = Returns an integer value of 1 (true) if the codepoint at the
 * passed offset matches the POSIX "[:punct:]" character class, else 0 (false)
 * is returned.
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

int Unicode_ispunct_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return !!iswpunct(l_powzObject[i_sizOffset]);
}

/**
 * @fn "long Unicode_tolower_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Get lowercase value of a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:punct:]" character class
 * @details Returns the lowercase version (if possible) of the codepoint at
 * offset **i_sizOffset** in the **Unicode** object according to the current
 * global locale. The original codepoint is not modified. Offset is zero-based.
 * Any attempt to access beyond the last codepoint stored in the **Unicode**
 * object will throw an exception.
 *
 * #### Example ####
 *
 * @code
 * // get the lowercase value of first codepoint
 * size_t l_sizOffset = 0;
 * long l_loLowercase = Unicode_tolower_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "long" = Long value containing lowercase (if possible) value of UTF32LE encoded codepoint
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

long Unicode_tolower_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return (long) towlower(l_powzObject[i_sizOffset]);
}

/**
 * @fn "long Unicode_toupper_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)"
 * @brief Get uppercase value of a codepoint at an offset inside a **Unicode** object
 * matches the POSIX "[:punct:]" character class
 * @details Returns the uppercase version (if possible) of the codepoint at
 * offset **i_sizOffset** in the **Unicode** object according to the current
 * global locale. The original codepoint is not modified. Offset is zero-based.
 * Any attempt to access beyond the last codepoint stored in the **Unicode**
 * object will throw an exception.
 *
 * #### Example ####
 *
 * @code
 * // get the uppercase value of first codepoint
 * size_t l_sizOffset = 0;
 * long l_loUppercase = Unicode_toupper_codepoint(l_pouniObject, l_sizOffset);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizOffset = Offset to put codepoint in **Unicode** object
 * @retval "long" = Long value containing uppercase (if possible) value of UTF32LE encoded codepoint
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_sizOffset >= i_pouniObject->m_sizCodepoints
 */

long Unicode_toupper_codepoint(const struct Unicode * i_pouniObject, size_t i_sizOffset)
{
    wchar_t * l_powzObject = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    if (i_sizOffset >= i_pouniObject->m_sizCodepoints) {
        fprintf(stderr, "%s(%d) = i_pouniObject->m_sizCodepoints = %lu, i_sizOffset = %lu\n",
            __FILE__, __LINE__, i_pouniObject->m_sizCodepoints, i_sizOffset);
        abort();
    }
    l_powzObject = (wchar_t *) i_pouniObject->m_poszCodepoints;
    return (long) towupper(l_powzObject[i_sizOffset]);
}

/**
 * @fn "int Unicode_save(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniPath, const char * i_poszEncoding)"
 * @brief Save the current **Unicode** object content to a file in the local filesystem
 * @details The contents of the **Unicode** object are converted and written
 * into a file using any **iconv(3)** supported encoding.  The name of the file
 * along with any parent folders is passed in **i_pouniPath**.  The character
 * encoding should be passed as a null-terminated char string.  Any character
 * encoding can be specified using **i_poszEncoding** as long as it is
 * supported by the implementation's **iconv(3)** C library function.
 *
 * #### Example ####
 *
 * @code
 * // save the current Unicode object to file
 * struct Unicode * l_pouniObject = Unicode_new();
 * struct Unicode * l_pouniInput = Unicode_from_string("ASCII.txt", 0, "UTF8");
 * struct Unicode * l_pouniOutput = Unicode_from_string("EBCDIC.txt", 0, "UTF8");
 * int l_inBytesread = Unicode_load(l_pouniObject, l_pouniInput, "ASCII");
 * int l_inByteswritten = Unicode_save(l_pouniObject, l_pouniOutput, "EBCDIC-US");
 * Unicode_delete(&l_pouniOutput);
 * Unicode_delete(&l_pouniInput);
 * Unicode_delete(&l_pouniObject);
 * @endcode
 *
 * @param[in] i_pouniObject = Input **Unicode** object containing file data
 * @param[in] i_pouniPath = Input **Unicode** object containing absolute or
 * relative path of the file containing the output text.
 * @param[in] i_poszEncoding = C null-terminated string containing **iconv(3)**
 * supported encoding code (eg: "ASCII", "POSIX", "UTF8")
 * @retval "int" = Returns number of bytes written to file
 * @exception abort(3) Aborts if i_pouniObject is null
 * @exception abort(3) Aborts if i_pouniPath is null
 * @exception abort(3) Aborts if i_poszEncoding is null
 * @exception abort(3) Aborts on iconv_open(3) failure
 * @exception abort(3) Aborts on iconv(3) failure
 * @exception abort(3) Aborts on iconv_close(3) failure
 * @exception assert(3) Aborts if calloc() returns null
 * @see Unicode_export_string(), iconv_open(3), iconv(3), iconv_close(3)
 */

int Unicode_save(const struct Unicode * i_pouniObject, const struct Unicode * i_pouniPath, const char * i_poszEncoding)
{
    char l_archToencoding[UNICODE_BUFFER_MAX + 1];
    const char * l_poszFromencoding = "UTF32LE";
    char * l_poszOutbuf = 0;
    char * l_poszOutbufleft = 0;
    char * l_poszInbuf = 0;
    char * l_poszPath = 0;
    FILE * l_fileSave = 0;
    size_t l_sizOutbytesleft = 0;
    size_t l_sizInbytesleft = 0;
    size_t l_sizReturn = 0;
    int l_inBytesconverted = 0;
    int l_inByteswritten = 0;
    int l_inReturn = 0;
    iconv_t l_ictCd;

    if (i_pouniObject == 0 || i_pouniPath == 0 || i_poszEncoding == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p, i_pouniPath = %p, i_poszEncoding = %p\n",
            __FILE__, __LINE__, i_pouniObject, i_pouniPath, i_poszEncoding);
        abort();
    }
    sprintf(l_archToencoding, "%s//TRANSLIT", i_poszEncoding);
    l_ictCd = iconv_open(l_archToencoding, l_poszFromencoding);
    if (l_ictCd == (iconv_t) -1) {
        fprintf(stderr, "%s(%d) = iconv_open() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', m_poszCodepoints = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, i_pouniObject->m_poszCodepoints);
        abort();
    }
    l_sizOutbytesleft = i_pouniObject->m_sizBytes + sizeof(wchar_t);
    l_poszOutbuf = (char *) calloc(1, l_sizOutbytesleft + sizeof(wchar_t));
    assert(l_poszOutbuf != 0);
    l_poszOutbufleft = l_poszOutbuf;
    l_sizInbytesleft = i_pouniObject->m_sizBytes;
    l_poszInbuf = i_pouniObject->m_poszCodepoints;
    l_sizReturn = iconv(l_ictCd, &l_poszInbuf, &l_sizInbytesleft, &l_poszOutbufleft, &l_sizOutbytesleft);
    if (l_sizReturn == (size_t) -1) {
        fprintf(stderr, "%s(%d) = iconv() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', l_sizInbytesleft = %lu, l_sizOutbytesleft = %lu\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, l_sizInbytesleft, l_sizOutbytesleft);
    }
    l_inReturn = iconv_close(l_ictCd);
    if (l_inReturn == -1) {
        fprintf(stderr, "%s(%d) = iconv_close() = -1, errno = %d, strerror = '%s', l_archToencoding = '%s', l_poszFromencoding = '%s', m_poszCodepoints = '%-.255s'\n",
            __FILE__, __LINE__, errno, strerror(errno), l_archToencoding, l_poszFromencoding, i_pouniObject->m_poszCodepoints);
        abort();
    }
    l_inBytesconverted = l_poszOutbufleft - l_poszOutbuf;
    l_poszPath = Unicode_export_string(i_pouniPath, i_pouniPath->m_sizBytes, "UTF8");
    l_fileSave = fopen(l_poszPath, "wb");
    if (l_fileSave != 0) {
        l_inByteswritten = fwrite(l_poszOutbuf, 1, l_inBytesconverted, l_fileSave);
        fclose(l_fileSave);
    }
    free(l_poszOutbuf);
    free(l_poszPath);
    return l_inByteswritten;
}

/**
 * @fn "int Unicode_load(struct Unicode * o_pouniObject, const struct Unicode * i_pouniPath, const char * i_poszEncoding)"
 * @brief Load the current **Unicode** object content from a file in the local filesystem
 * @details The contents of the **Unicode** object are read from a file and
 * converted using any **iconv(3)** supported encoding.  The name of the file
 * along with any parent folders is passed in **i_pouniPath**.  The character
 * encoding should be passed as a null-terminated char string.  Any character
 * encoding can be specified using **i_poszEncoding** as long as it is
 * supported by the implementation's **iconv(3)** C library function.
 *
 * #### Example ####
 *
 * @code
 * // save the current Unicode object to file
 * struct Unicode * l_pouniObject = Unicode_new();
 * struct Unicode * l_pouniInput = Unicode_from_string("EBCDIC.txt", 0, "UTF8");
 * struct Unicode * l_pouniOutput = Unicode_from_string("ASCII.txt", 0, "UTF8");
 * int l_inBytesread = Unicode_load(l_pouniObject, l_pouniInput, "EBCDIC-US");
 * int l_inByteswritten = Unicode_save(l_pouniObject, l_pouniOutput, "ASCII");
 * Unicode_delete(&l_pouniOutput);
 * Unicode_delete(&l_pouniInput);
 * Unicode_delete(&l_pouniObject);
 * @endcode
 *
 * @param[out] o_pouniObject = Output **Unicode** object containing file data
 * @param[in] i_pouniPath = Input **Unicode** object containing absolute or
 * relative path of the file containing the input text.
 * @param[in] i_poszEncoding = C null-terminated string containing **iconv(3)**
 * supported encoding code (eg: "ASCII", "POSIX", "UTF8")
 * @retval "int" = Returns number of bytes read from file before conversion
 * @exception abort(3) Aborts if o_pouniObject is null
 * @exception abort(3) Aborts if i_pouniPath is null
 * @exception abort(3) Aborts if i_poszEncoding is null
 * @exception assert(3) Aborts if calloc() returns null
 * @see Unicode_export_string(), Unicode_import_string(), iconv_open(3), iconv(3), iconv_close(3)
 */

int Unicode_load(struct Unicode * o_pouniObject, const struct Unicode * i_pouniPath, const char * i_poszEncoding)
{
    char * l_poszInbuf = 0;
    char * l_poszPath = 0;
    FILE * l_fileLoad = 0;
    int l_inBytesread = 0;

    if (o_pouniObject == 0 || i_pouniPath == 0 || i_poszEncoding == 0) {
        fprintf(stderr, "%s(%d) = o_pouniObject = %p, i_pouniPath = %p, i_poszEncoding = %p\n",
            __FILE__, __LINE__, o_pouniObject, i_pouniPath, i_poszEncoding);
        abort();
    }
    l_poszPath = Unicode_export_string(i_pouniPath, i_pouniPath->m_sizBytes, "UTF8");
    l_fileLoad = fopen(l_poszPath, "r+b");
    if (l_fileLoad != 0) {
        fseek(l_fileLoad, 0L, SEEK_END);
        l_inBytesread = ftell(l_fileLoad);
        fseek(l_fileLoad, 0L, SEEK_SET);
        l_poszInbuf = (char *) calloc(1, l_inBytesread + sizeof(wchar_t));
        assert(l_poszInbuf != 0);
        l_inBytesread = fread(l_poszInbuf, 1, l_inBytesread, l_fileLoad);
        fclose(l_fileLoad);
    }
    if (l_poszInbuf != 0) {
        Unicode_import_string(o_pouniObject, l_poszInbuf, l_inBytesread, i_poszEncoding);
        free(l_poszInbuf);
    }
    free(l_poszPath);
    return l_inBytesread;
}

/**
 * @fn "struct UnicodeTesseract * UnicodeTesseract_new(size_t i_sizCodepoints, size_t i_sizDim1, size_t i_sizDim2, size_t i_sizDim3, size_t i_sizDim4)"
 * @brief Function that constructs an empty **UnicodeTesseract** object.
 * @details A new **UnicodeTesseract** object is created on the heap. It also
 * creates a separate virtual memory space backed up by a temporary file for
 * the lifetime of the **UnicodeTesseract** object. The separate virtual memory
 * space is neither stored on the stack or the heap, and is used to store the
 * following data:
 * @li **UnicodeArray** object
 * @li One dimensional array of **Unicode** objects
 * @li Four dimensional array of codepoints for the **Unicode** objects
 * 
 * @note A **UnicodeTesseract** object uses the **tmpfile(3)** and **mmap(2)**
 * system calls to create a new virtual address space that caches memory access
 * to a private temporary file unique to each **UnicodeTesseract** object. When
 * using the **Unicode_get_element()** to extact **Unicode** objects from the
 * **UnicodeTesseract** object, you should not use **Unicode_clear()** or
 * **Unicode_delete()** on these objects since they exist in an alternate
 * virtual address memory space, in other words, they do not exist on the heap.
 * This has ramifications when using SWIG, as the **Unicode_get_element()**
 * function should not be used in a **%newobject** directive since it only
 * returns the address of an already existing **Unicode** object, and does not
 * create a new one.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in] i_sizCodepoints = Maximum size in codepoints of each **Unicode** object
 * @param[in] i_sizDim1 = Number of **Unicode** objects at dimension 1
 * @param[in] i_sizDim2 = Number of **Unicode** objects at dimension 2
 * @param[in] i_sizDim3 = Number of **Unicode** objects at dimension 3
 * @param[in] i_sizDim4 = Number of **Unicode** objects at dimension 4
 * @retval "struct UnicodeTesseract *" = Pointer to new **UnicodeTesseract** object
 * @exception assert(3) Aborts if calloc(3) returns a null pointer
 * @exception abort(3) Aborts if any dimension parameter is zero
 * @exception abort(3) Aborts if mkstemp(3) returns -1
 * @exception abort(3) Aborts if mmap(2) returns (void *) -1
 * @see ftruncate(2), mmap(2), munmap(2)
 */

struct UnicodeTesseract * UnicodeTesseract_new(size_t i_sizCodepoints, size_t i_sizDim1, size_t i_sizDim2, size_t i_sizDim3, size_t i_sizDim4)
{
    struct UnicodeTesseract * l_pountObject = 0;
    char * l_poszMemory = 0;
    FILE * l_poFile = 0;

    if (i_sizDim1 == 0 || i_sizDim2 == 0 || i_sizDim3 == 0 || i_sizDim4 == 0)
    {
        fprintf(stderr, "%s(%d) = i_sizDim1 = %lu, i_sizDim2 = %lu, i_sizDim3 = %lu, i_sizDim4 = %lu\n",
            __FILE__, __LINE__, i_sizDim1, i_sizDim2, i_sizDim3, i_sizDim4);
        abort();
    }
    l_pountObject = (struct UnicodeTesseract *) calloc(1, sizeof(struct UnicodeTesseract));
    assert(l_pountObject != 0);
    // number of elements stored inside the UnicodeTesseract virtual memory
    l_pountObject->m_sizElements = i_sizDim1 * i_sizDim2 * i_sizDim3 * i_sizDim4;
    // amount of virtual memory to hold UnicodeArray, Unicode objects, and Codepoints data
    l_pountObject->m_sizBytes = sizeof(struct UnicodeArray);
    l_pountObject->m_sizBytes += l_pountObject->m_sizElements * sizeof(struct Unicode);
    l_pountObject->m_sizBytes += l_pountObject->m_sizElements * i_sizCodepoints * sizeof(wchar_t);
    // create a temporary virtual memory file whose directory entry is deleted right after opening
    l_poFile = tmpfile();
    l_pountObject->m_inFile = fileno(l_poFile);
    if (l_pountObject->m_inFile == -1) {
        fprintf(stderr, "%s(%d) = tmpfile() = -1, errno = %d, strerror = '%s'\n",
            __FILE__, __LINE__, errno, strerror(errno));
        abort();
    }
    if (ftruncate(l_pountObject->m_inFile, l_pountObject->m_sizBytes) == -1) {
        fprintf(stderr, "%s(%d) = ftruncate(%lu) = -1, errno = %d, strerror = '%s'\n",
            __FILE__, __LINE__, l_pountObject->m_sizBytes, errno, strerror(errno));
        abort();
    }
    l_poszMemory = (char *) mmap(0, l_pountObject->m_sizBytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, l_pountObject->m_inFile, 0);
    if (l_poszMemory == (void *) -1) {
        fprintf(stderr, "%s(%d) = mmap(%lu) = -1, errno = %d, strerror = '%s'\n",
            __FILE__, __LINE__, l_pountObject->m_sizBytes, errno, strerror(errno));
        abort();
    }
    l_pountObject->m_pounaObject = (struct UnicodeArray *) l_poszMemory;
    l_pountObject->m_pounaObject->m_pouniObjects = (struct Unicode *) (l_poszMemory + sizeof(struct UnicodeArray));
    l_pountObject->m_pounaObject->m_sizObjects = l_pountObject->m_sizElements;
    l_pountObject->m_poszCodepoints = l_poszMemory + sizeof(struct UnicodeArray) + l_pountObject->m_sizElements * sizeof(struct Unicode);
    l_pountObject->m_sizCodepoints = i_sizCodepoints;
    l_pountObject->m_sizDim1 = i_sizDim1;
    l_pountObject->m_sizDim2 = i_sizDim2;
    l_pountObject->m_sizDim3 = i_sizDim3;
    l_pountObject->m_sizDim4 = i_sizDim4;
    return l_pountObject;
}

/**
 * @fn "void UnicodeTesseract_delete(struct UnicodeTesseract ** u_popountObject)"
 * @brief Function that destructs a **UnicodeTesseract** object
 * @details The **free()** function is called to release the heap allocations
 * for the **m_pouniObjects** struct members, the **m_pounaLevel[1-4]** members and the **UnicodeTesseract** object
 * itself.  Responsibility for calling **Unicode_clear()** on the **Unicode**
 * objects themselves is left to the calling routine, which if not done, can
 * lead to memory leaks of any allocated codepoints still on the heap.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in,out] u_popountObject = Update pointer to pointer to **UnicodeTesseract** object
 * @retval "void" = None
 * @exception assert(3) Aborts if u_popounaObject is null
 * @exception assert(3) Aborts if *u_popounaObject is null
 * @see munmap(3), close(2), free(3)
 */

void UnicodeTesseract_delete(struct UnicodeTesseract ** u_popountObject)
{
    assert(u_popountObject != 0);
    assert(*u_popountObject != 0);
    if ((*u_popountObject)->m_pounaObject != 0) {
        munmap((*u_popountObject)->m_pounaObject, (*u_popountObject)->m_sizBytes);
        close((*u_popountObject)->m_inFile);
    }
    free(*u_popountObject);
    *u_popountObject = 0;
}

/**
 * @fn "void Unicode_set_element(struct UnicodeTesseract * u_pountObject, const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Function to set a **UnicodeTesseract** object subvalue
 * @details The codepoints of the **i_pouniObject** object are assigned to an
 * element in the four dimensional **u_pountObject** array using the specified
 * 1-based indicies.  The **UnicodeTesseract** object mirrors the four
 * dimensional dynamic array contained inside a **Unicode** object. The
 * implementation of the 4-dimensional dynamic array inside a **Unicode**
 * object is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies are 1-based and can have a positive or negative value. Zero values
 * are not permitted. A positive value will access a specific subvalue at a
 * specific level.  A negative value will access a subvalue relative to the
 * highest dimensional index at the specified level in the
 * **UnicodeTesseract**. If a negative value has an absolute value greater than
 * the number of subvalues at the specified level, then the first subvalue will
 * be accessed. It is an error for any index to have a zero value.
 *
 * @note Inside a **Unicode** object, the ASCII field delimiters FS, GS, RS and
 * US provide a 4-level deep method for storing multiple subvalues inside of a
 * single **Unicode** object using delimiters that are UNICODE safe. For this
 * reason, any and all FS, GS, RS and US delimiters should be removed from the
 * codepoints in **i_pouniObject** before the codepoints are set inside an
 * element in **u_pountObject**. Any delimiters included in the codepoints
 * inserted into any of the **u_pountObject** elements can lead to undefined
 * behavior.
 *
 * @note This routine does not allocate or free any heap memory, but only
 * returns the address of an already existing **Unicode** object that exists in
 * the **UnicodeTesseract** object virtual memory address space.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in,out] u_pountObject = Update pointer to pointer to **UnicodeTesseract** object
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_inFS = Level 1 index of level 1 subvalues
 * @param[in] i_inGS = Level 2 index of level 2 subvalues
 * @param[in] i_inRS = Level 3 index of level 3 subvalues
 * @param[in] i_inUS = Level 4 index of level 4 subvalues
 * @retval "void" = None
 * @exception abort(3) Aborts if **u_pountObject** is null
 * @exception abort(3) Aborts if **i_pouniObject** is null
 * @exception abort(3) Aborts if any subvalue index is zero
 * @exception abort(3) Aborts if a positive subvalue index is larger than the
 * original dimensional value at the appropriate level when the
 * **UnicodeTesseract** object was created.
 */

void Unicode_set_element(struct UnicodeTesseract * u_pountObject, const struct Unicode * i_pouniObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    size_t l_sizFS = 0;                         // 0-based array offsets converted from input index parameters
    size_t l_sizGS = 0;
    size_t l_sizRS = 0;
    size_t l_sizUS = 0;
    size_t l_sizOffset = 0;
    size_t l_sizBytes = 0;
    size_t l_sizCodepoints = 0;

    if (u_pountObject == 0 || i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = u_pountObject = %p, i_pouniObject = %p\n",
            __FILE__, __LINE__, u_pountObject, i_pouniObject);
        abort();
    }
    if ((i_inFS == 0 || (size_t) i_inFS > u_pountObject->m_sizDim1)
        || (i_inGS == 0 || (size_t) i_inGS > u_pountObject->m_sizDim2)
        || (i_inRS == 0 || (size_t) i_inRS > u_pountObject->m_sizDim3)
        || (i_inUS == 0 || (size_t) i_inUS > u_pountObject->m_sizDim4))
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d, m_sizDim1 = %lu, m_sizDim2 = %lu, m_sizDim3 = %lu, m_sizDim4 = %lu\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS, u_pountObject->m_sizDim1, u_pountObject->m_sizDim2, u_pountObject->m_sizDim3, u_pountObject->m_sizDim4);
        abort();
    }
    l_sizFS = i_inFS > 0 ? (size_t) i_inFS : i_inFS + u_pountObject->m_sizDim1 < 1 ? 1 : i_inFS + u_pountObject->m_sizDim1 + 1;
    l_sizGS = i_inGS > 0 ? (size_t) i_inGS : i_inGS + u_pountObject->m_sizDim2 < 1 ? 1 : i_inGS + u_pountObject->m_sizDim2 + 1;
    l_sizRS = i_inRS > 0 ? (size_t) i_inRS : i_inRS + u_pountObject->m_sizDim3 < 1 ? 1 : i_inRS + u_pountObject->m_sizDim3 + 1;
    l_sizUS = i_inUS > 0 ? (size_t) i_inUS : i_inUS + u_pountObject->m_sizDim4 < 1 ? 1 : i_inUS + u_pountObject->m_sizDim4 + 1;
    l_sizOffset = (l_sizFS - 1) * u_pountObject->m_sizDim2 * u_pountObject->m_sizDim3 * u_pountObject->m_sizDim4;
    l_sizOffset += (l_sizGS - 1) * u_pountObject->m_sizDim3 * u_pountObject->m_sizDim4;
    l_sizOffset += (l_sizRS - 1) * u_pountObject->m_sizDim4;
    l_sizOffset += (l_sizUS - 1);
    l_sizBytes = u_pountObject->m_sizCodepoints * sizeof(wchar_t);
    u_pountObject->m_pounaObject->m_pouniObjects[l_sizOffset].m_poszCodepoints = u_pountObject->m_poszCodepoints + l_sizOffset * l_sizBytes;
    l_sizCodepoints = u_pountObject->m_sizCodepoints <= i_pouniObject->m_sizCodepoints ? u_pountObject->m_sizCodepoints : i_pouniObject->m_sizCodepoints;
    memcpy(u_pountObject->m_pounaObject->m_pouniObjects[l_sizOffset].m_poszCodepoints, i_pouniObject->m_poszCodepoints, l_sizCodepoints * sizeof(wchar_t));
    u_pountObject->m_pounaObject->m_pouniObjects[l_sizOffset].m_sizCodepoints = l_sizCodepoints;
    u_pountObject->m_pounaObject->m_pouniObjects[l_sizOffset].m_sizBytes = l_sizCodepoints * sizeof(wchar_t);
}

/**
 * @fn "struct Unicode * Unicode_get_element(const struct UnicodeTesseract * i_pountObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)"
 * @brief Function to get a **UnicodeTesseract** object subvalue
 * @details A pointer to the **Unicode** object element in the four dimensional
 * **UnicodeTesseract** object pointed to by the **i_pountObject** parameter
 * using specified 1-based indicies is returned.  The **UnicodeTesseract**
 * object mirrors the four dimensional dynamic array contained inside a
 * **Unicode** object. The implementation of the 4-dimensional dynamic array
 * inside a **Unicode** object is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * Indicies can have a positive or negative value. Zero values are not
 * permitted. A positive value will access a specific subvalue at a specific
 * level.  A negative value will access a subvalue relative to the highest
 * dimensional index at the specified level in the **UnicodeTesseract**. If a
 * negative value has an absolute value greater than the number of subvalues at
 * the specified level, then the first subvalue will be accessed. It is an
 * error for any subvalue index to have a zero value.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in] i_pountObject = Input pointer to pointer to **UnicodeTesseract** object
 * @param[in] i_inFS = Level 1 index of level 1 subvalues
 * @param[in] i_inGS = Level 2 index of level 2 subvalues
 * @param[in] i_inRS = Level 3 index of level 3 subvalues
 * @param[in] i_inUS = Level 4 index of level 4 subvalues
 * @retval "struct Unicode *" = Pointer to **Unicode** object inside
 * **UnicodeTesseract** object containing subvalue at specified indicies
 * @exception abort(3) Aborts if **i_pountObject** is null
 * @exception abort(3) Aborts if a subvalue index is zero and any of the
 * indices following it (to the right in the method parameter list) are
 * non-zero.
 * @exception abort(3) Aborts if a positive subvalue index is larger than the
 * original dimensional value at the appropriate level when the
 * **UnicodeTesseract** object was created.
 */

struct Unicode * Unicode_get_element(const struct UnicodeTesseract * i_pountObject, int i_inFS, int i_inGS, int i_inRS, int i_inUS)
{
    size_t l_sizFS = 0;                         // 0-based array offsets converted from input index parameters
    size_t l_sizGS = 0;
    size_t l_sizRS = 0;
    size_t l_sizUS = 0;
    size_t l_sizOffset = 0;

    if (i_pountObject == 0) {
        fprintf(stderr, "%s(%d) = i_pountObject = %p\n",
            __FILE__, __LINE__, i_pountObject);
        abort();
    }
    if (i_inFS == 0 || (size_t) i_inFS > i_pountObject->m_sizDim1
        || i_inGS == 0 || (size_t) i_inGS > i_pountObject->m_sizDim2
        || i_inRS == 0 || (size_t) i_inRS > i_pountObject->m_sizDim3
        || i_inUS == 0 || (size_t) i_inUS > i_pountObject->m_sizDim4)
    {
        fprintf(stderr, "%s(%d) = i_inFS = %d, i_inGS = %d, i_inRS = %d, i_inUS = %d, m_sizDim1 = %lu, m_sizDim2 = %lu, m_sizDim3 = %lu, m_sizDim4 = %lu\n",
            __FILE__, __LINE__, i_inFS, i_inGS, i_inRS, i_inUS, i_pountObject->m_sizDim1, i_pountObject->m_sizDim2, i_pountObject->m_sizDim3, i_pountObject->m_sizDim4);
        abort();
    }
    l_sizFS = i_inFS > 0 ? (size_t) i_inFS : i_inFS + i_pountObject->m_sizDim1 < 1 ? 1 : i_inFS + i_pountObject->m_sizDim1 + 1;
    l_sizGS = i_inGS > 0 ? (size_t) i_inGS : i_inGS + i_pountObject->m_sizDim2 < 1 ? 1 : i_inGS + i_pountObject->m_sizDim2 + 1;
    l_sizRS = i_inRS > 0 ? (size_t) i_inRS : i_inRS + i_pountObject->m_sizDim3 < 1 ? 1 : i_inRS + i_pountObject->m_sizDim3 + 1;
    l_sizUS = i_inUS > 0 ? (size_t) i_inUS : i_inUS + i_pountObject->m_sizDim4 < 1 ? 1 : i_inUS + i_pountObject->m_sizDim4 + 1;
    l_sizOffset = (l_sizFS - 1) * i_pountObject->m_sizDim2 * i_pountObject->m_sizDim3 * i_pountObject->m_sizDim4;
    l_sizOffset += (l_sizGS - 1) * i_pountObject->m_sizDim3 * i_pountObject->m_sizDim4;
    l_sizOffset += (l_sizRS - 1) * i_pountObject->m_sizDim4;
    l_sizOffset += (l_sizUS - 1);
    return &i_pountObject->m_pounaObject->m_pouniObjects[l_sizOffset];
}

/**
 * @fn "struct UnicodeTesseract * Unicode_to_tesseract(const struct Unicode * i_pouniObject, size_t i_sizCodepoints, size_t i_sizDim1, size_t i_sizDim2, size_t i_sizDim3, size_t i_sizDim4)"
 * @brief Function to split a **Unicode** object into a **UnicodeTesseract** object
 * @details Creates a **UnicodeTesseract** object from a **Unicode** object and
 * determines the dimensions of the **UnicodeTesseract** directly from the
 * codepoints of the **Unicode** object. Only a new **UnicodeTesseract** object
 * is created in dynamic memory.  The **UnicodeTesseract** object mirrors the
 * four dimensional dynamic array contained inside a **Unicode** object. The
 * implementation of the 4-dimensional dynamic array inside a **Unicode**
 * object is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * @note This routine allocates dynamic memory for a new **UnicodeTesseract**
 * object.  It must be deleted by the calling routine after processing is
 * complete (as appropriate) by a call to **UnicodeTesseract_delete()**.  If
 * not done, a memory leak will be created.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in] i_pouniObject = Input pointer to **Unicode** object
 * @param[in] i_sizCodepoints = Maximum size in codepoints of any **UnicodeTesseract** object element
 * @param[in] i_sizDim1 = Number of **Unicode** objects at dimension 1
 * @param[in] i_sizDim2 = Number of **Unicode** objects at dimension 2
 * @param[in] i_sizDim3 = Number of **Unicode** objects at dimension 3
 * @param[in] i_sizDim4 = Number of **Unicode** objects at dimension 4
 * @retval "struct UnicodeTesseract *" = Pointer to newly allocated **UnicodeTesseract** object
 * @exception abort(3) Aborts if **i_pouniObject** is null
 * @exception assert(3) Aborts if **UnicodeTesseract_new** call returns null
 */

struct UnicodeTesseract * Unicode_to_tesseract(
    const struct Unicode * i_pouniObject,
    size_t i_sizCodepoints,
    size_t i_sizDim1,
    size_t i_sizDim2,
    size_t i_sizDim3,
    size_t i_sizDim4)
{
    struct UnicodeTesseract * l_pountObject = 0;
    struct UnicodeArray * l_pounaLevel1 = 0;
    struct UnicodeArray * l_pounaLevel2 = 0;
    struct UnicodeArray * l_pounaLevel3 = 0;
    struct UnicodeArray * l_pounaLevel4 = 0;
    struct Unicode * l_pouniElement = 0;
    struct Unicode * l_pouniFS = 0;
    struct Unicode * l_pouniGS = 0;
    struct Unicode * l_pouniRS = 0;
    struct Unicode * l_pouniUS = 0;
    size_t l_sizCodepoints = 0;
    size_t l_sizBytes = 0;
    int l_inIndex1 = 0;
    int l_inIndex2 = 0;
    int l_inIndex3 = 0;
    int l_inIndex4 = 0;
    int l_inOffset = 0;

    if (i_pouniObject == 0) {
        fprintf(stderr, "%s(%d) = i_pouniObject = %p\n",
            __FILE__, __LINE__, i_pouniObject);
        abort();
    }
    l_pouniFS = Unicode_from_string("\x1C", 1, "ASCII");
    l_pouniGS = Unicode_from_string("\x1D", 1, "ASCII");
    l_pouniRS = Unicode_from_string("\x1E", 1, "ASCII");
    l_pouniUS = Unicode_from_string("\x1F", 1, "ASCII");
    l_pountObject = UnicodeTesseract_new(i_sizCodepoints, i_sizDim1, i_sizDim2, i_sizDim3, i_sizDim4);
    assert(l_pountObject != 0);
    l_pounaLevel1 = Unicode_split(i_pouniObject, l_pouniFS, 2);
    for (l_inIndex1 = 1; (size_t) l_inIndex1 <= l_pounaLevel1->m_sizObjects; l_inIndex1++) {
        l_pounaLevel2 = Unicode_split(&l_pounaLevel1->m_pouniObjects[l_inIndex1 - 1], l_pouniGS, 2);
        for (l_inIndex2 = 1; (size_t) l_inIndex2 <= l_pounaLevel2->m_sizObjects; l_inIndex2++) {
            l_pounaLevel3 = Unicode_split(&l_pounaLevel2->m_pouniObjects[l_inIndex2 - 1], l_pouniRS, 2);
            for (l_inIndex3 = 1; (size_t) l_inIndex3 <= l_pounaLevel3->m_sizObjects; l_inIndex3++) {
                l_pounaLevel4 = Unicode_split(&l_pounaLevel3->m_pouniObjects[l_inIndex3 - 1], l_pouniUS, 2);
                for (l_inIndex4 = 1; (size_t) l_inIndex4 <= l_pounaLevel4->m_sizObjects; l_inIndex4++) {
                    l_inOffset = (l_inIndex1 - 1) * l_pountObject->m_sizDim2 * l_pountObject->m_sizDim3 * l_pountObject->m_sizDim4;
                    l_inOffset += (l_inIndex2 - 1) * l_pountObject->m_sizDim3 * l_pountObject->m_sizDim4;
                    l_inOffset += (l_inIndex3 - 1) * l_pountObject->m_sizDim4;
                    l_inOffset += (l_inIndex4 - 1);
                    l_pouniElement = &l_pounaLevel4->m_pouniObjects[l_inIndex4 - 1];
                    l_sizBytes = i_sizCodepoints * sizeof(wchar_t);
                    l_pountObject->m_pounaObject->m_pouniObjects[l_inOffset].m_poszCodepoints = l_pountObject->m_poszCodepoints + l_inOffset * l_sizBytes;
                    l_sizCodepoints = i_sizCodepoints <= l_pouniElement->m_sizCodepoints ? i_sizCodepoints : l_pouniElement->m_sizCodepoints;
                    memcpy(l_pountObject->m_pounaObject->m_pouniObjects[l_inOffset].m_poszCodepoints, l_pouniElement->m_poszCodepoints, l_sizCodepoints * sizeof(wchar_t));
                    l_pountObject->m_pounaObject->m_pouniObjects[l_inOffset].m_sizCodepoints = l_sizCodepoints;
                    l_pountObject->m_pounaObject->m_pouniObjects[l_inOffset].m_sizBytes = l_sizCodepoints * sizeof(wchar_t);
                }
                UnicodeArray_delete(&l_pounaLevel4);
            }
            UnicodeArray_delete(&l_pounaLevel3);
        }
        UnicodeArray_delete(&l_pounaLevel2);
    }
    UnicodeArray_delete(&l_pounaLevel1);
    Unicode_delete(&l_pouniFS);
    Unicode_delete(&l_pouniGS);
    Unicode_delete(&l_pouniRS);
    Unicode_delete(&l_pouniUS);
    return l_pountObject;
}

/**
 * @fn "struct Unicode * Unicode_from_tesseract(const struct UnicodeTesseract * i_pountObject)"
 * @brief Function to join **UnicodeTesseract** object elements in a **Unicode** object
 * @details Creates a **Unicode** object from the elements stored inside a
 * **UnicodeTesseract** object.  Only a new **Unicode** object is created in
 * dynamic memory.  The **Unicode** object will contain a four dimensional
 * dynamic array that mirrors the elements in the **UnicodeTesseract** object.
 * The implementation of the 4-dimensional dynamic array inside a **Unicode**
 * object is structured as follows:
 * - Value contains level 1 subvalues delimited by FS characters
 * - Level 1 subvalues contain level 2 subvalues delimited by GS characters
 * - Level 2 subvalues contain level 3 subvalues delimited by RS characters
 * - Level 3 subvalues contain level 4 subvalues delimited by US characters
 *
 * @note This routine allocates dynamic memory for a new **Unicode**
 * object.  It must be deleted by the calling routine after processing is
 * complete (as appropriate) by a call to **UnicodeTesseract_delete()**.  If
 * not done, a memory leak will be created.
 *
 * #### Example ####
 *
 * @code
 * int l_inCodepoints = 10, l_inDim1 = 10, l_inDim2 = 10, l_inDim3 = 10, l_inDim4 = 10;
 * int l_inLevel1 = 0, l_inLevel2 = 0, l_inLevel3 = 0, l_inLevel4 = 0, l_inElement = 0, l_inSubvalue = 0;
 * struct UnicodeTesseract * l_pountObject = 0;
 * struct Unicode * l_pouniObject = 0;
 * struct Unicode * l_pouniElement = 0;
 * l_pountObject = UnicodeTesseract_new(l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_inElement = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 l_pouniElement = Unicode::from_int(l_inElement);
 *                 assert(l_pouniElement != 0);
 *                 Unicode_set_element(l_pountObject, l_pouniElement, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 Unicode_clear(&l_pouniElement);
 *             }
 *         }
 *     }
 * }
 * l_pouniObject = Unicode_from_tesseract(l_pountObject);
 * l_pountObject = Unicode_to_tesseract(l_pouniObject, l_inCodepoints, l_inDim1, l_inDim2, l_inDim3, l_inDim4);
 * for (l_inLevel1 = 1; l_inLevel1 <= l_inDim1; l_inLevel1++) {
 *     for (l_inLevel2 = 1; l_inLevel2 <= l_inDim2; l_inLevel2++) {
 *         for (l_inLevel3 = 1; l_inLevel3 <= l_inDim3; l_inLevel3++) {
 *             for (l_inLevel4 = 1; l_inLevel4 <= l_inDim4; l_inLevel4++) {
 *                 l_pouniElement = Unicode_get_element(l_pountObject, l_inLevel1, l_inLevel2, l_inLevel3, l_inLevel4);
 *                 assert(l_pouniElement != 0);
 *                 l_inElement = l_pouniElement->to_int();
 *                 l_inSubvalue = (l_inLevel1 - 1) * 1000000 + (l_inLevel2 - 1) * 10000 + (l_inLevel3 - 1) * 100 + (l_inLevel4 - 1);
 *                 assert(l_inElement == l_inSubvalue);
 *             }
 *         }
 *     }
 * }
 * UnicodeTesseract_delete(&l_pountObject);
 * @endcode
 *
 * @param[in] i_pountObject = Input pointer to **UnicodeTesseract** object
 * @retval "struct Unicode *" = Pointer to newly allocated **Unicode** object
 * @exception abort(3) Aborts if **i_pouniObject** is null
 * @exception assert(3) Aborts if **Unicode_new** call returns null
 * @exception assert(3) Aborts if **realloc(3)** call returns null
 */

struct Unicode * Unicode_from_tesseract(const struct UnicodeTesseract * i_pountObject)
{
    const int l_inBytes = 2097152;  //!< Expanding buffer extent size of 2 megabytes
    struct Unicode * l_pouniObject = 0;
    wchar_t * l_powzObject = 0;
    wchar_t * l_powzElement = 0;
    wchar_t l_wcFS = L'\x1C';
    wchar_t l_wcGS = L'\x1D';
    wchar_t l_wcRS = L'\x1E';
    wchar_t l_wcUS = L'\x1F';
    int l_inIndex1 = 0;
    int l_inIndex2 = 0;
    int l_inIndex3 = 0;
    int l_inIndex4 = 0;
    int l_inElement = 0;
    int l_inOffset = 0;
    int l_inCodepoints = 0;
 
    if (i_pountObject == 0) {
        fprintf(stderr, "%s(%d) = i_pountObject = %p\n",
            __FILE__, __LINE__, i_pountObject);
        abort();
    }
    l_pouniObject = Unicode_new();
    l_pouniObject->m_poszCodepoints = (char *) calloc(1, l_inBytes);  //!< Buffer extent size
    assert(l_pouniObject->m_poszCodepoints != 0);
    l_powzObject = (wchar_t *) l_pouniObject->m_poszCodepoints;
    for (l_inIndex1 = 1; (size_t) l_inIndex1 <= i_pountObject->m_sizDim1; l_inIndex1++) {
        if (l_inIndex1 == 1) {
            l_powzObject[l_inOffset++] = l_wcFS;
            l_pouniObject->m_sizCodepoints++;
            l_pouniObject->m_sizBytes += sizeof(wchar_t);
            if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                assert(l_powzObject != 0);
            }
        }
        for (l_inIndex2 = 1; (size_t) l_inIndex2 <= i_pountObject->m_sizDim2; l_inIndex2++) {
            if (l_inIndex2 == 1) {
                l_powzObject[l_inOffset++] = l_wcGS;
                l_pouniObject->m_sizCodepoints++;
                l_pouniObject->m_sizBytes += sizeof(wchar_t);
                if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                    l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                    assert(l_powzObject != 0);
                }
            }
            for (l_inIndex3 = 1; (size_t) l_inIndex3 <= i_pountObject->m_sizDim3; l_inIndex3++) {
                if (l_inIndex3 == 1) {
                    l_powzObject[l_inOffset++] = l_wcRS;
                    l_pouniObject->m_sizCodepoints++;
                    l_pouniObject->m_sizBytes += sizeof(wchar_t);
                    if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                        l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                        assert(l_powzObject != 0);
                    }
                }
                for (l_inIndex4 = 1; (size_t) l_inIndex4 <= i_pountObject->m_sizDim4; l_inIndex4++) {
                    if (l_inIndex4 == 1) {
                        l_powzObject[l_inOffset++] = l_wcUS;
                        l_pouniObject->m_sizCodepoints++;
                        l_pouniObject->m_sizBytes += sizeof(wchar_t);
                        if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                            l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                            assert(l_powzObject != 0);
                        }
                    }
                    l_inElement = (l_inIndex1 - 1) * i_pountObject->m_sizDim2 * i_pountObject->m_sizDim3 * i_pountObject->m_sizDim4;
                    l_inElement += (l_inIndex2 - 1) * i_pountObject->m_sizDim3 * i_pountObject->m_sizDim4;
                    l_inElement += (l_inIndex3 - 1) * i_pountObject->m_sizDim4;
                    l_inElement += (l_inIndex4 - 1);
                    l_powzElement = (wchar_t *) i_pountObject->m_pounaObject->m_pouniObjects[l_inElement].m_poszCodepoints;
                    for (l_inCodepoints = 0; (size_t) l_inCodepoints < i_pountObject->m_pounaObject->m_pouniObjects[l_inElement].m_sizCodepoints; l_inCodepoints++) {
                        l_powzObject[l_inOffset++] = l_powzElement[l_inCodepoints];
                        if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                            l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                            assert(l_powzObject != 0);
                        }
                    }
                    l_pouniObject->m_sizCodepoints += l_inCodepoints;
                    l_pouniObject->m_sizBytes += l_inCodepoints * sizeof(wchar_t);
                    l_powzObject[l_inOffset++] = l_wcUS;
                    l_pouniObject->m_sizCodepoints++;
                    l_pouniObject->m_sizBytes += sizeof(wchar_t);
                    if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                        l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                        assert(l_powzObject != 0);
                    }
                }
                l_powzObject[l_inOffset++] = l_wcRS;
                l_pouniObject->m_sizCodepoints++;
                l_pouniObject->m_sizBytes += sizeof(wchar_t);
                if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                    l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                    assert(l_powzObject != 0);
                }
            }
            l_powzObject[l_inOffset++] = l_wcGS;
            l_pouniObject->m_sizCodepoints++;
            l_pouniObject->m_sizBytes += sizeof(wchar_t);
            if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
                l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
                assert(l_powzObject != 0);
            }
        }
        l_powzObject[l_inOffset++] = l_wcFS;
        l_pouniObject->m_sizCodepoints++;
        l_pouniObject->m_sizBytes += sizeof(wchar_t);
        if (l_inOffset % (l_inBytes / sizeof(wchar_t)) == 0) {
            l_powzObject = (wchar_t *) realloc(l_powzObject, l_inOffset * sizeof(wchar_t) + l_inBytes);
            assert(l_powzObject != 0);
        }
    }
    l_powzObject[l_pouniObject->m_sizCodepoints] = 0;
    l_pouniObject->m_poszCodepoints = (char *) realloc(l_powzObject, l_pouniObject->m_sizBytes + sizeof(wchar_t));
    return l_pouniObject;
}

#ifdef __cplusplus
}
#endif

Leave a comment

Design a site like this with WordPress.com
Get started