//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#include <framecpp_config.h>

#include <iostream>
#include <algorithm>
#include <stdexcept>

#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>

#include "framecpp/Common/Compression.hh"
#include "framecpp/Common/IOStream.hh"
#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameSpec.tcc"

#include "framecpp/Universal/impl/DimensionClassicIO.hh"
#include "framecpp/Universal/impl/FrVectCompressionMapping.hh"

#include "framecpp/Universal/FrVect.hh"
#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/FrSH.hh"

#include "framecpp/Universal/PTR_STRUCT.hh"


using namespace FrameCPP::Universal;
using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;

using namespace FrameCPP::Compression;

#define LM_DEBUG 0
#define LM_INFO 0

#if LM_DEBUG
#define AT( ) std::cerr << "INFO: " << __FILE__ << " " << __LINE__ << std::endl;
#else
#define AT( )
#endif

#if HAVE_TEMPLATE_MOVE
#define MOVE_RET( a ) ( std::move( a ) )
#else /* HAVE_TEMPLATE_MOVE */
#define MOVE_RET( a ) ( a )
#endif /* HAVE_TEMPLATE_MOVE */

//=======================================================================
// Static
//=======================================================================

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrVectImpl,
                                      FSI_FR_VECT,
                                      "FrVect",
                                      "Vector Data Structure" )
#define TRACE_MEMORY 0
#if TRACE_MEMORY
#define MEM_ALLOCATE( )                                                        \
    std::cerr << "MEMORY: Allocate: " << FrVect::getStaticName( ) << " "       \
              << (void*)this << " Line: " << __LINE__ << std::endl;
#define MEM_DELETE( )                                                          \
    std::cerr << "MEMORY: Delete: " << FrVect::getStaticName( ) << " "         \
              << (void*)this << " Line: " << __LINE__ << std::endl;
#else
#define MEM_ALLOCATE( )
#define MEM_DELETE( )
#endif

namespace FrameCPP
{
    namespace Universal
    {
        namespace FrVectImpl
        {
            template <>
            Data::type_type
            Data::GetDataType< CHAR >( )
            {
                return FR_VECT_C;
            }

            template <>
            Data::type_type
            Data::GetDataType< CHAR_U >( )
            {
                return FR_VECT_1U;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_2S >( )
            {
                return FR_VECT_2S;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_2U >( )
            {
                return FR_VECT_2U;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_4S >( )
            {
                return FR_VECT_4S;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_4U >( )
            {
                return FR_VECT_4U;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_8S >( )
            {
                return FR_VECT_8S;
            }

            template <>
            Data::type_type
            Data::GetDataType< INT_8U >( )
            {
                return FR_VECT_8U;
            }

            template <>
            Data::type_type
            Data::GetDataType< REAL_4 >( )
            {
                return FR_VECT_4R;
            }

            template <>
            Data::type_type
            Data::GetDataType< REAL_8 >( )
            {
                return FR_VECT_8R;
            }

            template <>
            Data::type_type
            Data::GetDataType< COMPLEX_8 >( )
            {
                return FR_VECT_8C;
            }

            template <>
            Data::type_type
            Data::GetDataType< COMPLEX_16 >( )
            {
                return FR_VECT_16C;
            }

            template <>
            Data::type_type
            Data::GetDataType< std::string >( )
            {
                return FR_VECT_STRING;
            }

            void
            Data::compressToBuffer(
                const Compression::compress_type_mapping& CompressionMapping,
                const Compression::compress_type_reverse_mapping&
                                                      CompressionReverseMapping,
                const Compression::data_type_mapping& DataTypeMapping,
                const INT_4U                          DataType,
                const INT_8U                          NData,
                const CHAR_U*                         Source,
                const INT_8U                          SourceNBytes,
                const INT_4U                          SourceCompressionMode,
                boost::shared_array< CHAR_U >&        Dest,
                INT_8U&                               DestNBytes,
                INT_4U&                               DestCompressionMode,
                const INT_4U                          Level ) const
            {
                boost::shared_array< CHAR_U > expanded_buffer;

                const INT_8U nData( NData );
                INT_8U       nBytes( SourceNBytes );

#if WORKING
                //---------------------------------------------------------------------
                // Ensure the buffer is fully expanded
                /// \todo need to ensure that this is working for both
                /// endiannesses.
                //---------------------------------------------------------------------
                if ( ( SourceCompressionMode & 0xFF ) != Compression::MODE_RAW )
                {
                    expandToBuffer( CompressionMapping,
                                    DataTypeMapping,
                                    DataType,
                                    NData,
                                    Source,
                                    SourceNBytes,
                                    SourceCompressionMode,
                                    expanded_buffer,
                                    nBytes );
                }
#endif /* WORKING */

                //-----------------------------------------------------------------
                // Now compress the data
                //-----------------------------------------------------------------

                INT_4U compress = ( DestCompressionMode & 0xFF );
                boost::shared_array< CHAR_U > data_out;
                INT_8U                        data_out_len( 0 );

                FrameCPP::Compression::Compress(
                    compress,
                    Level,
                    CompressionMapping,
                    CompressionReverseMapping,
                    DataType,
                    DataTypeMapping,
                    ( expanded_buffer.get( ) ) // Have uncompressed buffer?
                        ? expanded_buffer.get( ) // Yes; use it
                        : Source, // No; use data buffer
                    nData,
                    nBytes,
                    data_out,
                    data_out_len );
                //-----------------------------------------------------------------
                // Final validation before returning to the caller
                //-----------------------------------------------------------------
                Dest = data_out;
                DestNBytes = data_out_len;

                if ( data_out_len == 0 )
                {
                    Dest.reset( (CHAR_U*)NULL );
                }
                else
                {
                    DestCompressionMode = compress;
                }
            } // method - FrVect::compressToBuffer

            void
            Data::expandToBuffer(
                const Compression::compress_type_mapping& CompressionMapping,
                const Compression::data_type_mapping&     DataTypeMapping,
                const INT_4U                              DataType,
                const INT_8U                              NData,
                const CHAR_U*                             Source,
                const INT_8U                              SourceNBytes,
                const INT_4U                              SourceCompressionMode,
                const INT_4U                              SourceByteOrder,
                boost::shared_array< CHAR_U >&            Dest,
                INT_8U&                                   DestNBytes ) const
            {
                //---------------------------------------------------------------------
                // Uncompress to local buffers, then use those buffers as
                //       input to the compression routines
                //---------------------------------------------------------------------
                boost::shared_array< CHAR_U > data_out;
                INT_8U                        data_out_len( 0 );

                FrameCPP::Compression::Expand(
                    ( BYTE_ORDER_HOST == SourceByteOrder ),
                    ( SourceCompressionMode & 0xFF ),
                    CompressionMapping,
                    DataType,
                    DataTypeMapping,
                    Source,
                    NData,
                    SourceNBytes,
                    data_out,
                    data_out_len );
                DestNBytes = data_out_len;
                if ( data_out )
                {
                    Dest = data_out;
                    if ( DestNBytes == 0 )
                    {
                        //-----------------------------------------------------------------
                        // Deallocate the buffer since data_out_length shows a
                        // buffer of size zero
                        //-----------------------------------------------------------------
                        Dest.reset( (CHAR_U*)NULL );
                    }
                }
                else
                {
                    Dest.reset( (CHAR_U*)NULL );
                    DestNBytes = 0;
                }
            } // method - FrVect::expandToBuffer

            FrameCPP::Compression::compress_type_mapping&
            Data::compress_type_map( )
            {
                static FrameCPP::Compression::compress_type_mapping m;

                if ( m.size( ) == 0 )
                {
#define INIT( x, y ) m[ compression_scheme_type::BIGENDIAN_##x ] = FrameCPP::Compression::y
                    INIT( RAW, MODE_RAW );
                    INIT( GZIP, MODE_GZIP );
                    INIT( DIFF_GZIP, MODE_DIFF_GZIP );
                    INIT( ZERO_SUPPRESS, MODE_ZERO_SUPPRESS );
                    INIT( ZSTD, MODE_ZSTD );
                    INIT( DIFF_ZSTD, MODE_DIFF_ZSTD );
                    INIT( ZERO_SUPPRESS_OTHERWISE_GZIP, MODE_ZERO_SUPPRESS_2_4_8_OTHERWISE_GZIP );
#undef INIT
                }
                return m;
            }

            FrameCPP::Compression::compress_type_reverse_mapping&
            Data::compress_type_reverse_map( )
            {
                static FrameCPP::Compression::compress_type_reverse_mapping m;

                if ( m.size( ) == 0 )
                {
#define INIT( x, y ) m[ FrameCPP::Compression::x ] = compression_scheme_type::BIGENDIAN_##y;
                    INIT( MODE_RAW, RAW );
                    INIT( MODE_GZIP, GZIP );
                    INIT( MODE_DIFF_GZIP, DIFF_GZIP );
                    INIT( MODE_ZERO_SUPPRESS, ZERO_SUPPRESS );
                    INIT( MODE_ZERO_SUPPRESS_SHORT, ZERO_SUPPRESS );
                    INIT( MODE_ZERO_SUPPRESS_INT_FLOAT, ZERO_SUPPRESS );
                    INIT( MODE_ZERO_SUPPRESS_WORD_4, ZERO_SUPPRESS );
                    INIT( MODE_ZERO_SUPPRESS_WORD_8, ZERO_SUPPRESS );
                    INIT( MODE_ZSTD, ZSTD );
                    INIT( MODE_DIFF_ZSTD, DIFF_ZSTD );
                    INIT( MODE_ZERO_SUPPRESS_2_OTHERWISE_GZIP, ZERO_SUPPRESS_OTHERWISE_GZIP );
                    INIT( MODE_ZERO_SUPPRESS_2_4_OTHERWISE_GZIP, ZERO_SUPPRESS_OTHERWISE_GZIP );
                    INIT( MODE_ZERO_SUPPRESS_2_4_8_OTHERWISE_GZIP, ZERO_SUPPRESS_OTHERWISE_GZIP );
                    INIT( MODE_ZERO_SUPPRESS_2_OTHERWISE_ZSTD, ZERO_SUPPRESS_OTHERWISE_ZSTD );
                    INIT( MODE_ZERO_SUPPRESS_2_4_OTHERWISE_ZSTD, ZERO_SUPPRESS_OTHERWISE_ZSTD );
                    INIT( MODE_ZERO_SUPPRESS_2_4_8_OTHERWISE_ZSTD, ZERO_SUPPRESS_OTHERWISE_ZSTD );
#undef INIT
                }
                return m;
            }

            FrameCPP::Compression::data_type_mapping&
            Data::data_type_map( )
            {
                static FrameCPP::Compression::data_type_mapping m;

                if ( m.size( ) == 0 )
                {
#define INIT( x ) m[ data_types_type::x ] = FrameCPP::Compression::x
                    INIT( FR_VECT_C );
                    INIT( FR_VECT_2S );
                    INIT( FR_VECT_8R );
                    INIT( FR_VECT_4R );
                    INIT( FR_VECT_4S );
                    INIT( FR_VECT_8S );
                    INIT( FR_VECT_8C );
                    INIT( FR_VECT_16C );
                    INIT( FR_VECT_STRING );
                    INIT( FR_VECT_2U );
                    INIT( FR_VECT_4U );
                    INIT( FR_VECT_8U );
                    INIT( FR_VECT_1U );
#undef INIT
                }
                return m;
            }

            FrameCPP::Compression::compress_type_mapping&
            Data::compress_type_map_v8( )
            {
                static FrameCPP::Compression::compress_type_mapping m;

                if ( m.size( ) == 0 )
                {
                    m[ v8::BIGENDIAN_RAW ] = FrameCPP::Compression::MODE_RAW;
                    m[ v8::BIGENDIAN_GZIP ] = FrameCPP::Compression::MODE_GZIP;
                    m[ v8::BIGENDIAN_DIFF_GZIP ] = FrameCPP::Compression::MODE_DIFF_GZIP;
                    m[ v8::BIGENDIAN_ZERO_SUPPRESS_WORD_2 ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_SHORT;
                    m[ v8::BIGENDIAN_ZERO_SUPPRESS_WORD_4 ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_WORD_4;
                    m[ v8::BIGENDIAN_ZERO_SUPPRESS_WORD_8 ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_WORD_8;
                    m[ v8::ZERO_SUPPRESS_OTHERWISE_GZIP ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_2_4_8_OTHERWISE_GZIP;
                }

                return m;
            }

            FrameCPP::Compression::compress_type_mapping&
            Data::compress_type_map_v5( )
            {
                static FrameCPP::Compression::compress_type_mapping m;

                if ( m.size( ) == 0 )
                {
                    m[ v5::BIGENDIAN_RAW ] = FrameCPP::Compression::MODE_RAW;
                    m[ v5::BIGENDIAN_GZIP ] = FrameCPP::Compression::MODE_GZIP;
                    m[ v5::BIGENDIAN_DIFF_GZIP ] = FrameCPP::Compression::MODE_DIFF_GZIP;
                    m[ v5::BIGENDIAN_ZERO_SUPPRESS_SHORT ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_SHORT;
                    m[ v5::BIGENDIAN_ZERO_SUPPRESS_INT_FLOAT ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_INT_FLOAT;
                    m[ v5::ZERO_SUPPRESS_OTHERWISE_GZIP ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_2_4_OTHERWISE_GZIP;
                }

                return m;
            }

            FrameCPP::Compression::compress_type_mapping&
            Data::compress_type_map_v4( )
            {
                static FrameCPP::Compression::compress_type_mapping m;

                if ( m.size( ) == 0 )
                {
                    m[ v4::BIGENDIAN_RAW ] = FrameCPP::Compression::MODE_RAW;
                    m[ v4::BIGENDIAN_GZIP ] = FrameCPP::Compression::MODE_GZIP;
                    m[ v4::BIGENDIAN_DIFF ] = FrameCPP::Compression::MODE_DIFF;
                    m[ v4::BIGENDIAN_DIFF_GZIP ] = FrameCPP::Compression::MODE_DIFF_GZIP;
                    m[ v4::BIGENDIAN_ZERO_SUPPRESS_SHORT ] = FrameCPP::Compression::MODE_ZERO_SUPPRESS_SHORT;
                }

                return m;
            }

            FrameCPP::Compression::compress_type_mapping&
            Data::compress_type_map_v3( )
            {
                static FrameCPP::Compression::compress_type_mapping m;

                if ( m.size( ) == 0 )
                {
                    m[ v3::BIGENDIAN_RAW ] = FrameCPP::Compression::MODE_RAW;
                    m[ v3::BIGENDIAN_GZIP ] = FrameCPP::Compression::MODE_GZIP;
                    m[ v3::BIGENDIAN_DIFF ] = FrameCPP::Compression::MODE_DIFF;
                    m[ v3::BIGENDIAN_DIFF_GZIP ] = FrameCPP::Compression::MODE_DIFF_GZIP;
                }

                return m;
            }

            template <>
            bool
            ClassicIO< FrameCPP::Universal::FrVect >::
            operator==( const Common::FrameSpec::Object& Obj ) const
            {
                // Cast *this to FrVect so the FrameCPP::compare<T>
                // template deduces T = FrVect and ADL on FrVect finds
                // the typed hidden-friend operator==.
                return FrameCPP::compare(
                    static_cast< const FrameCPP::Universal::FrVect& >(
                        *this ),
                    Obj );
            }

            template <>
            template <>
            FrameCPP::cmn_streamsize_type
            ClassicIO< FrameCPP::Universal::FrVect >::get_bytes_for_object<
                std::vector< FrameCPP::Universal::FrVect::dimension_type > >(
                const std::vector<
                    FrameCPP::Universal::FrVect::dimension_type >& Dims,
                const Common::StreamBase&                          Stream )
            {
                INT_8U ret( sizeof( nDim_type ) ); // nDim
                for ( std::vector< dimension_type >::const_iterator d(
                          Dims.begin( ) );
                      d != Dims.end( );
                      d++ )
                {
                    ret += d->Bytes( Stream );
                }
                return ret;
            } // Bytes

            //-----------------------------------------------------------------
            /// @brief The data structure description
            ///
            /// @return
            ///     The description of the data structure used to represent
            ///     this object when written to a GWF stream
            //-----------------------------------------------------------------
            template <>
            const Common::Description*
            ClassicIO< FrVect >::GetDescription( unsigned int frame_stream_version ) const
            {
                using Common::Description;

                Description* description = nullptr;

                if ( frame_stream_version >= 9 )
                {
                    static Description description_v9;

                    if ( description_v9.size( ) == 0 )
                    {
                        description_v9( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        description_v9( FrSE(
                            "name",
                            Description::Ascii< io_name_type >( ),
                            "Channel name -- not required to be unique" ) );
                        description_v9(
                            FrSE( "compress",
                                  Description::Ascii< compress_type >( ),
                                  "Compression algorithm number" ) );
                        description_v9(
                            FrSE( "type",
                                  Description::Ascii< type_type >( ),
                                  "Vector class" ) );
                        description_v9( FrSE(
                            "nData",
                            Description::Ascii< nData_type >( ),
                            "Number of sample elements in data series" ) );
                        description_v9( FrSE(
                            "nBytes",
                            Description::Ascii< nBytes_type >( ),
                            "Number of bytes in the compressed vector" ) );
                        description_v9(
                            FrSE( "data",
                                  Description::AsciiArray< CHAR_U >( "nBytes" ),
                                  "nData elements of specified class" ) );
                        description_v9(
                            FrSE( "nDim",
                                  Description::Ascii< nDim_type >( ),
                                  "Dimensionality of data vector" ) );
                        description_v9(
                            FrSE( "nx", Description::AsciiArray< DimensionImpl::Data::v9::nx_type >( "nDim" ), "dimension lengths" ) );
                        description_v9(
                            FrSE( "dx",
                                  Description::AsciiArray< DimensionImpl::Data::v9::dx_type >( "nDim" ),
                                  "sample spacing along each coordinate" ) );
                        description_v9( FrSE( "startX",
                                              Description::AsciiArray< DimensionImpl::Data::v9::startX_type >( "nDim" ),
                                              "Origin for each data set" ) );
                        description_v9( FrSE( "unitX",
                                              Description::AsciiArray< DimensionImpl::Data::v9::unitX_type >( "nDim" ),
                                              "scale factors in ASCII" ) );
                        description_v9(
                            FrSE( "unitY",
                                  Description::Ascii< io_unit_y_type >( ),
                                  "String describing how to interpret the "
                                  "value of each element" ) );
                        //-----------------------------------------------
                        // dataValid elements
                        //-----------------------------------------------
                        description_v9(
                            FrSE( "nDataValid",
                                  Description::Ascii< v9::nDataValid_type >( ),
                                  "A value of zero indicates no dataValid "
                                  "data is present; "
                                  "a non-zero value indicates dataValid "
                                  "container data" ) );
                        description_v9( FrSE( "dataValidCompScheme",
                                              Description::Ascii< INT_2U >( ),
                                              "Compression scheme" ) );
                        description_v9( FrSE(
                            "nDataValidCompBytes",
                            Description::Ascii< v9::nDataValidCompBytes_type >( ),
                            "Number of compressed byes in the dataValid" ) );
                        description_v9(
                            FrSE( "dataValid",
                                  Description::AsciiArray< CHAR_U >( "nDataValidCompBytes" ),
                                  "This is an array of dataValid values "
                                  "convering the span of "
                                  "the FrVect.data. "
                                  "A value of 0 in teh array signifies the "
                                  "corresponding block of data is valid. "
                                  "A non-zero value in "
                                  "the array signifies daa as suspect" ) );
                        //-----------------------------------------------
                        description_v9(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::FrameSpecName( ) ),
                                  "Identifier for additional data." ) );
                        //-----------------------------------------------
                        description_v9( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }

                    description = &description_v9;
                }
                else if ( frame_stream_version >= 8 )
                {
                    static Description description_v8;

                    if ( description_v8.size( ) == 0 )
                    {
                        description_v8( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        description_v8( FrSE(
                            "name",
                            Description::Ascii< io_name_type >( ),
                            "Channel name -- not required to be unique" ) );
                        description_v8(
                            FrSE( "compress",
                                  Description::Ascii< compress_type >( ),
                                  "Compression algorithm number" ) );
                        description_v8(
                            FrSE( "type",
                                  Description::Ascii< type_type >( ),
                                  "Vector class" ) );
                        description_v8( FrSE(
                            "nData",
                            Description::Ascii< nData_type >( ),
                            "Number of sample elements in data series" ) );
                        description_v8( FrSE(
                            "nBytes",
                            Description::Ascii< nBytes_type >( ),
                            "Number of bytes in the compressed vector" ) );
                        description_v8(
                            FrSE( "data",
                                  Description::AsciiArray< CHAR_U >( "nBytes" ),
                                  "nData elements of specified class" ) );
                        description_v8(
                            FrSE( "nDim",
                                  Description::Ascii< nDim_type >( ),
                                  "Dimensionality of data vector" ) );
                        description_v8(
                            FrSE( "nx", Description::AsciiArray< DimensionImpl::Data::v8::nx_type >( "nDim" ), "dimension lengths" ) );
                        description_v8(
                            FrSE( "dx",
                                  Description::AsciiArray< DimensionImpl::Data::v8::dx_type >( "nDim" ),
                                  "sample spacing along each coordinate" ) );
                        description_v8( FrSE( "startX",
                                              Description::AsciiArray< DimensionImpl::Data::v8::startX_type >( "nDim" ),
                                              "Origin for each data set" ) );
                        description_v8( FrSE( "unitX",
                                              Description::AsciiArray< DimensionImpl::Data::v8::unitX_type >( "nDim" ),
                                              "scale factors in ASCII" ) );
                        description_v8(
                            FrSE( "unitY",
                                  Description::Ascii< io_unit_y_type >( ),
                                  "String describing how to interpret the "
                                  "value of each element" ) );

                        description_v8(
                            FrSE( "next",
                                  PTR_STRUCT::Desc( Data::FrameSpecName( ) ),
                                  "Identifier for additional data." ) );

                        description_v8( FrSE( "chkSum",
                                              CheckSumDataClass( ),
                                              CheckSumDataComment( ) ) );
                    }

                    description = &description_v8;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description description_v5;

                    if ( description_v5.size( ) == 0 )
                    {
                        description_v5( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        description_v5( FrSE( "name", Description::Ascii< v5::name_type >( ), "" ) );
                        description_v5( FrSE( "compress", Description::Ascii< v5::compress_type >( ), "" ) );
                        description_v5( FrSE( "type", Description::Ascii< v5::type_type >( ), "" ) );
                        description_v5( FrSE( "nData", Description::Ascii< v5::nData_type >( ), "" ) );
                        description_v5( FrSE( "nBytes", Description::Ascii< v5::nBytes_type >( ), "" ) );
                        description_v5( FrSE( "data", Description::AsciiArray< CHAR_U >( "nBytes" ), "" ) );
                        description_v5( FrSE( "nDim", Description::Ascii< DimensionImpl::Data::v5::nDim_type >( ), "" ) );
                        description_v5( FrSE( "nx", Description::AsciiArray< DimensionImpl::Data::v5::nx_type >( "nDim" ), "" ) );
                        description_v5( FrSE( "dx", Description::AsciiArray< DimensionImpl::Data::v5::dx_type >( "nDim" ), "" ) );
                        description_v5( FrSE( "startX", Description::AsciiArray< DimensionImpl::Data::v5::startX_type >( "nDim" ), "" ) );
                        description_v5( FrSE( "unitX", Description::AsciiArray< DimensionImpl::Data::v5::unitX_type >( "nDim" ), "" ) );
                        description_v5( FrSE( "unitY", Description::Ascii< v5::unitY_type >( ), "" ) );

                        description_v5( FrSE(
                            "next",
                            PTR_STRUCT::Desc( Data::FrameSpecName( ) ) ) );
                    }
                    description = &description_v5;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description description_v4;

                    if ( description_v4.size( ) == 0 )
                    {
                        description_v4( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        description_v4( FrSE( "name", Description::Ascii< v4::name_type >( ), "" ) );
                        description_v4( FrSE( "compress", Description::Ascii< v4::compress_type >( ), "" ) );
                        description_v4( FrSE( "type", Description::Ascii< v4::type_type >( ), "" ) );
                        description_v4( FrSE( "nData", Description::Ascii< v4::nData_type >( ), "" ) );
                        description_v4( FrSE( "nBytes", Description::Ascii< v4::nBytes_type >( ), "" ) );
                        description_v4( FrSE( "data", Description::AsciiArray< CHAR_U >( "nBytes" ), "" ) );
                        description_v4( FrSE( "nDim", Description::Ascii< DimensionImpl::Data::v4::nDim_type >( ), "" ) );
                        description_v4( FrSE( "nx", Description::Ascii< DimensionImpl::Data::v4::nx_type *>( ), "" ) );
                        description_v4( FrSE( "dx", Description::Ascii< DimensionImpl::Data::v4::dx_type *>( ), "" ) );
                        description_v4( FrSE( "startX", Description::Ascii< DimensionImpl::Data::v4::startX_type *>( ), "" ) );
                        description_v4( FrSE( "unitX", Description::Ascii< DimensionImpl::Data::v4::unitX_type *>( ), "" ) );
                        description_v4( FrSE( "unitY", Description::Ascii< v4::unitY_type >( ), "" ) );

                        description_v4( FrSE(
                            "next",
                            PTR_STRUCT::Desc( Data::FrameSpecName( ) ) ) );
                    }
                    description = &description_v4;
                }
                else if ( frame_stream_version >= 3 )
                {
                    static Description description_v3;

                    if ( description_v3.size( ) == 0 )
                    {
                        description_v3( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );

                        description_v3( FrSE( "name", Description::Ascii< v3::name_type >( ), "" ) );
                        description_v3( FrSE( "compress", Description::Ascii< v3::compress_type >( ), "" ) );
                        description_v3( FrSE( "type", Description::Ascii< v3::type_type >( ), "" ) );
                        description_v3( FrSE( "nData", Description::Ascii< v3::nData_type >( ), "" ) );
                        description_v3( FrSE( "nBytes", Description::Ascii< v3::nBytes_type >( ), "" ) );
                        description_v3( FrSE( "data", Description::AsciiArray< CHAR_U >( "nBytes" ), "" ) );
                        description_v3( FrSE( "nDim", Description::Ascii< DimensionImpl::Data::v3::nDim_type >( ), "" ) );
                        description_v3( FrSE( "nx", Description::Ascii< DimensionImpl::Data::v3::nx_type *>( ), "" ) );
                        description_v3( FrSE( "dx", Description::Ascii< DimensionImpl::Data::v3::dx_type *>( ), "" ) );
                        description_v3( FrSE( "unitX", Description::Ascii< DimensionImpl::Data::v3::unitX_type *>( ), "" ) );
                        description_v3( FrSE( "unitY", Description::Ascii< v3::unitY_type >( ), "" ) );

                        description_v3( FrSE(
                            "next",
                            PTR_STRUCT::Desc( Data::FrameSpecName( ) ) ) );
                    }
                    description = &description_v3;
                }

                return description;
            }

            template <>
            INT_2U
            ClassicIO< Universal::FrVect >::compressDemote(
                Common::FrameSpec::version_type FrameVersion )
            {
                using namespace CompressionMapping;

                INT_2U current_compress( m_data.compress );
                INT_2U retval( m_data.compress );

                // Check if target version is current version (no conversion needed)
                // NOTE: When v10 is added, change this constant to 10
                if ( FrameVersion >= 9 )
                {
                    return retval;
                }

                //=================================================================
                // ITERATIVE ADJACENT-VERSION CONVERSION
                //=================================================================
                // Convert step-by-step through adjacent versions: v9→v8→v5→v4→v3
                // This makes it easy to verify against frame specification documents
                // and ensures proper handling of recompression at each step.
                //=================================================================

                auto data_type = static_cast< Data::data_types_type >( GetType( ) );

                // Helper lambda to apply one conversion step
                // target_version: The frame spec version we're converting TO in this step
                auto apply_conversion_step = [&]( const CompressionModeMapping* table,
                                                   size_t table_size,
                                                   INT_2U target_version ) -> bool
                {
                    const CompressionModeMapping* mapping =
                        find_mapping( table, table_size, current_compress, data_type );

                    if ( mapping == nullptr )
                    {
                        return false;  // No mapping found, stop iteration
                    }

                    // Handle recompression if needed
                    if ( mapping->requires_recompression )
                    {
                        Uncompress( );  // This sets actual_compression_version = 9

                        // Recompress with target compression type
                        // Note: compression always happens in NATIVE byte order
                        INT_2U target_base = mapping->target_mode & 0xFF;

                        if ( target_base == 0x01 )  // GZIP
                        {
                            CompressData( GZIP, DEFAULT_GZIP_LEVEL );
                        }
                        else if ( target_base == 0x03 || target_base == 0x04 )  // DIFF_GZIP
                        {
                            CompressData( DIFF_GZIP, DEFAULT_GZIP_LEVEL );
                        }

                        // CompressData() sets m_data.compress to native endianness
                        // Use the actual compression mode (not mapping->target_mode) because:
                        // 1. Compression always happens in NATIVE byte order
                        // 2. The endianness bit (0x8000) must match the actual data
                        current_compress = static_cast< INT_2U >( m_data.compress );

                        // Data has been recompressed in current (v9) format, then will be
                        // written in target_version format. Track the actual version.
                        m_data.actual_compression_version = target_version;
                    }
                    else
                    {
                        // No recompression needed, just update the compress mode number
                        // The compress field now uses target version's numbering, so update actual_compression_version
                        current_compress = mapping->target_mode;
                        m_data.compress = static_cast< compression_scheme_type >( current_compress );
                        m_data.actual_compression_version = target_version;
                    }

                    return true;
                };

                // Step 1: v9 → v8 (if target < 9 AND data is currently in v9 format)
                if ( FrameVersion < 9 && m_data.actual_compression_version >= 9 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v9, Data::v8 >::table,
                            MappingTableSelector< Data::v9, Data::v8 >::size,
                            8 ) )  // Target version is 8
                    {
                        return current_compress;
                    }
                }

                // Step 2: v8 → v5 (if target < 8 AND data is currently in v8 or higher format)
                if ( FrameVersion < 8 && m_data.actual_compression_version >= 8 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v8, Data::v5 >::table,
                            MappingTableSelector< Data::v8, Data::v5 >::size,
                            5 ) )  // Target version is 5
                    {
                        return current_compress;
                    }
                }

                // Step 3: v5 → v4 (if target < 5 AND data is currently in v5 or higher format)
                if ( FrameVersion < 5 && m_data.actual_compression_version >= 5 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v5, Data::v4 >::table,
                            MappingTableSelector< Data::v5, Data::v4 >::size,
                            4 ) )  // Target version is 4
                    {
                        return current_compress;
                    }
                }

                // Step 4: v4 → v3 (if target < 4 AND data is currently in v4 or higher format)
                if ( FrameVersion < 4 && m_data.actual_compression_version >= 4 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v4, Data::v3 >::table,
                            MappingTableSelector< Data::v4, Data::v3 >::size,
                            3 ) )  // Target version is 3
                    {
                        return current_compress;
                    }
                }

                retval = current_compress;
                return retval;
            } // namespace FrVectImpl

            template <>
            void
            ClassicIO< Universal::FrVect >::compressPromote(
                Common::FrameSpec::version_type FrameVersion )
            {
                using namespace CompressionMapping;

                INT_2U current_compress( m_data.compress );

                // Check if source version is current version (no conversion needed)
                // NOTE: When v10 is added, change this constant to 10
                if ( FrameVersion >= 9 )
                {
                    return;
                }

                //=================================================================
                // ITERATIVE ADJACENT-VERSION CONVERSION
                //=================================================================
                // Convert step-by-step through adjacent versions: v3→v4→v5→v8→v9
                // This makes it easy to verify against frame specification documents.
                //=================================================================

                auto data_type = static_cast< Data::data_types_type >( GetType( ) );

                // Helper lambda to apply one conversion step
                // target_version: The frame spec version we're converting TO in this step
                auto apply_conversion_step = [&]( const CompressionModeMapping* table,
                                                   size_t table_size,
                                                   INT_2U target_version ) -> bool
                {
                    const CompressionModeMapping* mapping =
                        find_mapping( table, table_size, current_compress, data_type );

                    if ( mapping == nullptr )
                    {
                        return false;  // No mapping found, stop iteration
                    }

                    // Update compression mode (no recompression needed for upconversion)
                    current_compress = mapping->target_mode;
                    m_data.compress = static_cast< compression_scheme_type >( current_compress );

                    // Update actual_compression_version to track which version's format the data is now in
                    m_data.actual_compression_version = target_version;

                    return true;
                };

                // Step 1: v3 → v4 (if source < 4 AND data is currently in v3 or earlier format)
                if ( FrameVersion < 4 && m_data.actual_compression_version < 4 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v3, Data::v4 >::table,
                            MappingTableSelector< Data::v3, Data::v4 >::size,
                            4 ) )  // Target version is 4
                    {
                        return;
                    }
                }

                // Step 2: v4 → v5 (if source < 5 AND data is currently in v4 or earlier format)
                if ( FrameVersion < 5 && m_data.actual_compression_version < 5 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v4, Data::v5 >::table,
                            MappingTableSelector< Data::v4, Data::v5 >::size,
                            5 ) )  // Target version is 5
                    {
                        return;
                    }
                }

                // Step 3: v5 → v8 (if source < 8 AND data is currently in v5 or earlier format)
                if ( FrameVersion < 8 && m_data.actual_compression_version < 8 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v5, Data::v8 >::table,
                            MappingTableSelector< Data::v5, Data::v8 >::size,
                            8 ) )  // Target version is 8
                    {
                        return;
                    }
                }

                // Step 4: v8 → v9 (if source < 9 AND data is currently in v8 or earlier format)
                if ( FrameVersion < 9 && m_data.actual_compression_version < 9 )
                {
                    if ( !apply_conversion_step(
                            MappingTableSelector< Data::v8, Data::v9 >::table,
                            MappingTableSelector< Data::v8, Data::v9 >::size,
                            9 ) )  // Target version is 9
                    {
                        return;
                    }
                }
            }

            template <>
            ClassicIO< Universal::FrVect >::self_type*
            ClassicIO< Universal::FrVect >::pCreate(
                istream_type& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );
                //---------------------------------------------------------------------
                // Read information from the stream
                //---------------------------------------------------------------------
                DimBuffer dims;

                auto* obj( new Universal::FrVect( ) );

                io_name_type   io_name = "";
                io_unit_y_type io_unit_y = "";

                Stream //
                    >> io_name //
                    >> obj->m_data.compress //
                    >> obj->m_data.type //
                    ;

                if ( frame_stream_version >= 5 )
                {
                    v5::nData_type  nData;
                    v5::nBytes_type nBytes;
                    Stream //
                        >> nData //
                        >> nBytes;
                    obj->m_data.nData = nData;
                    obj->m_data.nBytes = nBytes;
                }
                else if ( frame_stream_version >= 3 )
                {
                    v3::nData_type  nData;
                    v3::nBytes_type nBytes;

                    Stream //
                        >> nData //
                        >> nBytes;

                    obj->m_data.nData = nData;
                    obj->m_data.nBytes = nBytes;
                }
                obj->m_data.data.reset( new CHAR_U[ obj->m_data.nBytes ] );
                Stream.read( reinterpret_cast< Common::IStream::char_type* >(
                                 obj->m_data.data.get( ) ),
                             obj->m_data.nBytes );
                DimensionImpl::Read< istream_type >( Stream,
                                                     obj->m_data.dimension );
                Stream >> io_unit_y;

                // Set actual_compression_version to the version we just read from
                // This tells the decompression logic which version's compression scheme to use
                obj->m_data.actual_compression_version = frame_stream_version;

                obj->compressPromote( frame_stream_version );

                //---------------------------------------------------
                // dataValid information
                //---------------------------------------------------
                if ( frame_stream_version >= 9 )
                {
                    Stream //
                        >> obj->m_data.nDataValid //
                        >> obj->m_data.dataValidCompScheme //
                        >> obj->m_data.nDataValidCompBytes //
                        ;
                    obj->m_data.dataValid.reset(
                        new CHAR_U[ obj->m_data.nDataValidCompBytes ] );
                    Stream.read(
                        reinterpret_cast< Common::IStream::char_type* >(
                            obj->m_data.dataValid.get( ) ),
                        obj->m_data.nDataValidCompBytes );
                }
                //---------------------------------------------------
                Stream.Next( obj );
                //---------------------------------------------------------------------
                // Reorder dimension data
                //---------------------------------------------------------------------
                // dims.copy( obj->m_data.dimension );
                obj->m_data.name = io_name;
                obj->m_data.unitY = io_unit_y;

                return ( obj );
            }

            template <>
            void
            ClassicIO< Universal::FrVect >::pWrite( ostream_type& Stream ) const
            {
                auto frame_stream_version = StreamVersion( Stream );

                //---------------------------------------------------------------------
                // Get converted data (will use cache from pBytes call)
                //---------------------------------------------------------------------
                const auto& converted = getConvertedData( frame_stream_version );

                //---------------------------------------------------------------------
                // Write data to the stream using cached conversion result
                //---------------------------------------------------------------------
                Stream << io_name_type( m_data.name )
                       << io_compress_type( converted.compression_mode )
                       << m_data.type;

                if ( frame_stream_version >= 5 )
                {
                    Stream << m_data.nData << converted.converted_nbytes;
                }
                else
                {
                    Stream << v3::nData_type( m_data.nData )
                           << v3::nBytes_type( converted.converted_nbytes );
                }

                Stream.write(
                    reinterpret_cast< const Common::OStream::char_type* >(
                        converted.converted_data.get( ) ),
                    converted.converted_nbytes );

                DimensionImpl::Write< ostream_type >( Stream, m_data.dimension );
                Stream << io_unit_y_type( m_data.unitY );

                if ( frame_stream_version >= 9 )
                {
                    //-----------------------------------------------
                    // dataValid information
                    //-----------------------------------------------
                    Stream << m_data.nDataValid
                           << io_compress_type( m_data.dataValidCompScheme )
                           << m_data.nDataValidCompBytes;
                    Stream.write(
                        reinterpret_cast< const Common::OStream::char_type* >(
                            m_data.dataValid.get( ) ),
                        m_data.nDataValidCompBytes );
                }

                //---------------------------------------------------
                WriteNext( Stream );
            }

        } // namespace FrVectImpl
    } // namespace Universal
} // namespace FrameCPP

//=======================================================================
// CLASS - FrVect
//=======================================================================
FrVect::FrVect( )
{
    MEM_ALLOCATE( );
}

FrVect::FrVect( const FrVect& Source )
{
    MEM_ALLOCATE( );

    m_data.copy_core( Source.m_data );
    data_copy( Source.GetDataRaw( ).get( ), Source.GetNBytes( ) );
}

/** \cond ignore_no_uniquely */
FrVect::FrVect( const std::string&    name,
                type_type             type,
                nDim_type             nDim,
                const dimension_type* dims,
                const byte_order_type byte_order,
                const void*           data,
                const std::string&    unitY )
{
    MEM_ALLOCATE( );
    m_data.set( name, type, byte_order, nDim, dims, unitY );
    data_copy( reinterpret_cast< data_const_pointer_type >( data ),
               m_data.nBytes );
}
/** \endcond */

#if 0
FrVect::
FrVect( const std::string& name,
	INT_2U type,
	nDim_type nDim,
	const dimension_type* dims,
	const byte_order_type byte_order,
	void* data,
	const std::string& unitY )
{
  MEM_ALLOCATE( );
  m_data.set( name, type, byte_order, nDim, dims, unitY );
  data_copy( static_cast< data_const_pointer_type >( data ), m_data.nBytes );
}
#endif /* 0 */

#if NEEDED
FrVect::FrVect( const Previous::FrVect& Source, stream_base_type* Stream )
{
    bool data_is_unset = true;
    m_data.name = Source.GetName( );
    switch ( Source.GetCompress( ) )
    {
    case Previous::FrVect::BIGENDIAN_RAW:
        m_data.compress = BIGENDIAN_RAW;
        break;
    case Previous::FrVect::LITTLEENDIAN_RAW:
        m_data.compress = LITTLEENDIAN_RAW;
        break;
    case Previous::FrVect::BIGENDIAN_GZIP:
        m_data.compress = BIGENDIAN_GZIP;
        break;
    case Previous::FrVect::LITTLEENDIAN_GZIP:
        m_data.compress = LITTLEENDIAN_GZIP;
        break;
    case Previous::FrVect::BIGENDIAN_DIFF_GZIP:
        m_data.compress = BIGENDIAN_DIFF_GZIP;
        break;
    case Previous::FrVect::LITTLEENDIAN_DIFF_GZIP:
        m_data.compress = LITTLEENDIAN_DIFF_GZIP;
        break;
    case Previous::FrVect::BIGENDIAN_ZERO_SUPPRESS_WORD_2:
    case Previous::FrVect::BIGENDIAN_ZERO_SUPPRESS_WORD_4:
    case Previous::FrVect::BIGENDIAN_ZERO_SUPPRESS_WORD_8:
        m_data.compress = BIGENDIAN_ZERO_SUPPRESS;
        break;
    case Previous::FrVect::LITTLEENDIAN_ZERO_SUPPRESS_WORD_2:
    case Previous::FrVect::LITTLEENDIAN_ZERO_SUPPRESS_WORD_4:
    case Previous::FrVect::LITTLEENDIAN_ZERO_SUPPRESS_WORD_8:
        m_data.compress = LITTLEENDIAN_ZERO_SUPPRESS;
        ;
        break;
    default:
        //---------------------------------------------------------------
        // This code should not be needed, but it is better to have
        //   a back up plan.
        // If the FrVect does not have a compression mode that is
        //   not handled above, simply store the data decompressed.
        //---------------------------------------------------------------
        m_data.compress = RAW;
        m_data.nBytes = Source.GetNData( ) * Source.GetTypeSize( );
        Source.GetDataUncompressed( m_data.data );
        data_is_unset = false;
    }
    m_data.type = Source.GetType( );
    m_data.nData = Source.GetNData( );
    if ( data_is_unset )
    {
        m_data.nBytes = Source.GetNBytes( );
        m_data.data = Source.GetDataRaw( );
        data_is_unset = false;
    }
    for ( nDim_type cur = 0, last = Source.GetNDim( ); cur != last; ++cur )
    {
        m_data.dimension.push_back( dimension_type( Source.GetDim( cur ) ) );
    }
    m_data.unitY = Source.GetUnitY( );
}
#endif /* NEEDED  */

/** \cond ignore_no_uniquely */
FrVect::FrVect( const name_type&        name,
                compression_scheme_type Compress,
                type_type               type,
                nDim_type               nDim,
                const dimension_type*   dims,
                nData_type              NData,
                nBytes_type             NBytes,
                data_type               Data,
                const unitY_type&       unitY )
{
    m_data.set( name, type, BYTE_ORDER_HOST, nDim, dims, unitY );
    m_data.compress = Compress;
    m_data.nData = NData;
    m_data.nBytes = NBytes;
    m_data.data = Data;
}
/** \endcond */

#if 0
FrVect::
FrVect( const std::string& name,
	compress_type Compress,
	type_type type,
	nDim_type nDim,
	const dimension_type* dims,
	nData_type NData,
	nBytes_type NBytes,
	data_type Data,
	const std::string& unitY )
{
  m_data.set( name, type, BYTE_ORDER_HOST, nDim, dims, unitY );
  m_data.compress = Compress;
  m_data.nData = NData;
  m_data.data = Data;
}
#endif /* 0 */

FrVect::FrVect( const name_type&      Name,
                type_type             Type,
                nDim_type             NDim,
                const dimension_type* Dims,
                const unitY_type&     UnitY )
{
    m_data.set( Name, Type, BYTE_ORDER_HOST, NDim, Dims, UnitY );
    SetNData( dimension_type::CalcNData( NDim, Dims ) );
    nBytes_type nbytes = GetTypeSize( ) * GetNData( );
    m_data.data.reset( new CHAR_U[ nbytes ] );
}

FrVect::~FrVect( )
{
    MEM_DELETE( );
}

bool
FrVect::operator!=( const FrVect& RHS ) const
{
    return !( *this == RHS );
}

FrVect&
FrVect::operator+=( const FrVect& RHS )
{
    //---------------------------------------------------------------------
    // Perform sanity checks
    //---------------------------------------------------------------------
    if ( ( this->GetName( ) != RHS.GetName( ) ) ||
         ( this->GetType( ) != RHS.GetType( ) ) ||
         ( this->GetNDim( ) != RHS.GetNDim( ) ) ||
         ( this->GetUnitY( ) != RHS.GetUnitY( ) ) )
    {
        std::ostringstream msg;
        bool               comma;

        msg << "Unable to concat the FrVect structures because: ";
        comma = false;
        if ( this->GetName( ) != RHS.GetName( ) )
        {
            msg << "name of objects differ (" << this->GetName( ) << " vs. "
                << RHS.GetName( ) << ")";
            comma = true;
        }
        if ( this->GetType( ) != RHS.GetType( ) )
        {
            if ( comma )
            {
                msg << ", ";
            }
            msg << "type of objects differ (" << this->GetType( ) << " vs. "
                << RHS.GetType( ) << ")";
            comma = true;
        }
        if ( this->GetNDim( ) != RHS.GetNDim( ) )
        {
            if ( comma )
            {
                msg << ", ";
            }
            msg << "number of dimensions differ (" << this->GetNDim( )
                << " vs. " << RHS.GetNDim( ) << ")";
            comma = true;
        }
        if ( this->GetUnitY( ) != RHS.GetUnitY( ) )
        {
            if ( comma )
            {
                msg << ", ";
            }
            msg << "y units differ (" << this->GetUnitY( ) << " vs. "
                << RHS.GetUnitY( ) << ")";
            comma = true;
        }
        throw std::domain_error( msg.str( ) );
    }
    for ( nDim_type cur = 0, last = this->GetNDim( ); cur != last; ++cur )
    {
        const dimension_type& ld( this->GetDim( cur ) );
        const dimension_type& rd( RHS.GetDim( cur ) );

        if ( cur > 0 )
        {
            if ( ld != rd )
            {
                std::ostringstream msg;

                msg << "Unable to concat the FrVect structures";
                /// \todo
                ///   Need to have a list of reasons
                throw std::domain_error( msg.str( ) );
            }
        }
        else
        {
            if ( ( ld.GetNx( ) != rd.GetNx( ) ) ||
                 ( ld.GetStartX( ) != rd.GetStartX( ) ) ||
                 ( ld.GetUnitX( ) != rd.GetUnitX( ) ) )
            {
                std::ostringstream msg;

                msg << "Unable to concat the FrVect structures";
                /// \todo
                ///   Need to have a list of reasons
                throw std::domain_error( msg.str( ) );
            }
        }
    }
    //---------------------------------------------------------------------
    // Calculate how much space is required
    //---------------------------------------------------------------------
    const INT_2U type_size( GetTypeSize( ) );
    this->GetDim( 0 ).SetNx( this->GetDim( 0 ).GetNx( ) +
                             RHS.GetDim( 0 ).GetNx( ) );
    const INT_8U s = GetNData( ) + RHS.GetNData( );

    //---------------------------------------------------------------------
    // Allocate the space, fill it with the existing data and then store
    //   it.
    //---------------------------------------------------------------------
    data_pointer_type rhs_data( RHS.GetDataRaw( ).get( ) );
    data_type         rhs_data_expanded;
    nBytes_type       rhs_data_nbytes;

    expandToBuffer( rhs_data_expanded, rhs_data_nbytes );
    if ( rhs_data_nbytes )
    {
        rhs_data = rhs_data_expanded.get( );
    }

    data_type               data( new CHAR_U[ s ] );
    data_const_pointer_type source( GetDataUncompressed( ).get( ) );

    std::copy( source, source + ( GetNData( ) * type_size ), data.get( ) );
    std::copy( rhs_data,
               rhs_data + ( RHS.GetNData( ) * type_size ),
               data.get( ) + ( GetNData( ) * type_size ) );
    m_data.data = data;
    //---------------------------------------------------------------------
    // Return
    //---------------------------------------------------------------------
    return *this;
}

const FrVect::name_type&
FrVect::GetName( ) const
{
    return ( Data::GetName( ) );
}

FrVect&
FrVect::Merge( const FrVect& RHS )
{
    /// \todo
    ///   Need to implement Merge routine
    std::string msg( "Merge currently not implemented for " );
    msg += StructName( );

    throw std::domain_error( msg );
    return *this;
}

FrVect::subfrvect_type
FrVect::SubFrVect( INT_4U Start, INT_4U Stop ) const
{
    //---------------------------------------------------------------------
    // Create new FrVect
    //---------------------------------------------------------------------
    subfrvect_type retval( new FrVect );
    //---------------------------------------------------------------------
    // Calculate the block size
    //---------------------------------------------------------------------
    INT_4U          block_size = GetTypeSize( );
    const nDim_type ndim = GetNDim( );

    for ( nDim_type x = 1; x < ndim; ++x )
    {
        block_size *= GetDim( x ).GetNx( );
    }
    //---------------------------------------------------------------------
    // Copy the core of the information
    //---------------------------------------------------------------------
    retval->m_data.copy_core( this->m_data );
    //---------------------------------------------------------------------
    // Set the Dimension information for the sink
    //---------------------------------------------------------------------
    retval->GetDim( 0 ).SetNx( Stop - Start );
    retval->SetNData( Stop - Start );
    //---------------------------------------------------------------------
    // Allocate space in the sink
    //---------------------------------------------------------------------
    retval->data_alloc( block_size * ( Stop - Start ) );
    retval->m_data.compress = compression_scheme_type::RAW;
    //---------------------------------------------------------------------
    // Copy the data
    //---------------------------------------------------------------------
    data_pointer_type data_source( GetDataRaw( ).get( ) );
    nBytes_type       nBytes( 0 );
    data_type         expanded_buffer;

    expandToBuffer( expanded_buffer, nBytes );
    if ( nBytes )
    {
        data_source = expanded_buffer.get( );
    }
    data_pointer_type data_sink( retval->GetDataRaw( ).get( ) );

    for ( nBytes_type x = Start; x < Stop; ++x )
    {
        //-------------------------------------------------------------------
        // Copy 1 block's worth of data
        //-------------------------------------------------------------------
        std::copy( data_source + ( x * block_size ),
                   data_source + ( ( x + 1 ) * block_size ),
                   data_sink + ( ( x - Start ) * block_size ) );
    }
    //---------------------------------------------------------------------
    // Return the newly constructed FrVect
    //---------------------------------------------------------------------
#if __clang__ &&                                                               \
    ( ( __clang_major__ > 7 ) ||                                               \
      ( ( __clang_major__ == 7 ) && ( __clang_minor__ >= 3 ) ) )
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpessimizing-move"
#endif /* __clang__ */
    return MOVE_RET( retval );
#if __clang__ &&                                                               \
    ( ( __clang_major__ > 7 ) ||                                               \
      ( ( __clang_major__ == 7 ) && ( __clang_minor__ >= 3 ) ) )
#pragma clang diagnostic pop
#endif /* __clang__ */
}

//=======================================================================
// Local functions
//=======================================================================
#if !defined( __SUNPRO_CC ) || ( __SUNPRO_CC > 0x550 )
#include "framecpp/Universal/FrVect.tcc"

#define INSTANTIATE( LM_TYPE )                                                 \
    template FrVect::FrVect( const std::string&    name,                       \
                             nDim_type             nDim,                       \
                             const dimension_type* dims,                       \
                             const LM_TYPE*        data,                       \
                             const std::string&    unitY );                       \
                                                                               \
    template FrVect::FrVect( const std::string&    name,                       \
                             nDim_type             nDim,                       \
                             const dimension_type* dims,                       \
                             LM_TYPE*              data,                       \
                             const std::string&    unitY )

#include "TypeInstantiation.tcc"

#undef INSTANTIATE

#endif /* */
