//
//  floatx8.h
//  HA_misc
//
//  Created by hark on 2013/07/15.
//  Copyright (c) 2013年 hark. All rights reserved.
//

#ifndef HA_misc_floatx8_h
#define HA_misc_floatx8_h

#include <algorithm>
#include "HA_Object.h"
#include "HA_log.h"

#ifdef IS_APPLE_IOS
#if (!(TARGET_IPHONE_SIMULATOR))
#include <arm_neon.h>
#define ENABLE_NEON
#endif
#endif

#define FLOATx16_MODE

#ifdef  FLOATx16_MODE
/* 16chバッファモード（HARK-CPU版） */

namespace HARKAndroid {

class floatx8 {
private:

    float dat[16] __attribute__((aligned(32)));

public:
    
    float *getDatAddr(const unsigned int channel) __attribute__((always_inline)) {
        return(&dat[channel]);
    }
    
    floatx8() {

        std::fill(&dat[0], &dat[NELEM(dat)], 0);

    };

    void convert(const unsigned char * const in, const int channels, const int nbits) __attribute__((always_inline)) {
        
            const unsigned char *ptr = in;
            const int to = std::min(static_cast<int>(NELEM(dat)), channels);
            switch(nbits) {
                case 8:
                    for(int i = 0; i < to; i++, ptr++) {
                        dat[i] = (static_cast<int>(ptr[0]) << 24) >> 24;
                    }
                    break;
                case 16:
                    for(int i = 0; i < to; i++, ptr += 2) {
                        dat[i] = (static_cast<int>(ptr[0]) << 16 | (static_cast<int>(ptr[1]) << 24)) >> 16;
                    }
                    break;
                case 24:
                    for(int i = 0; i < to; i++, ptr += 3) {
                        dat[i] = (static_cast<int>(ptr[0])<< 8 | (static_cast<int>(ptr[1]) << 16) | (static_cast<int>(ptr[2]) << 24)) >> 8;
                    }
                    break;
                case 32:
                    for(int i = 0; i < to; i++, ptr += 4) {
                        dat[i] = *reinterpret_cast<const int *>(ptr);
                    }
                    break;
                default:
                    std::fill(&dat[0], &dat[NELEM(dat)], 0);
                    break;
            }
            for(int i = to; i < NELEM(dat); i++) {
                dat[i] = 0;
            }

    }
    
    void convert(const int * const in, const int channels) __attribute__((always_inline)) {

            const int to = std::min(static_cast<int>(NELEM(dat)), channels);
            for (int i = 0; i < to; i++) {
                dat[i] = *(in + i);
            }
            for(int i = to; i < NELEM(dat); i++) {
                dat[i] = 0;
            }
    }

    void get(float *dest, const int from, const int num) const __attribute__((always_inline)) {
        if ((0 <= from) && (from < 16)) {
            const int limit = std::min(16, from + num);
            std::copy(&dat[from], &dat[limit], dest);
        }
    }
    
    void set(const float * const src, const int from, const int num) __attribute__((always_inline)) {
        
            if ((0 <= from) && (from < 16)) {
                const int limit = std::min(16 - from, num);
                std::copy(&src[0], &src[limit], &dat[from]);
            }
    }
    
    //~floatx8();
    
};

    
} // end of namespace




#else /*********************************************************************/
/* 8chバッファモード（従来版） */


namespace HARKAndroid {

class floatx8 {
private:

    float dat[8] __attribute__((aligned(32)));

public:
    
    float *getDatAddr(const unsigned int channel) __attribute__((always_inline)) {
        return(&dat[channel]);
    }
    
    floatx8() {

#ifdef ENABLE_NEON
        
        __asm__ (
                 "push          {r0}                            \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                 "vpush         {q0}                            \n\t"   // 使用しているものは全部退避
                 "                                              \n\t"   //
                 "mov           r0,     %[dat]                  \n\t"   //
                 "veor          q0,     q0,     q0              \n\t"   // 0 くりあ
                 "vstmia        r0!,    {q0}                    \n\t"   // dat[0] - dat[3]
                 "vstmia        r0!,    {q0}                    \n\t"   // dat[4] - dat[7]
                 "                                              \n\t"   //
                 "vpop          {q0}                            \n\t"   // pop
                 "pop           {r0}                            \n\t"   // pop

                 :
                 : [dat] "r" (&dat[0])
                 : "r0", "q0"
                 );
        
#else
        std::fill(&dat[0], &dat[NELEM(dat)], 0);
#endif

    };

    void convert(const unsigned char * const in, const int channels, const int nbits) __attribute__((always_inline)) {
        
#ifdef ENABLE_NEON
        
        if ((!(reinterpret_cast<unsigned long>(in) & 3)) && (channels == 8) && (nbits == 24)) {
            
            const uint8x8_t btbl[] __attribute__((aligned(32))) = {
                {32,  0,  1,  2, 32,  3,  4,  5},       // d0
                {32,  6,  7,  8, 32,  9, 10, 11},       // d1
                {32, 12, 13, 14, 32, 15, 16, 17},       // d2
                {32, 18, 19, 20, 32, 21, 22, 23}        // d3
            };
            
            __asm__ (
                     "push          {r0-r1}                     \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush         {q0-q5}                     \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov           r0,     %[btbl]             \n\t"   //
                     "vldmia        r0,     {d0-d3}             \n\t"   // 変換テーブルをロード
                     "mov           r1,     %[src]              \n\t"   //
                     "vldmia        r1,     {d4-d6}             \n\t"   // 変換データをロード
                     "                                          \n\t"   //
                     "vtbl.8        d8,     {d4-d6},    d0      \n\t"   // 変換その１
                     "vtbl.8        d9,     {d4-d6},    d1      \n\t"   // 変換その２
                     "vtbl.8        d10,    {d4-d6},    d2      \n\t"   // 変換その３
                     "vtbl.8        d11,    {d4-d6},    d3      \n\t"   // 変換その４
                     "vshr.s32      q4,     q4,         #8      \n\t"   // まとめて右シフトその１
                     "vshr.s32      q5,     q5,         #8      \n\t"   // まとめて右シフトその２
                     "vcvt.f32.s32  q4,     q4                  \n\t"   // まとめてキャストその１
                     "vcvt.f32.s32  q5,     q5                  \n\t"   // まとめてキャストその２
                     "                                          \n\t"   //
                     "mov           r0,     %[dst]              \n\t"   //
                     "vstmia        r0,     {d8-d11}            \n\t"   // 結果をストア
                     "                                          \n\t"   //
                     "vpop          {q0-q5}                     \n\t"   // pop
                     "pop           {r0-r1}                     \n\t"   // pop
                     :
                     : [btbl] "r" (btbl), [src] "r" (in), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1", "q2", "q3", "q4", "q5"
                     );
            
        } else if ((!(reinterpret_cast<unsigned long>(in) & 3)) && (channels == 8) && (nbits == 16)) {
            
            const uint8x8_t btbl[] __attribute__((aligned(32))) = {
                {32, 32,  0,  1, 32, 32,  2,  3},       // d0
                {32, 32,  4,  5, 32, 32,  6,  7},       // d1
                {32, 32,  8,  9, 32, 32, 10, 11},       // d2
                {32, 32, 12, 13, 32, 32, 14, 15}        // d3
            };
            
            __asm__ (
                     "push          {r0-r1}                     \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush         {q0-q5}                     \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov           r0,     %[btbl]             \n\t"   //
                     "vldmia        r0,     {d0-d3}             \n\t"   // 変換テーブルをロード
                     "mov           r1,     %[src]              \n\t"   //
                     "vldmia        r1,     {d4-d5}             \n\t"   // 変換データをロード
                     "                                          \n\t"   //
                     "vtbl.8        d8,     {d4-d5},    d0      \n\t"   // 変換その１
                     "vtbl.8        d9,     {d4-d5},    d1		\n\t"	// 変換その２
                     "vtbl.8        d10,    {d4-d5},    d2		\n\t"	// 変換その３
                     "vtbl.8        d11,    {d4-d5},    d3		\n\t"	// 変換その４
                     "vshr.s32      q4,     q4,         #16		\n\t"	// まとめて右シフトその１
                     "vshr.s32      q5,     q5,         #16		\n\t"	// まとめて右シフトその２
                     "vcvt.f32.s32  q4,     q4                  \n\t"	// まとめてキャストその１
                     "vcvt.f32.s32  q5,     q5                  \n\t"	// まとめてキャストその２
                     "                                          \n\t"   //
                     "mov           r0,     %[dst]              \n\t"   //
                     "vstmia        r0,     {d8-d11}            \n\t"   // 結果をストア
                     "                                          \n\t"   //
                     "vpop          {q0-q5}                     \n\t"   // pop
                     "pop           {r0-r1}                     \n\t"   // pop
                     :
                     : [btbl] "r" (btbl), [src] "r" (in), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1", "q2", "q3", "q4", "q5"
                     );
            
        } else if ((!(reinterpret_cast<unsigned long>(in) & 3)) && (channels == 4) && (nbits == 16)) {
            
            const uint8x8_t btbl[] __attribute__((aligned(32))) = {
                {32, 32,  0,  1, 32, 32,  2,  3},       // d0
                {32, 32,  4,  5, 32, 32,  6,  7},       // d1
            };
            
            __asm__ (
                     "push          {r0-r1}                     \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush         {q0-q5}                     \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov           r0,     %[btbl]             \n\t"   //
                     "vldmia        r0,     {d0-d1}             \n\t"   // 変換テーブルをロード
                     "mov           r1,     %[src]              \n\t"   //
                     "vldmia        r1,     {d4-d5}             \n\t"   // 変換データをロード
                     "                                          \n\t"   //
                     "vtbl.8        d8,     {d4-d5},    d0      \n\t"	// 変換その１
                     "vtbl.8        d9,     {d4-d5},    d1		\n\t"	// 変換その２
                     "vshr.s32      q4,     q4,         #16		\n\t"	// まとめて右シフト
                     "vcvt.f32.s32  q4,     q4                  \n\t"	// まとめてキャスト
                     "veor          q5,     q5,         q5      \n\t"   // dat[4] - dat[7] 0 くりあ
                     "                                          \n\t"   //
                     "mov           r0,     %[dst]              \n\t"   //
                     "vstmia        r0,     {d8-d11}            \n\t"   // 結果をストア
                     "                                          \n\t"   //
                     "vpop          {q0-q5}                     \n\t"   // pop
                     "pop           {r0-r1}                     \n\t"   // pop
                     :
                     : [btbl] "r" (btbl), [src] "r" (in), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1", "q2", "q3", "q4", "q5"
                     );
            
        } else {
            
#endif

            const unsigned char *ptr = in;
            const int to = std::min(static_cast<int>(NELEM(dat)), channels);
            switch(nbits) {
                case 8:
                    for(int i = 0; i < to; i++, ptr++) {
                        dat[i] = (static_cast<int>(ptr[0]) << 24) >> 24;
                    }
                    break;
                case 16:
                    for(int i = 0; i < to; i++, ptr += 2) {
                        dat[i] = (static_cast<int>(ptr[0]) << 16 | (static_cast<int>(ptr[1]) << 24)) >> 16;
                    }
                    break;
                case 24:
                    for(int i = 0; i < to; i++, ptr += 3) {
                        dat[i] = (static_cast<int>(ptr[0])<< 8 | (static_cast<int>(ptr[1]) << 16) | (static_cast<int>(ptr[2]) << 24)) >> 8;
                    }
                    break;
                case 32:
                    for(int i = 0; i < to; i++, ptr += 4) {
                        dat[i] = *reinterpret_cast<const int *>(ptr);
                    }
                    break;
                default:
                    std::fill(&dat[0], &dat[NELEM(dat)], 0);
                    break;
            }
            for(int i = to; i < NELEM(dat); i++) {
                dat[i] = 0;
            }
#ifdef ENABLE_NEON
        }
#endif

    }
    
    void convert(const int * const in, const int channels) __attribute__((always_inline)) {

#ifdef ENABLE_NEON
        
        if ((!(reinterpret_cast<unsigned long>(in) & 3)) && (channels == 8)) {
            
            __asm__ (
                     "push          {r0-r1}                     \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush         {q0-q1}                     \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov           r0,     %[src]              \n\t"   //
                     "vldmia        r0,     {q0,q1}             \n\t"   // 変換データをロード
                     "                                          \n\t"   //
                     "vcvt.f32.s32  q0,     q0                  \n\t"	// まとめてキャストその１
                     "vcvt.f32.s32  q1,     q1                  \n\t"	// まとめてキャストその２
                     "                                          \n\t"   //
                     "mov           r1,     %[dst]              \n\t"   //
                     "vstmia        r1,     {q0,q1}             \n\t"   // 結果をストア
                     "                                          \n\t"   //
                     "vpop          {q0-q1}                     \n\t"   // pop
                     "pop           {r0-r1}                     \n\t"   // pop
                     :
                     : [src] "r" (in), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1"
                     );
            
        } else if ((!(reinterpret_cast<unsigned long>(in) & 3)) && (channels == 4)) {
            
            __asm__ (
                     "push          {r0-r1}                     \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush         {q0-q1}                     \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov           r0,     %[src]              \n\t"   //
                     "vldmia        r0,     {q0}                \n\t"   // 変換データをロード
                     "                                          \n\t"   //
                     "vcvt.f32.s32  q0,     q0                  \n\t"	// まとめてキャスト
                     "veor          q1,     q1,     q1          \n\t"   // dat[4] - dat[7] 0 くりあ
                     "                                          \n\t"   //
                     "mov           r1,     %[dst]              \n\t"   //
                     "vstmia        r1,     {q0,q1}             \n\t"   // 結果をストア
                     "                                          \n\t"   //
                     "vpop          {q0-q1}                     \n\t"   // pop
                     "pop           {r0-r1}                     \n\t"   // pop
                     :
                     : [src] "r" (in), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1"
                     );
            
        } else {
#endif
            const int to = std::min(static_cast<int>(NELEM(dat)), channels);
            for (int i = 0; i < to; i++) {
                dat[i] = *(in + i);
            }
            for(int i = to; i < NELEM(dat); i++) {
                dat[i] = 0;
            }
#ifdef ENABLE_NEON
        }
#endif
    }

    void get(float *dest, const int from, const int num) const __attribute__((always_inline)) {
        if ((0 <= from) && (from < 8)) {
            const int limit = std::min(8, from + num);
            std::copy(&dat[from], &dat[limit], dest);
        }
    }
    
    void set(const float * const src, const int from, const int num) __attribute__((always_inline)) {
        
#ifdef ENABLE_NEON

        if ((!(reinterpret_cast<unsigned long>(src) & 3)) && (from == 0) && (num == 8)) {

            __asm__ (
                     "push      {r0-r1}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush     {q0-q1}                         \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov       r0,     %[src]                  \n\t"   // ロード元
                     "mov       r1,     %[dst]                  \n\t"   // ストア先
                     "vldmia    r0,     {q0, q1}                \n\t"   // src[0] - src[7] をロード
                     "vstmia    r1,     {q0, q1}                \n\t"   // dst[0] - dst[7] をストア
                     "                                          \n\t"   //
                     "vpop      {q0-q1}                         \n\t"   // pop
                     "pop       {r0-r1}                         \n\t"   // pop
                     :
                     : [src] "r" (src), [dst] "r" (&dat[0])
                     : "r0", "r1", "q0", "q1"
                     );
            
        } else if ((!(reinterpret_cast<unsigned long>(src) & 3)) && ((from == 0) || (from == 4)) && (num == 4)) {
            
            __asm__ (
                     "push      {r0-r1}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush     {q0}                            \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov       r0,     %[src]                  \n\t"   // ロード元
                     "mov       r1,     %[dst]                  \n\t"   // ストア先
                     "vldmia    r0,     {q0}                    \n\t"   // src[0] - src[3] をロード
                     "vstmia    r1,     {q0}                    \n\t"   // dst[0] - dst[3] をストア
                     "                                          \n\t"   //
                     "vpop      {q0}                            \n\t"   // pop
                     "pop       {r0-r1}                         \n\t"   // pop
                     :
                     : [src] "r" (src), [dst] "r" (&dat[from])
                     : "r0", "r1", "q0"
                     );
            
        } else {
#endif
            if ((0 <= from) && (from < 8)) {
                const int limit = std::min(8 - from, num);
                std::copy(&src[0], &src[limit], &dat[from]);
            }
#ifdef ENABLE_NEON
        }
#endif
    }
    
#ifdef ENABLE_NEON
    
	floatx8& operator = (const floatx8& rhs) {
        __asm__ (
                 "push      {r0-r1}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                 "vpush     {q0-q1}                         \n\t"   // 使用しているものは全部退避
                 "                                          \n\t"   //
                 "mov       r0,     %[src]                  \n\t"   // ロード元
                 "mov       r1,     %[dst]                  \n\t"   // ストア先
                 "vldmia    r0,     {q0, q1}                \n\t"   // src[0] - src[7] をロード
                 "vstmia    r1,     {q0, q1}                \n\t"   // dst[0] - dst[7] をストア
                 "                                          \n\t"   //
                 "vpop      {q0-q1}                         \n\t"   // pop
                 "pop       {r0-r1}                         \n\t"   // pop
                 :
                 : [src] "r" (&rhs.dat[0]), [dst] "r" (&dat[0])
                 : "r0", "r1", "q0", "q1"
                 );
        
        return(*this);
    }
    
    floatx8(const floatx8& arg) {
        
        __asm__ (
                 "push      {r0-r1}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                 "vpush     {q0-q1}                         \n\t"   // 使用しているものは全部退避
                 "                                          \n\t"   //
                 "mov       r0,     %[src]                  \n\t"   // ロード元
                 "mov       r1,     %[dst]                  \n\t"   // ストア先
                 "vldmia    r0,     {q0, q1}                \n\t"   // src[0] - src[7] をロード
                 "vstmia    r1,     {q0, q1}                \n\t"   // dst[0] - dst[7] をストア
                 "                                          \n\t"   //
                 "vpop      {q0-q1}                         \n\t"   // pop
                 "pop       {r0-r1}                         \n\t"   // pop
                 :
                 : [src] "r" (&arg.dat[0]), [dst] "r" (&dat[0])
                 : "r0", "r1", "q0", "q1"
                 );
        
    }
    
#endif
    
    //~floatx8();
    
};


#ifdef ENABLE_NEON
class deinterleave : public HAObject {

public:
    inline void operator()(float ** const dest, const float ** const src, const int destnum, const int srcnum) __attribute__((always_inline)) {
    
        if ((destnum == 8) && (srcnum == 8)) {
            
            __asm__ (
                     "push      {r0-r8}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush     {q0-q7}                         \n\t"   // 使用しているものは全部退避
                     "vpush     {q8-q15}                        \n\t"   //
                     "                                          \n\t"   //
                     "mov       r8,     %[src]                  \n\t"   // 転送元アドレス r0 - r7
                     "ldmia     r8,     {r0-r7}                 \n\t"   //
                     "                                          \n\t"   //
                     "vldmia    r0,     {q0-q1}                 \n\t"   //  d0  d1  d2  d3     0  1  2  3  4  5  6  7
                     "vldmia    r1,     {q2-q3}                 \n\t"   //  d4  d5  d6  d7     8  9 10 11 12 13 14 15
                     "vldmia    r2,     {q4-q5}                 \n\t"   //  d8  d9 d10 d11    16 17 18 19 20 21 22 23
                     "vldmia    r3,     {q6-q7}                 \n\t"   // d12 d13 d14 d15    24 25 26 27 28 29 30 31
                     "vldmia    r4,     {q8-q9}                 \n\t"   // d16 d17 d18 d19    32 33 34 35 36 37 38 39
                     "vldmia    r5,     {q10-q11}               \n\t"   // d20 d21 d22 d23    40 41 42 43 44 45 46 47
                     "vldmia    r6,     {q12-q13}               \n\t"   // d24 d25 d26 d27    48 49 50 51 52 53 54 55
                     "vldmia    r7,     {q14-q15}               \n\t"   // d28 d29 d30 d31    56 57 58 59 60 61 62 63
                     "                                          \n\t"   //
                     "vtrn.32   q0,     q2                      \n\t"   //   0  8  2 10  4 12  6 14
                     "vtrn.32   q1,     q3                      \n\t"   //   1  9  3 11  5 13  7 15
                     "vtrn.32   q4,     q6                      \n\t"   //  16 24 18 26 20 28 22 30
                     "vtrn.32   q5,     q7                      \n\t"   //  17 25 19 27 21 29 23 31
                     "vtrn.32   q8,     q10                     \n\t"   //  32 40 34 42 36 44 38 46
                     "vtrn.32   q9,     q11                     \n\t"   //  33 41 35 43 37 45 39 47
                     "vtrn.32   q12,    q14                     \n\t"   //  48 56 50 58 52 60 54 62
                     "vtrn.32   q13,    q15                     \n\t"   //  49 57 51 59 53 61 55 63
                     "                                          \n\t"   //
                     "vswp      d1,     d8                      \n\t"   //   0  8 16 24 32 40 48 56
                     "vswp      d2,     d16                     \n\t"   //
                     "vswp      d3,     d24                     \n\t"   //
                     "vswp      d5,     d12                     \n\t"   //   1  9 17 25 33 41 49 57
                     "vswp      d6,     d20                     \n\t"   //
                     "vswp      d7,     d28                     \n\t"   //
                     "vswp      d10,    d17                     \n\t"   //   2 10 18 26 34 42 50 58
                     "vswp      d11,    d25                     \n\t"   //
                     "vswp      d14,    d21                     \n\t"   //   3 11 19 27 35 43 51 59
                     "vswp      d15,    d29                     \n\t"   //
                     "vswp      d19,    d26                     \n\t"   //   4 12 20 28 36 44 52 60
                     "vswp      d23,    d30                     \n\t"   //   5 13 21 29 37 45 53 61
                     "                                          \n\t"   //   6 14 22 30 38 46 54 62
                     "                                          \n\t"   //   7 15 23 31 39 47 55 63
                     "                                          \n\t"   //
                     "mov       r8,     %[dest]                 \n\t"   // 転送先アドレス r0 - r7
                     "ldmia     r8,     {r0-r7}                 \n\t"   //
                     "vstmia    r0,     {q0-q1}                 \n\t"   //
                     "vstmia    r1,     {q2-q3}                 \n\t"   //
                     "vstmia    r2,     {q4-q5}                 \n\t"   //
                     "vstmia    r3,     {q6-q7}                 \n\t"   //
                     "vstmia    r4,     {q8-q9}                 \n\t"   //
                     "vstmia    r5,     {q10-q11}               \n\t"   //
                     "vstmia    r6,     {q12-q13}               \n\t"   //
                     "vstmia    r7,     {q14-q15}               \n\t"   //
                     "                                          \n\t"   //
                     "vpop      {q8-q15}                        \n\t"   // pop
                     "vpop      {q0-q7}                         \n\t"   // pop
                     "pop       {r0-r8}                         \n\t"   // pop
                     :
                     : [src] "r" (src), [dest] "r" (dest)
                     :  "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7", "r8",
                        "q0",  "q1",  "q2",  "q3",  "q4",  "q5",  "q6",  "q7",
                        "q8",  "q9", "q10", "q11", "q12", "q13", "q14", "q15"
                     );

        } else if ((destnum == 4) && (srcnum == 4)) {

            __asm__ (
                     "push      {r0-r4}                         \n\t"   // 破壊レジスタを書いても一部反映されないようで怪しいので
                     "vpush     {q0-q3}                         \n\t"   // 使用しているものは全部退避
                     "                                          \n\t"   //
                     "mov       r4,     %[src]                  \n\t"   // 転送元アドレス r0 - r3
                     "ldmia     r4,     {r0-r3}                 \n\t"   //
                     "vldmia    r0,     {q0}                    \n\t"   //  d0  d1     0  1  2  3
                     "vldmia    r1,     {q1}                    \n\t"   //  d2  d3     4  5  6  7
                     "vldmia    r2,     {q2}                    \n\t"   //  d4  d5     8  9 10 11
                     "vldmia    r3,     {q3}                    \n\t"   //  d6  d7    12 13 14 15
                     "                                          \n\t"   //
                     "vtrn.32   q0,     q1                      \n\t"   //   0  4  2  6
                     "                                          \n\t"   //   1  5  3  7
                     "vtrn.32   q2,     q3                      \n\t"   //   8 12 10 14
                     "                                          \n\t"   //   9 13 11 15
                     "                                          \n\t"   //
                     "vswp      d1,     d4                      \n\t"   //   0  4  8 12
                     "vswp      d3,     d6                      \n\t"   //   1  5  9 13
                     "                                          \n\t"   //   2  6 10 14
                     "                                          \n\t"   //   3  7 11 15
                     "                                          \n\t"   //
                     "mov       r4,     %[dest]                 \n\t"   // 転送先アドレス r0 - r3
                     "ldmia     r4,     {r0-r3}                 \n\t"   //
                     "vstmia    r0,     {q0}                    \n\t"   //
                     "vstmia    r1,     {q1}                    \n\t"   //
                     "vstmia    r2,     {q2}                    \n\t"   //
                     "vstmia    r3,     {q3}                    \n\t"   //
                     "                                          \n\t"   //
                     "vpop      {q0-q3}                         \n\t"   // pop
                     "pop       {r0-r4}                         \n\t"   // pop
                     :
                     : [src] "r" (src), [dest] "r" (dest)
                     :  "r0", "r1", "r2", "r3", "r4",
                        "q0", "q1", "q2", "q3"
                     );
            
        } else {

            for(int i = 0; i < destnum; i++) {
                for(int j = 0; j < srcnum; j++) {
                    dest[i][j] = src[j][i];
                }
            }
            
        }
        
    }
};

#endif // ENABLE_NEON
    
} // end of namespace


#endif  /* FLOATx16_MODE */


#endif
