#if !defined HAVE_COPY_H__
#define      HAVE_COPY_H__


#include "fxttypes.h"
#include "complextype.h"
#include <cstring>  // memcpy(), memset()

namespace fxtaux
{


template <typename Type>
inline void memzero(Type *dst, ulong n)
// Set array (memory) to zero (bytes).
{
    memset(dst, 0, n*sizeof(Type));
}
// -------------------------


template <typename Type>
inline void fill(Type *dst, ulong n, Type v)
// Fill array with value v
{
    if ( n&1 ) { *dst++ = v; }
    n >>= 1;
    if ( n&1 ) { *dst++ = v;  *dst++ = v; }
    n >>= 1;
    while ( n-- )
    {
        dst[0] = v;
        dst[1] = v;
        dst[2] = v;
        dst[3] = v;
        dst += 4;
    }
}
// -------------------------


template <typename Type>
inline void null(Type *dst, ulong n)
// Set array to zero.
// If the element zero consists of zero bytes only,
// then prefer memzero().
{
    const Type z(0);
    fill(dst, n, z);
}
// -------------------------

// Types where zero is binary zero:
inline void null(Complex *dst, ulong n)  { memzero(dst,n); }
inline void null(double *dst, ulong n)  { memzero(dst,n); }
inline void null(float *dst, ulong n)  { memzero(dst,n); }
inline void null(ulong *dst, ulong n)  { memzero(dst,n); }
inline void null(long *dst, ulong n)  { memzero(dst,n); }
inline void null(int *dst, ulong n)  { memzero(dst,n); }
inline void null(uint *dst, ulong n)  { memzero(dst,n); }
inline void null(char *dst, ulong n)  { memzero(dst,n); }


template <typename Type>
inline void memcopy(const Type *src, Type *dst, ulong n)
// Copy array (memory) src[] to dst[]
{
    memcpy(dst, src, n*sizeof(Type));
}
// -------------------------

template <typename Type>
inline void copy(const Type *src, Type *dst, ulong n)
// Copy array src[] to dst[]
{
    if ( n&1 ) { *dst++ = *src++; }
    n >>= 1;
    if ( n&1 ) { *dst++ = *src++;  *dst++ = *src++; }
    n >>= 1;
    while ( n-- )
    {
        dst[0] = src[0];
        dst[1] = src[1];
        dst[2] = src[2];
        dst[3] = src[3];
        dst += 4;
        src += 4;
    }
}
// -------------------------

// Types where copy is memory copy:
inline void copy(const Complex *src, Complex *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const double *src, double *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const float *src, float *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const ulong *src, ulong *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const long *src, long *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const int *src, int *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const uint *src, uint *dst, ulong n) { memcopy(src, dst, n); }
inline void copy(const char *src, char *dst, ulong n) { memcopy(src, dst, n); }


template <typename Type1, typename Type2>
inline void copy_cast(const Type1 *src, Type2 *dst, ulong n)
// Copy array src[] to dst[]
{
    for (ulong k=0; k<n; ++k)  dst[k] = (Type2)src[k];
}
// -------------------------

template <typename Type1, typename Type2>
inline void copy0(const Type1 *src, ulong ns, Type2 *dst, ulong nd)
// Copy as much as makes sense, fill rest with zeros
// from src[] (length ns) to dst[] (length nd):
{
    ulong k = 0;
    ulong n = ( nd<ns ? nd : ns ); // == min(nd, ns);
//    for (k=0; k<n; ++k)  dst[k] = (Type2)src[k];
//    for (  ; k<nd; ++k)  dst[k] = (Type2)0;
    copy_cast(src, dst, n);
    if ( k<nd )  null(dst+n, nd-n);
}
// -------------------------


template <typename Type>
inline void copy_cyclic(const Type *src, Type *dst, ulong n, ulong s)
// Copy array src[] to dst[]
// starting from position s in src[]
// wrap around end of src[]  (src[n-1])
//
// src[] is assumed to be of length n
// dst[] must be length n at least
//
// Equivalent to:  { copy(src, dst, n); rotate_right(dst, n, s)}
{
    ulong k = 0;
    while ( s<n )  dst[k++] = src[s++];

    s = 0;
    while ( k<n )  dst[k++] = src[s++];
}
// -------------------------


template <typename Type>
inline void copy_reverse(const Type *src, Type *dst, ulong n)
// Copy array reversed:
// dst[] <-- reverse(f[])
{
    ulong j = 0;
    while ( n-- )
    {
        dst[j] = src[n];
        ++j;
    }
}
// -------------------------

template <typename Type>
inline void copy_reverse_0(const Type *src, Type *dst, ulong n)
// Copy array reversed around index 0:
// dst[] <-- reverse_0(f[])
{
    dst[0] = src[0];
    copy_reverse(src+1, dst+1, n-1);
}
// -------------------------

//template <typename Type1, typename Type2>
//inline void offset_copy(const Type1 *src, ulong ns,
//            Type2 *dst, ulong nd,
//            ulong off)
//// Copy with offset
//// from src[] (length ns) to dst[] (length nd):
//{
//    if ( off>=ns )  return;  // no elements to copy from
//    if ( off>=nd )  return;  // no elements to copy to
//
//    ulong len = min(ns-off, nd-off);
//    copy(src+off, dst+off, len);
//}
//// -------------------------


template <typename Type>
inline void skip_copy(const Type *src, Type *dst, ulong n, ulong d)
// Copy n elements from src[] at positions
//   [0],[d],[2d],[3d],...,[(n-1)*d]
//   to dst[0, 1, ..., n-1]
{
    for (ulong k=0,j=0; j<n; k+=d,j++)  dst[j] = src[k];
}
// -------------------------


template <typename Type>
inline void skip_copy_back(const Type *src, Type *dst, ulong n, ulong d)
// Copy n elements from src[0, 1, ..., n-1]
//   to dst[] at positions
//   [0],[d],[2d],[3d],...,[(n-1)*d]
{
    for (ulong k=0,j=0; j<n; k+=d,j++)  dst[k] = src[j];
}
// -------------------------


template <typename Type>
inline void swap(Type *f, Type *g, ulong n)
// Swap arrays
{
    while ( n-- )
    {
        Type t(f[n]);  f[n]=g[n];  g[n]=t;
    }
}
// -------------------------


template <typename Type>
inline void swap_reverse(Type *f, Type *g, ulong n)
// Swap arrays reversed:
// f[] <-- reverse(g[])
// g[] <-- reverse(f[])
{
    ulong j = 0;
    while ( n-- )
    {
        Type t(f[j]);  f[j]=g[n];  g[n]=t;
        ++j;
    }
}
// -------------------------


template <typename Type>
inline void set_seq(Type *dst, ulong n, Type start=0, Type step=1)
// Fill array with sequence
//   start, start+step, start+2*step, ...
{
    for (ulong k=0; k<n; ++k)
    {
        dst[k] = start;
        start += step;
    }
}
// -------------------------


template <typename Type>
inline bool is_seq(const Type *dst, ulong n, Type start=0, Type step=1)
// Check whether array elemnts are the sequence
//   start, start+step, start+2*step, ...
// (as filled in by set_seq(dst, n, start, step)
{
    for (ulong k=0; k<n; ++k)
    {
        if ( dst[k] != start )  return false;
        start += step;
    }

    return true;
}
// -------------------------


template <typename Type1, typename Type2>
inline long compare(const Type1 *f, const Type2 *g, ulong n)
// Compare sequences, return 0 if both are identical.
// If a difference is detected, the return +-(k+1)
//  where k is the index so that  f[k]!=g[k]
//  and the sign is positive if f[k]>g[k], else negative.
{
    ulong k;
    for (k=0; k<n; ++k)
    {
        if ( f[k] != g[k] )  break;
    }

    if ( k==n )  return 0;
    else
    {
        ulong j = k+1;
        return ( f[k]>g[k] ? j : -j );
    }
}
// -------------------------



} // end namespace fxtaux


using namespace fxtaux;


#endif // !defined HAVE_COPY_H__
