
/************************************************************
**  SemiBlindICA.c
**
**  Object name :
**    SemiBlindICA
**
**  History :
**     2013.05.14 M.Takigahira Build有無指定に対応
**     2013.01.10 K.Takagi サンプルネットワークとデータを比較し結果を確認
**     2012.12.18 K.Takagi 既知の値にて結果を確認
**     2012.12.17 K.Takagi 処理部分を実装、ビルドの確認
**     2012.12.16 K.Takagi 新規作成
**
**  Description
**  -----------
**  セミブラインドICA
**
**  Copyright (c) 2012 - 2013
**  System In Frontier,Inc.
**  All rights reserved.
**
**  当社の許可なしに複製および転載を禁ず
**
*/

///////////////////////////////////////////////////////////////
//  Include Files
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <sys/time.h>
#include "cuda.h"
#include "cuda_runtime.h"
#include "cuda_profiler_api.h"
#include "EventLog.h"

#include "build_modules.h"
#include "HA_log.h"
#include "android_hark.h"

#include "MatrixMemoryAllocate.h"
#include "SemiBlindICA_gpu.h"

#define TH_NUM 10

#define SBICA_CUDA_WORKS
#define SBICA_CUDA_STREAM
#define SBICA_CUDA_CMEM_PARAM

//#define SAMPLE_FOUT

///////////////////////////////////////////////////////////////
//  Define Macros
#define FLOOR_VALUE (1.0E-5f)

#if !defined BUILD_MODULE_SEMI_BLIND_ICA
///////////////////////////////////////////////////////////////
//  Dummy Functions for build empty methods.
int HASetParamSemiBlindICA(Param_SemiBlindICA_t *param){ return 0; }
int HAInitSemiBlindICA(void){ return 0; }
int HAReleaseSemiBlindICA(void){ return 0; }
int SemiBlindICA(int frame_count, COMPLEX_FLOAT **out_data, COMPLEX_FLOAT **in_data){ return 0; }
#ifdef STANDALON_TEST
int main(int argc, char **argv){ return 0; }
#endif // STANDALON_TEST
#else // #if defined BUILD_MODULE_SEMI_BLIND_ICA
///////////////////////////////////////////////////////////////
//  Parameter

/*=========================================================*/
/*                 Structure Declaration                   */
/*=========================================================*/
typedef struct _SBICA_INFO_t_
{
    int taps;
    int idx;
    int itvl;
    unsigned int mask;
    float n_var;
    float r_var;
}SBICA_INFO_t;

typedef struct _SBICA_CUDA_CMEM_PARAM_t_
{
    int ch;
    int freq;
    int size;
    int noise_ch;
    float is_zero_local;
} SBICA_CUDA_CMEM_PARAM_t;

typedef struct _SBICA_CUDA_WORKS_t_
{
    float mu_f;
    float rsvd;
    COMPLEX_FLOAT buf_s;
    COMPLEX_FLOAT buf_ns;
    COMPLEX_FLOAT w;
} SBICA_CUDA_WORKS_t;

/*=========================================================*/
/*                    Private Valiable                     */
/*=========================================================*/
static Param_SemiBlindICA_t semi_blind_ica_info;
static SBICA_INFO_t **sbica_info;

#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_WORKS)
// 無し
#else

//static COMPLEX_FLOAT ***mu_f;
static float ***mu_f;
static COMPLEX_FLOAT ***buf_s;
static COMPLEX_FLOAT ***buf_ns;
static COMPLEX_FLOAT ***w;

#endif

#if defined(SBICA_CUDA_ENABLE)

MEM_t sbica_info_t;
__device__ static SBICA_INFO_t *sbica_info_d;

#if defined(SBICA_CUDA_WORKS)

MEM_t works_t;
static SBICA_CUDA_WORKS_t ***works;
__device__ static SBICA_CUDA_WORKS_t *works_d;

#else

MEM_t mu_f_t;
MEM_t w_t;
MEM_t buf_s_t;
MEM_t buf_ns_t;
__device__ static float *mu_f_d;
__device__ static COMPLEX_FLOAT *buf_s_d;
__device__ static COMPLEX_FLOAT *buf_ns_d;
__device__ static COMPLEX_FLOAT *w_d;

#endif

__constant__ static float mu_s_d;
__constant__ static float mu_n_d;

SBICA_CUDA_CMEM_PARAM_t c_prms;
__constant__ SBICA_CUDA_CMEM_PARAM_t c_prms_d;

static cudaStream_t stream[TH_NUM];

#endif // SBICA_CUDA_ENABLE

static float mu_s;
static float mu_n;

//static int qqqq_max, qqqq_min;
//unsigned int rs; //random seed
__device__ static unsigned int randtemp_d[4]; // for CUDA
static unsigned int randtemp[4];
__device__ static unsigned int randindex_d; // for CUDA
static unsigned int randindex;
/*
unsigned int xorshift128(void){
  unsigned int t = randtemp[0] ^ (randtemp[0] << 11);
  randtemp[0]=randtemp[1]; randtemp[1]=randtemp[2]; randtemp[2]=randtemp[3];
  return randtemp[3] = randtemp[3] ^ (randtemp[3] >> 19) ^ t ^ (t >> 8);
}
*/
/*
float floor_rand(){
  union {
    float f;
    struct ieee_single {
      u_int mant : 23;
      u_int expo : 8;
      u_int sign : 1;
    } s;
  } u; u.f = 1.0f;
  unsigned int t = randtemp[0] ^ (randtemp[0] << 11);
  randtemp[0]=randtemp[1]; randtemp[1]=randtemp[2]; randtemp[2]=randtemp[3];
  randtemp[3] = randtemp[3] ^ (randtemp[3] >> 19) ^ t ^ (t >> 8);
  //return FLOOR_VALUE * randtemp[3] / (float)4294967296.0;
  u.s.expo = (randtemp[3] >> 9);
  return FLOOR_VALUE * (u.f - 1.0f);
}
*/
//__device__ float floor_rand(){ // for CUDA
__device__ __inline float floor_rand_d(){
  union {
    float f;
    unsigned int s;
  } u;
  unsigned int index3 = (randindex_d + 3) & 0x3;
  unsigned int t = randtemp_d[randindex_d] ^ (randtemp_d[randindex_d] << 11);
  randtemp_d[randindex_d] = randtemp_d[index3] ^ (randtemp_d[index3] >> 19) ^ t ^ (t >> 8);
  u.s = (randtemp_d[randindex_d] >> 9) | 0x3f800000;
  randindex_d = (randindex_d + 1) & 0x3;
  return FLOOR_VALUE * (u.f - 1.0f);
}

__inline float floor_rand(){
  union {
    float f;
    unsigned int s;
  } u;
  unsigned int index3 = (randindex + 3) & 0x3;
  unsigned int t = randtemp[randindex] ^ (randtemp[randindex] << 11);
  randtemp[randindex] = randtemp[index3] ^ (randtemp[index3] >> 19) ^ t ^ (t >> 8);
  u.s = (randtemp[randindex] >> 9) | 0x3f800000;
  randindex = (randindex + 1) & 0x3;
  return FLOOR_VALUE * (u.f - 1.0f);
}

#if 0
// ieee754 floating point format trick (big error rate : NG)
__inline float fast_exp1(float f){
  union {
    double d;
    int i[2];
  } y;
  y.i[0]=0;
  y.i[1]=(1512775 * f + 1072632447); //1512775 * f + (1072693248 - 60801)
  return (float) y.d;
}

// google fastaprox (little error rate : OK but slower)
__inline float fast_exp2(float f){
  float offset = (f < 0) ? 1.0f : 0.0f;
  float clip = (f < -126) ? -126.0f : f;
  int w = clip;
  float z = clip - w + offset;
  union {
    unsigned int i;
    float f;
  } v = { (unsigned int) ((1 << 23) * (clip + 121.2740575f + 27.7280233f / (4.84252568f - z) - 1.49012907f * z)) };
  return 1.442695040f * v.f;
}

// google fastaprox (midium error rate : OK)
__inline float fast_exp3(float f){
  float clip = (f < -126) ? -126.0f : f;
  union {
    unsigned int i;
    float f;
  } v = { (unsigned int) ((1 << 23) * (clip + 126.94269504f)) };
  return 1.442695040f * v.f;
}

// use fast_exp3 algorithm and mul reduction (big error rate : NG)
//(exp(f)-exp(-f)) / (exp(f)+exp(-f))
__inline float fast_tanh3(float f){
  float pclip = (f < -126) ? -126.0f : f;
  float nclip = (-f < -126) ? -126.0f : -f;
  union {
    unsigned int i;
    float f;
  } pv = { (unsigned int) ((1 << 23) * (pclip + 126.94269504f)) };
  union {
    unsigned int i;
    float f;
  } nv = { (unsigned int) ((1 << 23) * (nclip + 126.94269504f)) };
  //float v = (((1.442695040f*pv.f-1.442695040f*nv.f))/((1.442695040f*pv.f+1.442695040f*nv.f)));
  float v = ((pv.f-nv.f)/(pv.f+nv.f));
  return v;
}
#endif

// use fast_exp3 algorithm and error reduction
//((exp(f)-exp(-f)) / (exp(f)+exp(-f))) * (exp(-f) / exp(-f));
//(1.0f-exp(-2f)) / (1.0f+exp(-2f)); 
__device__ __host__ __inline float fast_tanh31(float in_f){
  float f = -2.0f * in_f;
  //if(f < -126){ f = -126.0f; }
  //f = ((f < -126) ? -126.0f : f);
  f = ((f < -126) ? 0.94269504f : f + 126.94269504f);
  union {
    unsigned int i;
    float f;
  } v = { (unsigned int) ((1 << 23) * (f)) }; // f + 126.94269504f
  //v.f *= 1.442695040f;
  //return ((1.0f-v.f)/(1.0f+v.f));
  // divide constant value, (1.0f/1.442695040f) => 0.693147180987050458f
  return (0.693147180987050458f-v.f)/(0.693147180987050458f+v.f);
}

#if 0
// {ieee float/ieee} union version (Maybe slow :: obsolete)
__inline float fast_tanh32(float in_f){
  union {
    float f;
    struct ieee_single {
      u_int mant : 23;
      u_int expo : 8;
      u_int sign : 1;
    } s;
  } u = { in_f };
  u.s.sign ^= 0x1; u.s.expo += 1; // u.f *= -2.0f;
  u.f = ((u.f < -126) ? 0.94269504f : u.f + 126.94269504f);
  u.s.expo += 23; // u.f *= (1 << 23);
  union {
    unsigned int i;
    float f;
  } v = { (unsigned int) (u.f) };
  return (0.693147180987050458f-v.f)/(0.693147180987050458f+v.f);
}

// google fastaprox algorithm (for compare fast_tanh31, etc)
//-1.0f + 2.0f / (1.0f + fast_exp3(-2.0f*f))
__inline float fast_tanh4(float in_f){
  float f = -2.0 * in_f;
  union {
    unsigned int i;
    float f;
  } v = { (unsigned int) ((1 << 23) * (((f < -126) ? -126.0f : f) + 126.94269504f)) };
  return -1.0f + 2.0f / (1.0f + 1.442695040f * v.f);
}
#endif

#define CHECK(call)                                                  \
{                                                                    \
    const cudaError_t error = call;                                  \
    if (error != cudaSuccess)                                        \
    {                                                                \
        printf("Error: %s:%d, ", __FILE__, __LINE__);                \
        printf("code:%d, reason: %s\n", error,                       \
                cudaGetErrorString(error));                          \
        exit(1);                                                     \
    }                                                                \
}

void h_printCudaDeviceProp(void)
{
    int dev = 0;
    cudaDeviceProp deviceProp;

    CHECK(cudaSetDevice(dev));
    CHECK(cudaGetDeviceProperties(&deviceProp, dev));

    printf("[CU] cudaDeviceProp\n");
    printf("  name               %s\n",  deviceProp.name);
    printf("  totalGlobalMem     %lu\n", deviceProp.totalGlobalMem);
    printf("  sharedMemPerBlock  %lu\n", deviceProp.sharedMemPerBlock);
    printf("  regsPerBlock       %d\n",  deviceProp.regsPerBlock);
    printf("  warpSize           %d\n",  deviceProp.warpSize);
    printf("  memPitch           %lu\n", deviceProp.memPitch);
    printf("  maxThreadsPerBlock %d\n",  deviceProp.maxThreadsPerBlock);
    printf("  maxThreadsDim      %d,%d,%d\n", deviceProp.maxThreadsDim[0], deviceProp.maxThreadsDim[1], deviceProp.maxThreadsDim[2]);
    printf("  maxGridSize        %d,%d,%d\n", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]);
    printf("  clockRate          %d\n",  deviceProp.clockRate);
    printf("  totalConstMem      %lu\n", deviceProp.totalConstMem);
    printf("  major.minor        %d.%d\n",  deviceProp.major, deviceProp.minor);
    printf("  deviceOverlap      %d\n",  deviceProp.deviceOverlap);
    printf("  canMapHostMemory   %d\n",  deviceProp.canMapHostMemory);
    printf("  computeMode        %d\n",  deviceProp.computeMode);
    printf("  concurrentKernels  %d\n",  deviceProp.concurrentKernels);
    printf("  asyncEngineCount   %d\n",  deviceProp.asyncEngineCount);
    printf("  unifiedAddressing  %d\n",  deviceProp.unifiedAddressing);

    cudaFuncCache cacheConfig;
    cudaSharedMemConfig shMemConfig;

    CHECK(cudaDeviceGetCacheConfig(&cacheConfig));
    printf("[CU] cacheConfig\n");
    switch(cacheConfig)
    {
        case cudaFuncCachePreferNone:   printf("  cudaFuncCachePreferNone: no preference for shared memory or L1 (default)\n"); break;
        case cudaFuncCachePreferShared: printf("  cudaFuncCachePreferShared: prefer larger shared memory and smaller L1 cache\n"); break;
        case cudaFuncCachePreferL1:     printf("  cudaFuncCachePreferL1: prefer larger L1 cache and smaller shared memory\n"); break;
        case cudaFuncCachePreferEqual:  printf("  cudaFuncCachePreferEqual: prefer equal size L1 cache and shared memory\n"); break;
    }

    CHECK(cudaDeviceGetSharedMemConfig(&shMemConfig));
    printf("[CU] shMemConfig\n");
    switch(shMemConfig)
    {
        case cudaSharedMemBankSizeDefault:   printf("  cudaSharedMemBankSizeDefault: shared memory bank width is default\n"); break;
        case cudaSharedMemBankSizeFourByte:  printf("  cudaSharedMemBankSizeFourByte: shared memory bank width is four bytes\n"); break;
        case cudaSharedMemBankSizeEightByte: printf("  cudaSharedMemBankSizeEightByte: shared memory bank width is eight bytes\n"); break;
    }
}
/* 参考
struct cudaDeviceProp {
    char name[256];
    size_t totalGlobalMem;
    size_t sharedMemPerBlock;
    int regsPerBlock;
    int warpSize;
    size_t memPitch;
    int maxThreadsPerBlock;
    int maxThreadsDim[3];
    int maxGridSize[3];
    int clockRate;
    size_t totalConstMem;
    int major;
    int minor;
    size_t textureAlignment;
    size_t texturePitchAlignment;
    int deviceOverlap;
    int multiProcessorCount;
    int kernelExecTimeoutEnabled;
    int integrated;
    int canMapHostMemory;
    int computeMode;
    int maxTexture1D;
    int maxTexture1DMipmap;
    int maxTexture1DLinear;
    int maxTexture2D[2];
    int maxTexture2DMipmap[2];
    int maxTexture2DLinear[3];
    int maxTexture2DGather[2];
    int maxTexture3D[3];
    int maxTexture3DAlt[3];
    int maxTextureCubemap;
    int maxTexture1DLayered[2];
    int maxTexture2DLayered[3];
    int maxTextureCubemapLayered[2];
    int maxSurface1D;
    int maxSurface2D[2];
    int maxSurface3D[3];
    int maxSurface1DLayered[2];
    int maxSurface2DLayered[3];
    int maxSurfaceCubemap;
    int maxSurfaceCubemapLayered[2];
    size_t surfaceAlignment;
    int concurrentKernels;
    int ECCEnabled;
    int pciBusID;
    int pciDeviceID;
    int pciDomainID;
    int tccDriver;
    int asyncEngineCount;
    int unifiedAddressing;
    int memoryClockRate;
    int memoryBusWidth;
    int l2CacheSize;
    int maxThreadsPerMultiProcessor;
    int streamPrioritiesSupported;
    int globalL1CacheSupported;
    int localL1CacheSupported;
    size_t sharedMemPerMultiprocessor;
    int regsPerMultiprocessor;
    int managedMemSupported;
    int isMultiGpuBoard;
    int multiGpuBoardGroupID;
}*/

void h_cudaDeviceReset(void)
{
    CHECK(cudaDeviceReset());
}

#ifdef SBICA_CUDA_ENABLE

void **h_cudaMalloc2D(MEM_t *mem_t, const char *name, unsigned int kind, unsigned int ny, unsigned int nx, size_t size)
{
    cudaError_t err;
    void **data;
    int y;

    data = (void **)malloc(sizeof(void *) * ny);
    if(data == NULL){
        printf("[CU] %s: [1] malloc Error.\n", __FUNCTION__);
        goto ErrEnd1_Allocate2D;
    }

    switch(kind)
    {
        // 通常メモリ＋グローバルメモリ
        case 0:
            // メモリ確保 (ホスト)
            data[0] = (void *)malloc(size * nx * ny);
            if(data[0] == NULL){
                printf("[CU] %s: [2] malloc Error.\n", __FUNCTION__);
                goto ErrEnd2_Allocate2D;
            }
            // メモリ確保 (デバイス)
            err = cudaMalloc(&mem_t->d_ptr, size * nx * ny);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMalloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            break;

        // ピンメモリ＋グローバルメモリ
        case 1:
            // メモリ確保 (ホスト)
            err = cudaMallocHost(&data[0], size * nx * ny);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMallocHost() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            // メモリ確保 (デバイス)
            err = cudaMalloc(&mem_t->d_ptr, size * nx * ny);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMalloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            break;

        // ゼロコピーメモリ
        case 2:
            // メモリ確保 (ホスト)
            err = cudaHostAlloc(&data[0], size * nx * ny, cudaHostAllocMapped);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaHostAlloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            // メモリアドレス取得 (デバイス)
            err = cudaHostGetDevicePointer(&mem_t->d_ptr, data[0], 0);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaHostGetDevicePointer() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            break;
        // ユニファイドメモリ
        case 3:
            // メモリ確保 (ユニファイド)
            err = cudaMallocManaged(&data[0], size * nx * ny);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMallocManaged() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd2_Allocate2D;
            }
            mem_t->d_ptr = data[0];
            break;
    }
    printf("[CU] %s: addr host %p device %p kind %d name %s (%s)\n", __FUNCTION__, data[0], mem_t->d_ptr, kind, name, cudaGetErrorString(err));

    if(err != cudaSuccess){
        goto ErrEnd2_Allocate2D;
    }
    for(y=1; y<ny; y++){
        data[y] = (unsigned char *)data[0] + y * nx * size;
    }

    mem_t->kind = kind;
    mem_t->data_2d = data;
    mem_t->data_3d = NULL;
    mem_t->h_ptr = data[0];
    mem_t->dim = 2;
    mem_t->nx = nx;
    mem_t->ny = ny;
    mem_t->size = size;
    mem_t->alllen = ny * nx * size;
    strncpy(mem_t->name, name, sizeof(mem_t->name));

//  h_printMem(mem_t);
    return data;

ErrEnd2_Allocate2D:
    free(data);

ErrEnd1_Allocate2D:
    return NULL;
}

void ***h_cudaMalloc3D(MEM_t *mem_t, const char *name, unsigned int kind, unsigned int nz, unsigned int ny, unsigned int nx, size_t size)
{
    cudaError_t err;
    void ***data;
    int z;
    int y;

    data = (void ***)malloc(sizeof(void **) * nz);
    if(data == NULL){
        printf("[CU] %s: [1] malloc Error.\n", __FUNCTION__);
        goto ErrEnd1_Allocate3D;
    }
    data[0] = (void **)malloc(sizeof(void *) * ny * nz);
    if(data[0] == NULL){
        printf("[CU] %s: [2] malloc Error.\n", __FUNCTION__);
        goto ErrEnd2_Allocate3D;
    }
    for(z=1; z<nz; z++){
        data[z] = &(data[0][z*ny]);
    }

    switch(kind)
    {
        // 通常メモリ＋グローバルメモリ
        case 0:
            // メモリ確保 (ホスト)
            data[0][0] = (void *)malloc(size * nx * ny * nz);
            if(data[0][0] == NULL){
                printf("[CU] %s: [3] malloc Error.\n", __FUNCTION__);
                goto ErrEnd3_Allocate3D;
            }
            // メモリ確保 (デバイス)
            err = cudaMalloc(&mem_t->d_ptr, size * nx * ny * nz);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMalloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            break;

        // ピンメモリ＋グローバルメモリ
        case 1:
            // メモリ確保 (ホスト)
            err = cudaMallocHost(&data[0][0], size * nx * ny * nz);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMallocHost() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            // メモリ確保 (デバイス)
            err = cudaMalloc(&mem_t->d_ptr, size * nx * ny * nz);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMalloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            break;

        // ゼロコピーメモリ
        case 2:
            // メモリ確保 (ホスト)
            err = cudaHostAlloc(&data[0][0], size * nx * ny * nz, cudaHostAllocMapped);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaHostAlloc() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            // メモリアドレス取得 (デバイス)
            err = cudaHostGetDevicePointer(&mem_t->d_ptr, mem_t->h_ptr, 0);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaHostGetDevicePointer() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            break;
        // ユニファイドメモリ
        case 3:
            // メモリ確保 (ユニファイド)
            err = cudaMallocManaged(&data[0][0], size * nx * ny * nz);
            if(err != cudaSuccess){
                printf("[CU] %s: cudaMallocManaged() Error. kind %d name %s (%s)\n", __FUNCTION__, kind, name, cudaGetErrorString(err));
                goto ErrEnd3_Allocate3D;
            }
            mem_t->d_ptr = data[0][0];
            break;
    }
    printf("[CU] %s: addr host %p device %p kind %d name %s (%s)\n", __FUNCTION__, data[0][0], mem_t->d_ptr, kind, name, cudaGetErrorString(err));

    if(err != cudaSuccess){
        goto ErrEnd3_Allocate3D;
    }
    for(y=1; y<ny*nz; y++){
        data[0][y] = (unsigned char *)data[0][0] + y * nx * size;
    }

    mem_t->kind = kind;
    mem_t->data_2d = NULL;
    mem_t->data_3d = data;
    mem_t->h_ptr = data[0][0];
    mem_t->dim = 3;
    mem_t->nx = nx;
    mem_t->ny = ny;
    mem_t->nz = nz;
    mem_t->size = size;
    mem_t->alllen = nz * ny * nx * size;
    strncpy(mem_t->name, name, sizeof(mem_t->name));

//  h_printMem(mem_t);
    return data;

ErrEnd3_Allocate3D:
    free(data[0]);

ErrEnd2_Allocate3D:
    free(data);

ErrEnd1_Allocate3D:
    return NULL;
}

void h_cudaFree2D(MEM_t *mem_t)
{
    switch(mem_t->kind)
    {
        // 通常メモリ＋グローバルメモリ
        case 0:
            // メモリ解放 (ホスト)
            free(mem_t->data_2d[0]);
            free(mem_t->data_2d);
            // メモリ解放 (デバイス)
            CHECK(cudaFree(mem_t->d_ptr));
            break;

        // ピンメモリ＋グローバルメモリ
        case 1:
            // メモリ解放 (ホスト)
            CHECK(cudaFreeHost(mem_t->data_2d[0]));
            free(mem_t->data_2d);
            // メモリ解放 (デバイス)
            CHECK(cudaFree(mem_t->d_ptr));
            break;

        // ゼロコピーメモリ
        case 2:
            // メモリ解放 (ホスト)
            CHECK(cudaFreeHost(mem_t->data_2d[0]));
            free(mem_t->data_2d);
            break;

        // ユニファイドメモリ
        case 3:
            // メモリ解放 (ユニファイドメモリ)
            CHECK(cudaFree(mem_t->data_2d[0]));
            free(mem_t->data_2d);
            break;
    }
}

void h_cudaFree3D(MEM_t *mem_t)
{
    switch(mem_t->kind)
    {
        // 通常メモリ＋グローバルメモリ
        case 0:
            // メモリ解放 (ホスト)
            free(mem_t->data_3d[0][0]);
            free(mem_t->data_3d[0]);
            free(mem_t->data_3d);
            // メモリ解放 (デバイス)
            CHECK(cudaFree(mem_t->d_ptr));
            break;

        // ピンメモリ＋グローバルメモリ
        case 1:
            // メモリ解放 (ホスト)
            CHECK(cudaFreeHost(mem_t->data_3d[0][0]));
            free(mem_t->data_3d[0]);
            free(mem_t->data_3d);
            // メモリ解放 (デバイス)
            CHECK(cudaFree(mem_t->d_ptr));
            break;

        // ゼロコピーメモリ
        case 2:
            // メモリ解放 (ホスト)
            CHECK(cudaFreeHost(mem_t->data_3d[0][0]));
            free(mem_t->data_3d[0]);
            free(mem_t->data_3d);
            break;

        // ユニファイドメモリ
        case 3:
            // メモリ解放 (ユニファイドメモリ)
            CHECK(cudaFree(mem_t->data_3d[0][0]));
            free(mem_t->data_3d[0]);
            free(mem_t->data_3d);
            break;
    }
}

void h_printCudaMemGetInfo(void)
{
    size_t free;
    size_t total;

    CHECK(cudaMemGetInfo(&free, &total));
    printf("[CU] free memory %lu bytes, total memory %lu bytes\n", free, total);
}

__global__ void d_printMem(MEM_t mem_t)
{
    printf("[CU_D] %-12s addr %p size %d len %d dim %d (nx:%d ny:%d nz:%d)\n",
            mem_t.name, mem_t.d_ptr, mem_t.size, mem_t.alllen, mem_t.dim, mem_t.nx, mem_t.ny, mem_t.nz);
}

void h_printMem(MEM_t *mem_t)
{
    printf("[CU] %-12s kind %d h_ptr %p d_ptr %p alllen %d dim %d (nx:%d ny:%d nz:%d size:%d)\n",
            mem_t->name, mem_t->kind, mem_t->h_ptr, mem_t->d_ptr, mem_t->alllen, mem_t->dim, mem_t->nx, mem_t->ny, mem_t->nz, mem_t->size);
}

__global__ void d_printMemDump(MEM_t mem_t, unsigned int len)
{
    unsigned int i;
    unsigned char *ub_p = (unsigned char *)(mem_t.d_ptr);

    printf("[CU_D] %-12s addr %p size %d len %d dim %d (nx:%d ny:%d nz:%d)\n",
            mem_t.name, mem_t.d_ptr, mem_t.size, mem_t.alllen, mem_t.dim, mem_t.nx, mem_t.ny, mem_t.nz);
    printf("%16s : +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F\n", "Address");
    printf("       -----------------------------------------------------------\n");
    for(i=0; i<=len/16; i++) {
        printf("%16lx : %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", (unsigned long)(ub_p+i*16),
                                    *(ub_p+i*16+0), *(ub_p+i*16+1), *(ub_p+i*16+2), *(ub_p+i*16+3),
                                    *(ub_p+i*16+4), *(ub_p+i*16+5), *(ub_p+i*16+6), *(ub_p+i*16+7),
                                    *(ub_p+i*16+8), *(ub_p+i*16+9), *(ub_p+i*16+10), *(ub_p+i*16+11),
                                    *(ub_p+i*16+12), *(ub_p+i*16+13), *(ub_p+i*16+14), *(ub_p+i*16+15));
    }
}

void h_printMemDump(const char *name, void *mem_h, unsigned int len)
{
    unsigned int i;
    unsigned char *ub_p = (unsigned char *)(mem_h);

    printf("[CU] %-12s addr %p\n", name, mem_h);
    printf("%16s : +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F\n", "Address");
    printf("       -----------------------------------------------------------\n");
    for(i=0; i<=len/16; i++) {
        printf("%16lx : %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", (unsigned long)(ub_p+i*16),
                                    *(ub_p+i*16+0), *(ub_p+i*16+1), *(ub_p+i*16+2), *(ub_p+i*16+3),
                                    *(ub_p+i*16+4), *(ub_p+i*16+5), *(ub_p+i*16+6), *(ub_p+i*16+7),
                                    *(ub_p+i*16+8), *(ub_p+i*16+9), *(ub_p+i*16+10), *(ub_p+i*16+11),
                                    *(ub_p+i*16+12), *(ub_p+i*16+13), *(ub_p+i*16+14), *(ub_p+i*16+15));
    }
}

__global__ void d_printGlobalMem()
{
    printf("[CU_D] sbica_info_d addr %p\n", sbica_info_d);
#if defined(SBICA_CUDA_WORKS)
    printf("[CU_D] works_d      addr %p\n", works_d);
#else
    printf("[CU_D] mu_f_d       addr %p\n", mu_f_d);
    printf("[CU_D] buf_s_d      addr %p\n", buf_s_d);
    printf("[CU_D] buf_ns_d     addr %p\n", buf_ns_d);
    printf("[CU_D] w_d          addr %p\n", w_d);
#endif
}

#ifdef SBICA_CUDA_CMEM_PARAM
__global__ void d_printConstArgs()
{
    printf("[CU_D] c_prms_d.ch       %d\n", c_prms_d.ch);
    printf("[CU_D] c_prms_d.freq     %d\n", c_prms_d.freq);
    printf("[CU_D] c_prms_d.size     %d\n", c_prms_d.size);
    printf("[CU_D] c_prms_d.noise_ch %d\n", c_prms_d.noise_ch);
    printf("[CU_D] c_prms_d.is_zero  %f\n", c_prms_d.is_zero_local);
}
#endif

#endif // SBICA_CUDA_ENABLE

void h_printTypeSize(void)
{
    printf("[CU] sizeof(void*)              = %lu\n", sizeof(void*));
    printf("[CU] sizeof(void**)             = %lu\n", sizeof(void**));
    printf("[CU] sizeof(int)                = %lu\n", sizeof(int));
    printf("[CU] sizeof(long)               = %lu\n", sizeof(long));
    printf("[CU] sizeof(long long)          = %lu\n", sizeof(long long));
    printf("[CU] sizeof(unsigned int)       = %lu\n", sizeof(unsigned int));
    printf("[CU] sizeof(unsigned long)      = %lu\n", sizeof(unsigned long));
    printf("[CU] sizeof(unsigned long long) = %lu\n", sizeof(unsigned long long));
#ifdef SBICA_CUDA_ENABLE
    printf("[CU] sizeof(MEM_t)              = %lu\n", sizeof(MEM_t));
    printf("[CU] -------------------------------\n");
    printf("[CU] offsetof(MEM_t.name)     = %lu\n", offsetof(MEM_t, name));
    printf("[CU] offsetof(MEM_t.kind)     = %lu\n", offsetof(MEM_t, kind));
    printf("[CU] offsetof(MEM_t.dim)      = %lu\n", offsetof(MEM_t, dim));
    printf("[CU] offsetof(MEM_t.nx)       = %lu\n", offsetof(MEM_t, nx));
    printf("[CU] offsetof(MEM_t.ny)       = %lu\n", offsetof(MEM_t, ny));
    printf("[CU] offsetof(MEM_t.nz)       = %lu\n", offsetof(MEM_t, nz));
    printf("[CU] offsetof(MEM_t.size)     = %lu\n", offsetof(MEM_t, size));
    printf("[CU] offsetof(MEM_t.alllen)   = %lu\n", offsetof(MEM_t, alllen));
    printf("[CU] offsetof(MEM_t.data_2d)  = %lu\n", offsetof(MEM_t, data_2d));
    printf("[CU] offsetof(MEM_t.data_3d)  = %lu\n", offsetof(MEM_t, data_3d));
    printf("[CU] offsetof(MEM_t.h_ptr)    = %lu\n", offsetof(MEM_t, h_ptr));
    printf("[CU] offsetof(MEM_t.d_ptr)    = %lu\n", offsetof(MEM_t, d_ptr));
#endif
}

///////////////////////////////////////////////////////////////
//  Local Buffer

///////////////////////////////////////////////////////////////
//  Clear
/************************************************************
**  History :
**    2012.12.16 K.Takagi 新規作成
*/
// Public : バッファクリアなどの解放処理
int HAReleaseSemiBlindICA(const int thread_no)
{
#if defined(SBICA_CUDA_ENABLE)
    if(thread_no == 0) {
        h_cudaFree2D(&sbica_info_t);
#if defined(SBICA_CUDA_WORKS)
        h_cudaFree3D(&works_t);
#else
        h_cudaFree3D(&mu_f_t);
        h_cudaFree3D(&w_t);
        h_cudaFree3D(&buf_s_t);
        h_cudaFree3D(&buf_ns_t);
#endif
    }
#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_STREAM)
//  printf("[CU] cudaStreamDestroy() stream[%d] %p\n", thread_no, stream[thread_no]);
    CHECK(cudaStreamDestroy(stream[thread_no]));
#endif
#else
    if(sbica_info != NULL){
        free(sbica_info[0]);
        free(sbica_info);
    }
    sbica_info = NULL;

    if(mu_f != NULL){
        free(mu_f[0][0]);
        free(mu_f[0]);
        free(mu_f);
    }
    mu_f = NULL;

    if(w != NULL){
        free(w[0][0]);
        free(w[0]);
        free(w);
    }
    w = NULL;

    if(buf_s != NULL){
        free(buf_s[0][0]);
        free(buf_s[0]);
        free(buf_s);
    }
    buf_s = NULL;

    if(buf_ns != NULL){
        free(buf_ns[0][0]);
        free(buf_ns[0]);
        free(buf_ns);
    }
    buf_ns = NULL;
#endif
    return 0;
}

///////////////////////////////////////////////////////////////
//  Initialize
/************************************************************
**  History :
**    2012.12.16 K.Takagi 新規作成
*/
// Public : 初期化
int HAInitSemiBlindICA(const int thread_no)
{
    int i,j;
    int t;

    int channel_num;
    int fft_length;
    
    int taps;
    int tap_max;
    int tap_lower;
    int tap_upper;
    
    //float mu_s;
    float mu_v;
    //float mu_n;
    
    float lambda;
    
    int itvl;
    int size;
    int size_max;
    
    channel_num = semi_blind_ica_info.channel;
    if((channel_num <= 1) || (channel_num > 16)){
        return -1;
    }
    
    fft_length = semi_blind_ica_info.length;
    if(fft_length <= 0){
        return -1;
    }
    fft_length = semi_blind_ica_info.length / 2 + 1;

    itvl = semi_blind_ica_info.interval;

    mu_s = semi_blind_ica_info.mu_s / 10000.0;
    mu_v = semi_blind_ica_info.mu_v / 10000.0;
    mu_n = semi_blind_ica_info.mu_n / 10000.0;

    lambda = semi_blind_ica_info.lambda;

    tap_max = semi_blind_ica_info.tap_lower;
    tap_lower = semi_blind_ica_info.tap_lower;
    tap_upper = semi_blind_ica_info.tap_upper;

    if(thread_no == 0) {
		//h_printTypeSize();

#if defined(SBICA_CUDA_ENABLE)
        /* バッファ確保 */
        sbica_info = (SBICA_INFO_t **)h_cudaMalloc2D(&sbica_info_t, "sbica_info_t", 0, channel_num, fft_length, sizeof(SBICA_INFO_t));
        if(sbica_info == NULL){
            return -1;
        }
        
#if !defined(SBICA_CUDA_WORKS)
        mu_f = (float ***)h_cudaMalloc3D(&mu_f_t, "mu_f_t", 0, channel_num, fft_length, tap_max, sizeof(float));
        if(mu_f == NULL){
            return -1;
        }
        
        w = (COMPLEX_FLOAT ***)h_cudaMalloc3D(&w_t, "w_t", 0, channel_num, fft_length, tap_max, sizeof(COMPLEX_FLOAT));
        if(w == NULL){
            return -1;
        }
#endif
#else
        /* バッファ確保 */
        sbica_info = (SBICA_INFO_t **)MemoryAllocate2D(channel_num, fft_length, sizeof(SBICA_INFO_t));
        if(sbica_info == NULL){
            return -1;
        }
        
        mu_f = (float ***)MemoryAllocate3D(channel_num, fft_length, tap_max, sizeof(float));
        if(mu_f == NULL){
            return -1;
        }
        
        w = (COMPLEX_FLOAT ***)MemoryAllocate3D(channel_num, fft_length, tap_max, sizeof(COMPLEX_FLOAT));
        if(w == NULL){
            return -1;
        }
#endif
    
#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_WORKS)
        size_max = 0;
        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                taps = (int)((double)tap_lower + (double)(tap_upper - tap_lower) * (double)i / (double)fft_length);
                sbica_info[j][i].taps = taps;
                
                sbica_info[j][i].idx = 0;
                sbica_info[j][i].itvl = itvl;
                //sbica_info[j][i].mu_s = mu_s;
                //sbica_info[j][i].mu_n = mu_n;
                
                sbica_info[j][i].n_var = 1.0;
                sbica_info[j][i].r_var = 1.0;
                
                for(t=1; t<31; t++){
                    size = 1 << t;
                    if((taps * itvl) <= size){
                        break;
                    }
                }
                sbica_info[j][i].mask = size -1;
                
                if(size_max < size){
                    size_max = size;
                }
            }
        }

        works = (SBICA_CUDA_WORKS_t ***)h_cudaMalloc3D(&works_t, "works_t", 0, channel_num, fft_length, size_max, sizeof(SBICA_CUDA_WORKS_t));
        if(works == NULL){
            return -1;
        }

        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                for(t=0; t<tap_max; t++){
                    works[j][i][t].mu_f = 0; 
                    
                    works[j][i][t].w.real = 0; 
                    works[j][i][t].w.imag = 0; 
                }

                for(t=0; t<sbica_info[j][i].taps; t++){
                    works[j][i][t].mu_f = pow(lambda, t) * mu_v;
                }
            }
        }
#else
        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                for(t=0; t<tap_max; t++){
                    mu_f[j][i][t] = 0; 
                    
                    w[j][i][t].real = 0; 
                    w[j][i][t].imag = 0; 
                }
            }
        }

        size_max = 0;
        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                taps = (int)((double)tap_lower + (double)(tap_upper - tap_lower) * (double)i / (double)fft_length);
                sbica_info[j][i].taps = taps;
                
                sbica_info[j][i].idx = 0;
                sbica_info[j][i].itvl = itvl;
                //sbica_info[j][i].mu_s = mu_s;
                //sbica_info[j][i].mu_n = mu_n;
                
                sbica_info[j][i].n_var = 1.0;
                sbica_info[j][i].r_var = 1.0;
                
                for(t=0; t<taps; t++){
                    mu_f[j][i][t] = pow(lambda, t) * mu_v;
                }
                
                for(t=1; t<31; t++){
                    size = 1 << t;
                    if((taps * itvl) <= size){
                        break;
                    }
                }
                sbica_info[j][i].mask = size -1;
                
                if(size_max < size){
                    size_max = size;
                }
            }
        }
#endif

#if defined(SBICA_CUDA_ENABLE)
#if !defined(SBICA_CUDA_WORKS)
        buf_s = (COMPLEX_FLOAT ***)h_cudaMalloc3D(&buf_s_t, "buf_s_t", 0, channel_num, fft_length, size_max, sizeof(COMPLEX_FLOAT));
        if(buf_s == NULL){
            return -1;
        }
        
        buf_ns = (COMPLEX_FLOAT ***)h_cudaMalloc3D(&buf_ns_t, "buf_ns_t", 0, channel_num, fft_length, size_max, sizeof(COMPLEX_FLOAT));
        if(buf_ns == NULL){
            return -1;
        }
#endif
#else
        buf_s = (COMPLEX_FLOAT ***)MemoryAllocate3D(channel_num, fft_length, size_max, sizeof(COMPLEX_FLOAT));
        if(buf_s == NULL){
            return -1;
        }
        
        buf_ns = (COMPLEX_FLOAT ***)MemoryAllocate3D(channel_num, fft_length, size_max, sizeof(COMPLEX_FLOAT));
        if(buf_ns == NULL){
            return -1;
        }
#endif
    
#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_WORKS)
        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                for(t=0; t<size_max; t++){
                    works[j][i][t].buf_s.real = 0; 
                    works[j][i][t].buf_s.imag = 0; 
                    
                    works[j][i][t].buf_ns.real = 0; 
                    works[j][i][t].buf_ns.imag = 0; 
                }
            }
        }
#else
        for(j=0; j<channel_num; j++){
            for(i=0; i<fft_length; i++){
                for(t=0; t<size_max; t++){
                    buf_s[j][i][t].real = 0; 
                    buf_s[j][i][t].imag = 0; 
                    
                    buf_ns[j][i][t].real = 0; 
                    buf_ns[j][i][t].imag = 0; 
                }
            }
        }
#endif

#if defined(SBICA_CUDA_ENABLE)
        // デバイスに転送
        CHECK(cudaMemcpyToSymbol(sbica_info_d, &sbica_info_t.d_ptr, sizeof(sbica_info_d)));
        CHECK(cudaMemcpy(sbica_info_t.d_ptr, sbica_info[0], sbica_info_t.alllen, cudaMemcpyHostToDevice));

#if defined(SBICA_CUDA_WORKS)
        CHECK(cudaMemcpyToSymbol(works_d, &works_t.d_ptr, sizeof(works_d)));
        CHECK(cudaMemcpy(works_t.d_ptr, works[0][0], works_t.alllen, cudaMemcpyHostToDevice));
#else
        CHECK(cudaMemcpyToSymbol(mu_f_d,       &mu_f_t.d_ptr,       sizeof(mu_f_d)));
        CHECK(cudaMemcpyToSymbol(w_d,          &w_t.d_ptr,          sizeof(w_d)));
        CHECK(cudaMemcpyToSymbol(buf_s_d,      &buf_s_t.d_ptr,      sizeof(buf_s_d)));
        CHECK(cudaMemcpyToSymbol(buf_ns_d,     &buf_ns_t.d_ptr,     sizeof(buf_ns_d)));
        CHECK(cudaMemcpy(mu_f_t.d_ptr,       mu_f[0][0],    mu_f_t.alllen,       cudaMemcpyHostToDevice));
        CHECK(cudaMemcpy(w_t.d_ptr,          w[0][0],       w_t.alllen,          cudaMemcpyHostToDevice));
        CHECK(cudaMemcpy(buf_s_t.d_ptr,      buf_s[0][0],   buf_s_t.alllen,      cudaMemcpyHostToDevice));
        CHECK(cudaMemcpy(buf_ns_t.d_ptr,     buf_ns[0][0],  buf_ns_t.alllen,     cudaMemcpyHostToDevice));
#endif

//      d_printGlobalMem<<<1,1>>>();
//      CHECK(cudaDeviceSynchronize());
#endif
    }

#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_STREAM)
    // ストリーム作成
    CHECK(cudaStreamCreateWithFlags(&stream[thread_no], cudaStreamNonBlocking));
//  printf("[CU] cudaStreamCreate() stream[%d] %p\n", thread_no, stream[thread_no]);
#endif

    if(thread_no == 0) {
        //rs = 1234;
        randtemp[0] = 123456789;
        randtemp[1] = 362436069;
        randtemp[2] = 521288629;
        randtemp[3] = 88675123;
        randindex = 0;

#if defined(SBICA_CUDA_ENABLE)
        CHECK(cudaMemcpyToSymbol(mu_s_d, &mu_s, sizeof(mu_s)));
        CHECK(cudaMemcpyToSymbol(mu_n_d, &mu_n, sizeof(mu_n)));
        CHECK(cudaMemcpyToSymbol(randtemp_d, randtemp, sizeof(randtemp)));
        CHECK(cudaMemcpyToSymbol(randindex_d, &randindex, sizeof(randindex)));

        h_printCudaDeviceProp();
        h_printCudaMemGetInfo();

#if 0
        h_printMem(&sbica_info_t);
        h_printMem(&mu_f_t);
        h_printMem(&w_t);
        h_printMem(&buf_s_t);
        h_printMem(&buf_ns_t);
#if defined(SBICA_CUDA_WORKS)
        h_printMem(&works_t);
#endif

        //d_printMemDump<<<1,1>>>(sbica_info_t, 64);
        //d_printMemDump<<<1,1>>>(mu_f_t, 64);
        //d_printMemDump<<<1,1>>>(w_t, 64);
        //d_printMemDump<<<1,1>>>(buf_s_t, 64);
        //d_printMemDump<<<1,1>>>(buf_ns_t, 64);
        //CHECK(cudaDeviceSynchronize());

        {
            MEM_t rand_t;

            CHECK(cudaGetSymbolAddress(&rand_t.d_ptr, randtemp_d));
            d_printMemDump<<<1,1>>>(rand_t, sizeof(randtemp));

            CHECK(cudaGetSymbolAddress(&rand_t.d_ptr, randindex_d));
            d_printMemDump<<<1,1>>>(rand_t, sizeof(randindex));
            CHECK(cudaDeviceSynchronize());
        }
#endif

#ifdef SBICA_CUDA_CMEM_PARAM
        {
            union {
                float f;
                struct ieee_single {
                    unsigned int mant : 23;
                    unsigned int expo : 8;
                    unsigned int sign : 1;
                } s;
            } u;

            u.f = semi_blind_ica_info.is_zero;
            u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
            c_prms.is_zero_local = u.f;

            // 入力データの最後のチャネルをノイズとする
            c_prms.ch = semi_blind_ica_info.channel;
            c_prms.freq = semi_blind_ica_info.length/2+1;
            c_prms.size = size_max;
            c_prms.noise_ch = semi_blind_ica_info.channel;

            // デバイスに転送
            CHECK(cudaMemcpyToSymbol(c_prms_d, &c_prms, sizeof(c_prms)));
            //d_printConstArgs<<<1,1>>>();
            //CHECK(cudaDeviceSynchronize());
        }
#else
        // no processing
#endif
#endif // SBICA_CUDA_ENABLE
    }

    return 0;
}

///////////////////////////////////////////////////////////////
//  SetParam
/************************************************************
**  History :
**    2012.12.16 K.Takagi 新規作成
*/
// Public : パラメータ設定
int HASetParamSemiBlindICA(const int thread_no, Param_SemiBlindICA_t *param)
{
    int channel_num;
    channel_num = param->channel;
    if((channel_num <= 1) || (channel_num > 16)){
        return -1;
    }

    if(thread_no == 0){
        semi_blind_ica_info.channel     = param->channel;
        semi_blind_ica_info.length      = param->length;
        semi_blind_ica_info.interval    = param->interval;
        semi_blind_ica_info.tap_lower   = param->tap_lower;
        semi_blind_ica_info.tap_upper   = param->tap_upper;
        semi_blind_ica_info.lambda      = param->lambda;
        semi_blind_ica_info.mu_v        = param->mu_v;
        semi_blind_ica_info.mu_s        = param->mu_s;
        semi_blind_ica_info.mu_n        = param->mu_n;
        semi_blind_ica_info.is_zero     = param->is_zero;
        semi_blind_ica_info.output_freq = param->output_freq;

        printf("semi_blind_ica_info.channel     = %d\n",  semi_blind_ica_info.channel);
        printf("semi_blind_ica_info.length      = %d\n",  semi_blind_ica_info.length);
        printf("semi_blind_ica_info.interval    = %d\n",  semi_blind_ica_info.interval);
        printf("semi_blind_ica_info.tap_lower   = %d\n",  semi_blind_ica_info.tap_lower);
        printf("semi_blind_ica_info.tap_upper   = %d\n",  semi_blind_ica_info.tap_upper);
        printf("semi_blind_ica_info.lambda      = %lf\n", semi_blind_ica_info.lambda);
        printf("semi_blind_ica_info.mu_v        = %lf\n", semi_blind_ica_info.mu_v);
        printf("semi_blind_ica_info.mu_s        = %lf\n", semi_blind_ica_info.mu_s);
        printf("semi_blind_ica_info.mu_n        = %lf\n", semi_blind_ica_info.mu_n);
        printf("semi_blind_ica_info.is_zero     = %lf\n", semi_blind_ica_info.is_zero);
        printf("semi_blind_ica_info.output_freq = %d\n",  semi_blind_ica_info.output_freq);
    }

    return 0;
}

#if defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_CMEM_PARAM) && defined(SBICA_CUDA_WORKS)

__global__ void SemiBlindICA_d(const int thread_no, COMPLEX_FLOAT *in_data_d, COMPLEX_FLOAT *out_data_d)
{
    unsigned int nx_2d = c_prms_d.freq;
    unsigned int ny_2d = c_prms_d.ch;
    unsigned int nx_3d = c_prms_d.size;
    unsigned int ny_3d = c_prms_d.freq;
    unsigned int nz_3d = c_prms_d.ch;
    unsigned int ix = threadIdx.x + blockIdx.x * blockDim.x;
    unsigned int iy = threadIdx.y + blockIdx.y * blockDim.y;
    unsigned int idx_2d;
    unsigned int idx_3d;
    unsigned int idx_3d_base;
    unsigned int idx_noise;
    
    union {
        float f;
        struct ieee_single {
            unsigned int mant : 23;
            unsigned int expo : 8;
            unsigned int sign : 1;
        } s;
    } u;

    int i;
    
    int idx;
    int itvl;
    int mask;
    int taps;
    
    float n_var;
    float r_var;

    float f_real;
    float f_imag;
    float cf_abs;

    COMPLEX_FLOAT d, sum, c1, n_d;
    int midx;
    float ry, v1, e1, rx, v2, e2;

    if(ix >= nx_2d || iy >= ny_2d)
        return;

    // 2Dインデックスチェック
    idx_2d = iy * nx_2d + ix;
    if(idx_2d >= (ny_2d * nx_2d)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx_2d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx_2d);
        return;
    }

    taps = sbica_info_d[idx_2d].taps;

    idx  = sbica_info_d[idx_2d].idx;
    itvl = sbica_info_d[idx_2d].itvl;
    mask = sbica_info_d[idx_2d].mask;

    n_var = sbica_info_d[idx_2d].n_var;
    r_var = sbica_info_d[idx_2d].r_var;

    // 3Dインデックスチェック
    idx_3d_base = nx_3d * ny_3d * iy + nx_3d * ix;
    idx_3d = idx_3d_base + idx;
    if(idx_3d >= (nz_3d * ny_3d * nx_3d)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx=%d,idx_2d=%d,idx_3d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx, idx_2d, idx_3d);
        return;
    }
    idx_noise = c_prms_d.noise_ch * nx_2d + ix;

    //デバック
    //LOGI("ch = %d f = %d\n", ch, f);
    //LOGI("%d, %d\n", ch, f);
    //LOGI("x = (%g, %g) s = (%g, %g)\n", in_data_d[idx_2d].real, in_data_d[idx_2d].imag, in_data_d[idx_noise].real, in_data_d[idx_noise].imag);
    
    u.f = in_data_d[idx_noise].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    works_d[idx_3d].buf_s.real = u.f;
    u.f = in_data_d[idx_noise].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    works_d[idx_3d].buf_s.imag = u.f;

    cf_abs = hypot(works_d[idx_3d].buf_s.real, works_d[idx_3d].buf_s.imag);
    
    if(cf_abs < c_prms_d.is_zero_local){
        out_data_d[idx_2d] = in_data_d[idx_2d];
        return;
    }

    sum.real = 0;
    sum.imag = 0;
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real = works_d[idx_3d_base+i].w.real * works_d[idx_3d_base+midx].buf_s.real - works_d[idx_3d_base+i].w.imag * works_d[idx_3d_base+midx].buf_s.imag;
        f_imag = works_d[idx_3d_base+i].w.real * works_d[idx_3d_base+midx].buf_s.imag + works_d[idx_3d_base+i].w.imag * works_d[idx_3d_base+midx].buf_s.real;
        sum.real += f_real;
        sum.imag += f_imag;
    }
    u.f = in_data_d[idx_2d].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.real = u.f - sum.real;
    u.f = in_data_d[idx_2d].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.imag = u.f - sum.imag;

    //デバック
    //LOGI("sum = (%g,%g) d = (%g,%g)\n", sum.real, sum.imag, d.real, d.imag);

    n_d.real = n_var * d.real;
    n_d.imag = n_var * d.imag;
    works_d[idx_3d].buf_ns.real = r_var * works_d[idx_3d].buf_s.real;
    works_d[idx_3d].buf_ns.imag = r_var * works_d[idx_3d].buf_s.imag;

    //デバック
    //LOGI("idx = %d buf_ns = (%g,%g)\n", idx, works_d[idx_3d].buf_ns.real, works_d[idx_3d].buf_ns.imag);

    cf_abs = hypot(n_d.real, n_d.imag);
    ry = cf_abs + floor_rand_d();
    v1 = fast_tanh31(ry);
    e1 = ry;
    c1.real = v1 * n_d.real / ry;
    c1.imag = v1 * n_d.imag / ry;

    cf_abs = hypot(works_d[idx_3d].buf_ns.real, works_d[idx_3d].buf_ns.imag);
    rx = cf_abs + floor_rand_d();
    v2 = fast_tanh31(rx);
    e2 = rx;

    // filter update
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real =   c1.real * works_d[idx_3d_base+midx].buf_ns.real + c1.imag * works_d[idx_3d_base+midx].buf_ns.imag;
        f_imag = - c1.real * works_d[idx_3d_base+midx].buf_ns.imag + c1.imag * works_d[idx_3d_base+midx].buf_ns.real;
        works_d[idx_3d_base+i].w.real += works_d[idx_3d_base+i].mu_f * f_real;
        works_d[idx_3d_base+i].w.imag += works_d[idx_3d_base+i].mu_f * f_imag;
    }

    // variance estimation
    n_var += mu_n_d * (1 - v1 * e1) * n_var;
    if (n_var < 0) n_var *= -1;
    
    r_var += mu_s_d * (1 - v2 * e2) * r_var;
    if (r_var < 0) r_var *= -1;
    
    idx = (idx + 1) & mask;

    u.f = d.real;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].real = u.f;
    u.f = d.imag;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].imag = u.f;

    //更新
    sbica_info_d[idx_2d].idx   = idx;
    sbica_info_d[idx_2d].n_var = n_var;
    sbica_info_d[idx_2d].r_var = r_var;
    
    //デバック
    //LOGI("n_var = %g r_var = %g\n", n_var, r_var);
}

#elif defined(SBICA_CUDA_ENABLE) && defined(SBICA_CUDA_CMEM_PARAM)

__global__ void SemiBlindICA_d(const int thread_no, COMPLEX_FLOAT *in_data_d, COMPLEX_FLOAT *out_data_d)
{
    unsigned int nx_2d = c_prms_d.freq;
    unsigned int ny_2d = c_prms_d.ch;
    unsigned int nx_3d = c_prms_d.size;
    unsigned int ny_3d = c_prms_d.freq;
    unsigned int nz_3d = c_prms_d.ch;
    unsigned int ix = threadIdx.x + blockIdx.x * blockDim.x;
    unsigned int iy = threadIdx.y + blockIdx.y * blockDim.y;
    unsigned int idx_2d;
    unsigned int idx_3d;
    unsigned int idx_3d_base;
    unsigned int idx_noise;
    
    union {
        float f;
        struct ieee_single {
            unsigned int mant : 23;
            unsigned int expo : 8;
            unsigned int sign : 1;
        } s;
    } u;

    int i;
    
    int idx;
    int itvl;
    int mask;
    int taps;
    
    float n_var;
    float r_var;

    float f_real;
    float f_imag;
    float cf_abs;

    COMPLEX_FLOAT d, sum, c1, n_d;
    int midx;
    float ry, v1, e1, rx, v2, e2;

    if(ix >= nx_2d || iy >= ny_2d)
        return;

    // 2Dインデックスチェック
    idx_2d = iy * nx_2d + ix;
    if(idx_2d >= (ny_2d * nx_2d)) {
//    if(idx_2d >= (c_prms_d.ch * c_prms_d.freq)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx_2d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx_2d);
        return;
    }

    taps = sbica_info_d[idx_2d].taps;

    idx  = sbica_info_d[idx_2d].idx;
    itvl = sbica_info_d[idx_2d].itvl;
    mask = sbica_info_d[idx_2d].mask;

    n_var = sbica_info_d[idx_2d].n_var;
    r_var = sbica_info_d[idx_2d].r_var;

    // 3Dインデックスチェック
    idx_3d = nx_3d * ny_3d * iy + nx_3d * ix + idx;
    idx_3d_base = nx_3d * ny_3d * iy + nx_3d * ix;
    if(idx_3d >= (nz_3d * ny_3d * nx_3d)) {
//    if(idx_3d >= (c_prms_d.ch * c_prms_d.freq * c_prms_d.size)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx=%d,idx_2d=%d,idx_3d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx, idx_2d, idx_3d);
        return;
    }
    idx_noise = c_prms_d.noise_ch * nx_2d + ix;

    //デバック
    //LOGI("ch = %d f = %d\n", ch, f);
    //LOGI("%d, %d\n", ch, f);
    //LOGI("x = (%g, %g) s = (%g, %g)\n", in_data_d[idx_2d].real, in_data_d[idx_2d].imag, in_data_d[idx_noise].real, in_data_d[idx_noise].imag);
    
    u.f = in_data_d[idx_noise].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    buf_s_d[idx_3d].real = u.f;
    u.f = in_data_d[idx_noise].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    buf_s_d[idx_3d].imag = u.f;

    cf_abs = hypot(buf_s_d[idx_3d].real, buf_s_d[idx_3d].imag);
    
    if(cf_abs < c_prms_d.is_zero_local){
        out_data_d[idx_2d] = in_data_d[idx_2d];
        return;
    }

    sum.real = 0;
    sum.imag = 0;
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real = w_d[idx_3d_base+i].real * buf_s_d[idx_3d_base+midx].real - w_d[idx_3d_base+i].imag * buf_s_d[idx_3d_base+midx].imag;
        f_imag = w_d[idx_3d_base+i].real * buf_s_d[idx_3d_base+midx].imag + w_d[idx_3d_base+i].imag * buf_s_d[idx_3d_base+midx].real;
        sum.real += f_real;
        sum.imag += f_imag;
    }
    u.f = in_data_d[idx_2d].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.real = u.f - sum.real;
    u.f = in_data_d[idx_2d].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.imag = u.f - sum.imag;

    //デバック
    //LOGI("sum = (%g,%g) d = (%g,%g)\n", sum.real, sum.imag, d.real, d.imag);

    n_d.real = n_var * d.real;
    n_d.imag = n_var * d.imag;
    buf_ns_d[idx_3d].real = r_var * buf_s_d[idx_3d].real; 
    buf_ns_d[idx_3d].imag = r_var * buf_s_d[idx_3d].imag; 

    //デバック
    //LOGI("idx = %d buf_ns = (%g,%g)\n", idx, buf_ns_d[idx_3d].real, buf_ns_d[idx_3d].imag);

    cf_abs = hypot(n_d.real, n_d.imag);
    ry = cf_abs + floor_rand_d();
    v1 = fast_tanh31(ry);
    e1 = ry;
    c1.real = v1 * n_d.real / ry;
    c1.imag = v1 * n_d.imag / ry;

    cf_abs = hypot(buf_ns_d[idx_3d].real, buf_ns_d[idx_3d].imag);
    rx = cf_abs + floor_rand_d();
    v2 = fast_tanh31(rx);
    e2 = rx;

    // filter update
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real =   c1.real * buf_ns_d[idx_3d_base+midx].real + c1.imag * buf_ns_d[idx_3d_base+midx].imag;
        f_imag = - c1.real * buf_ns_d[idx_3d_base+midx].imag + c1.imag * buf_ns_d[idx_3d_base+midx].real;
        w_d[idx_3d_base+i].real += mu_f_d[idx_3d_base+i] * f_real;
        w_d[idx_3d_base+i].imag += mu_f_d[idx_3d_base+i] * f_imag;
    }

    // variance estimation
    n_var += mu_n_d * (1 - v1 * e1) * n_var;
    if (n_var < 0) n_var *= -1;
    
    r_var += mu_s_d * (1 - v2 * e2) * r_var;
    if (r_var < 0) r_var *= -1;
    
    idx = (idx + 1) & mask;

    u.f = d.real;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].real = u.f;
    u.f = d.imag;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].imag = u.f;

    //更新
    sbica_info_d[idx_2d].idx   = idx;
    sbica_info_d[idx_2d].n_var = n_var;
    sbica_info_d[idx_2d].r_var = r_var;
    
    //デバック
    //LOGI("n_var = %g r_var = %g\n", n_var, r_var);
}

#elif defined(SBICA_CUDA_ENABLE)

__global__ void SemiBlindICA_d(const int thread_no, COMPLEX_FLOAT *in_data_d, COMPLEX_FLOAT *out_data_d, int ch, int freq, int size, int noise_ch, float is_zero_local)
{
    unsigned int nx_2d = freq;
    unsigned int ny_2d = ch;
    unsigned int nx_3d = size;
    unsigned int ny_3d = freq;
    unsigned int nz_3d = ch;
    unsigned int ix = threadIdx.x + blockIdx.x * blockDim.x;
    unsigned int iy = threadIdx.y + blockIdx.y * blockDim.y;
    unsigned int idx_2d;
    unsigned int idx_3d;
    unsigned int idx_3d_base;
    unsigned int idx_noise;
    
    union {
        float f;
        struct ieee_single {
            unsigned int mant : 23;
            unsigned int expo : 8;
            unsigned int sign : 1;
        } s;
    } u;

    int i;
    
    int idx;
    int itvl;
    int mask;
    int taps;
    
    float n_var;
    float r_var;

    float f_real;
    float f_imag;
    float cf_abs;

    COMPLEX_FLOAT d, sum, c1, n_d;
    int midx;
    float ry, v1, e1, rx, v2, e2;

    if(ix >= nx_2d || iy >= ny_2d)
        return;

    // 2Dインデックスチェック
    idx_2d = iy * nx_2d + ix;
    if(idx_2d >= (ch * freq)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx_2d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx_2d);
        return;
    }

    taps = sbica_info_d[idx_2d].taps;

    idx  = sbica_info_d[idx_2d].idx;
    itvl = sbica_info_d[idx_2d].itvl;
    mask = sbica_info_d[idx_2d].mask;

    n_var = sbica_info_d[idx_2d].n_var;
    r_var = sbica_info_d[idx_2d].r_var;

    // 3Dインデックスチェック
    idx_3d = nx_3d * ny_3d * iy + nx_3d * ix + idx;
    idx_3d_base = nx_3d * ny_3d * iy + nx_3d * ix;
    if(idx_3d >= (ch * freq * size)) {
//      printf("%s: gDim(%d,%d,%d) bDim(%d,%d,%d) bIdx(%d,%d,%d) tIdx(%d,%d,%d) ix=%d,iy=%d,idx=%d,idx_2d=%d,idx_3d=%d <- OVER!!\n", __FUNCTION__,
//              gridDim.x, gridDim.y, gridDim.z, blockDim.x, blockDim.y, blockDim.z,
//              blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z,
//              ix, iy, idx, idx_2d, idx_3d);
        return;
    }
    idx_noise = noise_ch * nx_2d + ix;

    //デバック
    //LOGI("ch = %d f = %d\n", ch, f);
    //LOGI("%d, %d\n", ch, f);
    //LOGI("x = (%g, %g) s = (%g, %g)\n", in_data_d[idx_2d].real, in_data_d[idx_2d].imag, in_data_d[idx_noise].real, in_data_d[idx_noise].imag);
    
    u.f = in_data_d[idx_noise].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    buf_s_d[idx_3d].real = u.f;
    u.f = in_data_d[idx_noise].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    buf_s_d[idx_3d].imag = u.f;

    cf_abs = hypot(buf_s_d[idx_3d].real, buf_s_d[idx_3d].imag);
    
    if(cf_abs < is_zero_local){
        out_data_d[idx_2d] = in_data_d[idx_2d];
        return;
    }

    sum.real = 0;
    sum.imag = 0;
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real = w_d[idx_3d_base+i].real * buf_s_d[idx_3d_base+midx].real - w_d[idx_3d_base+i].imag * buf_s_d[idx_3d_base+midx].imag;
        f_imag = w_d[idx_3d_base+i].real * buf_s_d[idx_3d_base+midx].imag + w_d[idx_3d_base+i].imag * buf_s_d[idx_3d_base+midx].real;
        sum.real += f_real;
        sum.imag += f_imag;
    }
    u.f = in_data_d[idx_2d].real;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.real = u.f - sum.real;
    u.f = in_data_d[idx_2d].imag;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    d.imag = u.f - sum.imag;

    //デバック
    //LOGI("sum = (%g,%g) d = (%g,%g)\n", sum.real, sum.imag, d.real, d.imag);

    n_d.real = n_var * d.real;
    n_d.imag = n_var * d.imag;
    buf_ns_d[idx_3d].real = r_var * buf_s_d[idx_3d].real; 
    buf_ns_d[idx_3d].imag = r_var * buf_s_d[idx_3d].imag; 

    //デバック
    //LOGI("idx = %d buf_ns = (%g,%g)\n", idx, buf_ns_d[idx_3d].real, buf_ns_d[idx_3d].imag);

    cf_abs = hypot(n_d.real, n_d.imag);
    ry = cf_abs + floor_rand_d();
    v1 = fast_tanh31(ry);
    e1 = ry;
    c1.real = v1 * n_d.real / ry;
    c1.imag = v1 * n_d.imag / ry;

    cf_abs = hypot(buf_ns_d[idx_3d].real, buf_ns_d[idx_3d].imag);
    rx = cf_abs + floor_rand_d();
    v2 = fast_tanh31(rx);
    e2 = rx;

    // filter update
    for(i=0; i<taps; i++){
        midx = (idx - i * itvl) & mask;
        f_real =   c1.real * buf_ns_d[idx_3d_base+midx].real + c1.imag * buf_ns_d[idx_3d_base+midx].imag;
        f_imag = - c1.real * buf_ns_d[idx_3d_base+midx].imag + c1.imag * buf_ns_d[idx_3d_base+midx].real;
        w_d[idx_3d_base+i].real += mu_f_d[idx_3d_base+i] * f_real;
        w_d[idx_3d_base+i].imag += mu_f_d[idx_3d_base+i] * f_imag;
    }
    
    // variance estimation
    n_var += mu_n_d * (1 - v1 * e1) * n_var;
    if (n_var < 0) n_var *= -1;
    
    r_var += mu_s_d * (1 - v2 * e2) * r_var;
    if (r_var < 0) r_var *= -1;
    
    idx = (idx + 1) & mask;

    u.f = d.real;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].real = u.f;
    u.f = d.imag;
    u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
    out_data_d[idx_2d].imag = u.f;
    
    //更新
    sbica_info_d[idx_2d].idx   = idx;
    sbica_info_d[idx_2d].n_var = n_var;
    sbica_info_d[idx_2d].r_var = r_var;
    
    //デバック
    //LOGI("n_var = %g r_var = %g\n", n_var, r_var);
}
#endif // SBICA_CUDA_ENABLE

///////////////////////////////////////////////////////////////
//  MAIN
/************************************************************
**  History :
**    2012.12.18 K.Takagi 同じ入力データでg,sum,buf_ns,n_var,r_varの結果を確認
**    2012.12.17 K.Takagi 新規作成
*/
// Public : SemiBlindICA処理
#ifdef SBICA_CUDA_ENABLE
int SemiBlindICA(const int thread_no, int frame_count, MEM_t *out_data_p, MEM_t *in_data_p)
{
#ifdef SBICA_CUDA_CMEM_PARAM
    // no processing
#else
    int nchannel;
    int noise_ch;
    int freq;
    
    float is_zero_local;
    
    union {
        float f;
        struct ieee_single {
            unsigned int mant : 23;
            unsigned int expo : 8;
            unsigned int sign : 1;
        } s;
    } u;

    u.f = semi_blind_ica_info.is_zero;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    is_zero_local = u.f;

    // 入力データの最後のチャネルをノイズとする
    nchannel = semi_blind_ica_info.channel;
    noise_ch = semi_blind_ica_info.channel;
    freq = semi_blind_ica_info.length/2+1;
#endif

#ifdef SAMPLE_FOUT
    {
        int ch;
        int f;
		int nchannel;
		int freq;
        FILE *fp;
        char filename[64];
        COMPLEX_FLOAT **in_data  = (COMPLEX_FLOAT**)in_data_p->data_2d;

        nchannel = semi_blind_ica_info.channel;
        freq = semi_blind_ica_info.length/2+1;

        // 0-10、以降は100毎に出力
        if(frame_count <= 10 || (frame_count <= 10000 && ((frame_count%100)== 0))) {
            // 入力データをファイル出力
            sprintf(filename, "/tmp/SemiBlindICA_in_data_%d.bin", frame_count);
            fp = fopen(filename, "wb");
            fwrite(in_data[0], freq*sizeof(COMPLEX_FLOAT), nchannel+1, fp);
            fclose(fp);

            // サンプル的にデバッグ出力
            sprintf(filename, "/tmp/SemiBlindICA_in_data_%d.txt", frame_count);
            fp = fopen(filename, "wb");
            for(ch=0; ch<=nchannel; ch++){
                for(f=0; f<freq; f++){
                    if(f%64 == 0) {
                        fprintf(fp, "frame[%d] in_data[%d][%d] = %f %f\n", frame_count, ch, f, in_data[ch][f].real, in_data[ch][f].imag);
                    }
                }
            }
            fclose(fp);
        }
    }
#endif

    {
	    //struct timespec req = { 0, 10000 };

        int dimx = 32;
        int dimy = 1;
#ifdef SBICA_CUDA_CMEM_PARAM
        dim3 block(dimx, dimy);
        dim3 grid((c_prms.freq + block.x - 1) / block.x, (c_prms.ch + block.y - 1) / block.y);
#else
        dim3 block(dimx, dimy);
        dim3 grid((freq + block.x - 1) / block.x, (nchannel + block.y - 1) / block.y);
#endif

#ifdef SBICA_CUDA_STREAM
        // デバイスに転送
        CHECK(cudaMemcpyAsync(in_data_p->d_ptr, in_data_p->h_ptr, in_data_p->alllen, cudaMemcpyHostToDevice, stream[thread_no]));
#ifdef SBICA_CUDA_CMEM_PARAM
        SemiBlindICA_d<<<grid, block, 0, stream[thread_no]>>>(thread_no, (COMPLEX_FLOAT *)(in_data_p->d_ptr), (COMPLEX_FLOAT *)(out_data_p->d_ptr));
#else
        SemiBlindICA_d<<<grid, block, 0, stream[thread_no]>>>(thread_no, (COMPLEX_FLOAT *)(in_data_p->d_ptr), (COMPLEX_FLOAT *)(out_data_p->d_ptr), nchannel, freq, 16, noise_ch, is_zero_local);
#endif // SBICA_CUDA_CMEM_PARAM
        CHECK(cudaMemcpyAsync(out_data_p->h_ptr, out_data_p->d_ptr, out_data_p->alllen, cudaMemcpyDeviceToHost, stream[thread_no]));
        while(cudaStreamQuery(stream[thread_no]) != cudaSuccess)
        {
            /*nanosleep(&req, NULL)*/;
        }
#else // SBICA_CUDA_STREAM
        // デバイスに転送
        CHECK(cudaMemcpyAsync(in_data_p->d_ptr, in_data_p->h_ptr, in_data_p->alllen, cudaMemcpyHostToDevice, 0));
        SemiBlindICA_d<<<grid, block>>>(thread_no, (COMPLEX_FLOAT *)(in_data_p->d_ptr), (COMPLEX_FLOAT *)(out_data_p->d_ptr), nchannel, freq, 16, noise_ch, is_zero_local);
        CHECK(cudaMemcpyAsync(out_data_p->h_ptr, out_data_p->d_ptr, out_data_p->alllen, cudaMemcpyDeviceToHost, 0));
        CHECK(cudaDeviceSynchronize());
#endif // SBICA_CUDA_STREAM
    }

#ifdef SAMPLE_FOUT
    {
        int ch;
        int f;
		int nchannel;
		int freq;
        FILE *fp;
        char filename[64];
        COMPLEX_FLOAT **out_data = (COMPLEX_FLOAT**)out_data_p->data_2d;

        nchannel = semi_blind_ica_info.channel;
        freq = semi_blind_ica_info.length/2+1;

        // 0-10、以降は100毎に出力
        if(frame_count <= 10 || (frame_count <= 10000 && ((frame_count%100)== 0))) {
            // 出力データをファイル出力
            sprintf(filename, "/tmp/SemiBlindICA_out_data_%d.bin", frame_count);
            fp = fopen(filename, "wb");
            fwrite(out_data[0], freq*sizeof(COMPLEX_FLOAT), nchannel, fp);
            fclose(fp);

            // サンプル的にデバッグ出力
            sprintf(filename, "/tmp/SemiBlindICA_out_data_%d.txt", frame_count);
            fp = fopen(filename, "wb");
            for(ch=0; ch<nchannel; ch++){
                for(f=0; f<freq; f++){
                    if(f%64 == 0) {
                        fprintf(fp, "frame[%d] out_data[%d][%d] = %f %f\n", frame_count, ch, f, out_data[ch][f].real, out_data[ch][f].imag);
                    }
                }
            }
            fclose(fp);
        }
    }
#endif

    return 0;
}

#else // SBICA_CUDA_ENABLE

int SemiBlindICA(const int thread_no, int frame_count, COMPLEX_FLOAT **out_data, COMPLEX_FLOAT **in_data)
{
    int ch;
    int nchannel;
    int noise_ch;
    int freq;
    int f;
    
    float is_zero_local;
    
    union {
        float f;
        struct ieee_single {
            unsigned int mant : 23;
            unsigned int expo : 8;
            unsigned int sign : 1;
        } s;
    } u;

    u.f = semi_blind_ica_info.is_zero;
    u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
    is_zero_local = u.f;

    // 入力データの最後のチャネルをノイズとする
    nchannel = semi_blind_ica_info.channel;
    noise_ch = semi_blind_ica_info.channel;
    freq = semi_blind_ica_info.length/2+1;

#ifdef SAMPLE_FOUT
    {
        FILE *fp;
        char filename[64];

        // 0-10、以降は100毎に出力
        if(frame_count <= 10 || (frame_count <= 10000 && ((frame_count%100)== 0))) {
            // 入力データをファイル出力
            sprintf(filename, "/tmp/SemiBlindICA_in_data_%d.bin", frame_count);
            fp = fopen(filename, "wb");
            fwrite(in_data[0], freq*sizeof(COMPLEX_FLOAT), nchannel+1, fp);
            fclose(fp);

            // サンプル的にデバッグ出力
            sprintf(filename, "/tmp/SemiBlindICA_in_data_%d.txt", frame_count);
            fp = fopen(filename, "wb");
            for(ch=0; ch<=nchannel; ch++){
                for(f=0; f<freq; f++){
                    if(f%64 == 0) {
                        fprintf(fp, "frame[%d] in_data[%d][%d] = %f %f\n", frame_count, ch, f, in_data[ch][f].real, in_data[ch][f].imag);
                    }
                }
            }
            fclose(fp);
        }
    }
#endif

    for(ch=0; ch<nchannel; ch++){
        for(f=0; f<freq; f++){
            {
                int i;
                
                int idx;
                int itvl;
                int mask;
                int taps;
                
                float n_var;
                float r_var;
    
                float f_real;
                float f_imag;
                float cf_abs;
    
                COMPLEX_FLOAT d, sum, c1, n_d;
                int midx;
                float ry, v1, e1, rx, v2, e2;

                taps = sbica_info[ch][f].taps;

                idx  = sbica_info[ch][f].idx;
                itvl = sbica_info[ch][f].itvl;
                mask = sbica_info[ch][f].mask;

                n_var = sbica_info[ch][f].n_var;
                r_var = sbica_info[ch][f].r_var;

                //デバック
                //LOGI("ch = %d f = %d\n", ch, f);
                //LOGI("%d, %d\n", ch, f);
                //LOGI("x = (%g, %g) s = (%g, %g)\n", in_data[ch][f].real, in_data[ch][f].imag, in_data[noise_ch][f].real, in_data[noise_ch][f].imag);
                
                u.f = in_data[noise_ch][f].real;
                u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
                buf_s[ch][f][idx].real = u.f;
                u.f = in_data[noise_ch][f].imag;
                u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
                buf_s[ch][f][idx].imag = u.f;

                cf_abs = hypot(buf_s[ch][f][idx].real, buf_s[ch][f][idx].imag);
                
                if(cf_abs < is_zero_local){
                    out_data[ch][f] = in_data[ch][f];
                    continue;
                }

                sum.real = 0;
                sum.imag = 0;
                for(i=0; i<taps; i++){
                    midx = (idx - i * itvl) & mask;
                    f_real = w[ch][f][i].real * buf_s[ch][f][midx].real - w[ch][f][i].imag * buf_s[ch][f][midx].imag;
                    f_imag = w[ch][f][i].real * buf_s[ch][f][midx].imag + w[ch][f][i].imag * buf_s[ch][f][midx].real;
                    sum.real += f_real;
                    sum.imag += f_imag;
                }
                u.f = in_data[ch][f].real;
                u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
                d.real = u.f - sum.real;
                u.f = in_data[ch][f].imag;
                u.s.expo = (u.s.expo > 14) ? (u.s.expo - 14) : 0;
                d.imag = u.f - sum.imag;

                //デバック
                //LOGI("sum = (%g,%g) d = (%g,%g)\n", sum.real, sum.imag, d.real, d.imag);

                n_d.real = n_var * d.real;
                n_d.imag = n_var * d.imag;
                buf_ns[ch][f][idx].real = r_var * buf_s[ch][f][idx].real; 
                buf_ns[ch][f][idx].imag = r_var * buf_s[ch][f][idx].imag; 

                //デバック
                //LOGI("idx = %d buf_ns = (%g,%g)\n", idx, buf_ns[ch][f][idx].real, buf_ns[ch][f][idx].imag);

                cf_abs = hypot(n_d.real, n_d.imag);
                ry = cf_abs + floor_rand();
                v1 = fast_tanh31(ry);
                e1 = ry;
                c1.real = v1 * n_d.real / ry;
                c1.imag = v1 * n_d.imag / ry;

                cf_abs = hypot(buf_ns[ch][f][idx].real, buf_ns[ch][f][idx].imag);
                rx = cf_abs + floor_rand();
                v2 = fast_tanh31(rx);
                e2 = rx;

                // filter update
                for(i=0; i<taps; i++){
                    midx = (idx - i * itvl) & mask;
                    f_real =   c1.real * buf_ns[ch][f][midx].real + c1.imag * buf_ns[ch][f][midx].imag;
                    f_imag = - c1.real * buf_ns[ch][f][midx].imag + c1.imag * buf_ns[ch][f][midx].real;
                    w[ch][f][i].real += mu_f[ch][f][i] * f_real;
                    w[ch][f][i].imag += mu_f[ch][f][i] * f_imag;
                }
                
                // variance estimation
                n_var += mu_n * (1 - v1 * e1) * n_var;
                if (n_var < 0) n_var *= -1;
                
                r_var += mu_s * (1 - v2 * e2) * r_var;
                if (r_var < 0) r_var *= -1;
                
                idx = (idx + 1) & mask;

                u.f = d.real;
                u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
                out_data[ch][f].real = u.f;
                u.f = d.imag;
                u.s.expo = (u.s.expo < 240) ? (u.s.expo + 14) : 254;
                out_data[ch][f].imag = u.f;
                
                //更新
                sbica_info[ch][f].idx   = idx;
                sbica_info[ch][f].n_var = n_var;
                sbica_info[ch][f].r_var = r_var;
                
                //デバック
                //LOGI("n_var = %g r_var = %g\n", n_var, r_var);
            }
        }
    }

#ifdef SAMPLE_FOUT
    {
        FILE *fp;
        char filename[64];

        // 0-10、以降は100毎に出力
        if(frame_count <= 10 || (frame_count <= 10000 && ((frame_count%100)== 0))) {
            // 出力データをファイル出力
            sprintf(filename, "/tmp/SemiBlindICA_out_data_%d.bin", frame_count);
            fp = fopen(filename, "wb");
            fwrite(out_data[0], freq*sizeof(COMPLEX_FLOAT), nchannel, fp);
            fclose(fp);

            // サンプル的にデバッグ出力
            sprintf(filename, "/tmp/SemiBlindICA_out_data_%d.txt", frame_count);
            fp = fopen(filename, "wb");
            for(ch=0; ch<nchannel; ch++){
                for(f=0; f<freq; f++){
                    if(f%64 == 0) {
                        fprintf(fp, "frame[%d] out_data[%d][%d] = %f %f\n", frame_count, ch, f, out_data[ch][f].real, out_data[ch][f].imag);
                    }
                }
            }
            fclose(fp);
        }
    }
#endif

    return 0;
}

#endif // SBICA_CUDA_ENABLE

#ifdef STANDALON_TEST
MEM_t fft_data_t[TH_NUM];
MEM_t temp_fft_buffer_t[TH_NUM];

/************************************************************
**  History :
**    2012.12.18 K.Takagi 新規作成
*/
// Public : 単体テストのメイン　固定値を使って結果が同じになることを確認
int main(int argc, char **argv)
{
    int hark_count;
    int stat;
    int channels;
    int ch;
    int f;
    
    int length;

    FILE *fp;
    char filename[64];
    unsigned long sta,end;
    
    COMPLEX_FLOAT **in_data;
    COMPLEX_FLOAT **out_data;
        
    Param_SemiBlindICA_t semi_blind_ica_param;

    /* イベントログ初期化＆有効化 */
    event_log_init();
    event_log_enable();
    
    channels = 8;
    length   = 512;
    
#if 1
    semi_blind_ica_param.channel     = channels;
    semi_blind_ica_param.length      = length;
    semi_blind_ica_param.interval    = 1;
    semi_blind_ica_param.tap_lower   = 16;
    semi_blind_ica_param.tap_upper   = 8;
    semi_blind_ica_param.lambda      = 0.8;
    semi_blind_ica_param.mu_v        = 0.01;
    semi_blind_ica_param.mu_s        = 0.01;
    semi_blind_ica_param.mu_n        = 0.01;
    semi_blind_ica_param.is_zero     = 0.0001;
    semi_blind_ica_param.output_freq = 150;
#else
    semi_blind_ica_param.channel     = channels;
    semi_blind_ica_param.length      = length;
    semi_blind_ica_param.interval    = 1;
    semi_blind_ica_param.tap_lower   = 8;
    semi_blind_ica_param.tap_upper   = 4;
    semi_blind_ica_param.lambda      = 0.8;
    semi_blind_ica_param.mu_v        = 0.01;
    semi_blind_ica_param.mu_s        = 0.01;
    semi_blind_ica_param.mu_n        = 0.01;
    semi_blind_ica_param.is_zero     = 0.001;
    semi_blind_ica_param.output_freq = 150;
#endif
    
    /* パラメータ設定 */
    HASetParamSemiBlindICA(&semi_blind_ica_param);
    
    /* 初期化 */
    stat = HAInitSemiBlindICA(0);
    if(stat < 0){
        printf("Init Error.\n");
        return -1;
    }
    //バッファクリア
    in_data  = NULL;
    out_data = NULL;
#ifdef SBICA_CUDA_ENABLE
    in_data = (COMPLEX_FLOAT **)h_cudaMalloc2D(&temp_fft_buffer_t[0], "temp_fft_t", 1, channels+1, length/2+1, sizeof(COMPLEX_FLOAT));
#else
//    in_data = (COMPLEX_FLOAT **)MemoryAllocate2D(channels+1, length/2+1, sizeof(COMPLEX_FLOAT));
    in_data = (COMPLEX_FLOAT **)h_cudaMalloc2D(&temp_fft_buffer_t[0], "temp_fft_t", 0, channels+1, length/2+1, sizeof(COMPLEX_FLOAT));
#endif
    if(in_data == NULL){
        goto ErrEnd;
    }
#ifdef SBICA_CUDA_ENABLE
    out_data = (COMPLEX_FLOAT **)h_cudaMalloc2D(&fft_data_t[0], "fft_data_t", 1, channels, length/2+1, sizeof(COMPLEX_FLOAT));
#else
//    out_data = (COMPLEX_FLOAT **)MemoryAllocate2D(channels, length/2+1, sizeof(COMPLEX_FLOAT));
    out_data = (COMPLEX_FLOAT **)h_cudaMalloc2D(&fft_data_t[0], "fft_data_t", 0, channels, length/2+1, sizeof(COMPLEX_FLOAT));
#endif
    if(out_data == NULL){
        goto ErrEnd;
    }
    //バッファ初期化（固定値）
    for(ch=0; ch<channels; ch++){
        for(f=0; f<length/2+1; f++){
            in_data[ch][f].real = 2.0;
            in_data[ch][f].imag = 1.1;
        }
    }
    //ノイズ部の初期化
    for(f=0; f<length/2+1; f++){
        in_data[channels][f].real = 0.2;
        in_data[channels][f].imag = 0.11;
    }
    sta = get_us();
    for(hark_count=0; hark_count<1000; hark_count++){
//        LOGI("hark count = %d\n", hark_count);

        EVENT_LOG_PUT(1, hark_count);
//        SemiBlindICA(0, hark_count, out_data, in_data);
        SemiBlindICA(0, hark_count, &fft_data_t[0], &temp_fft_buffer_t[0]);
    }
    end = get_us();
    printf("hark_count %d elasped time %lluus\n", hark_count, end - sta);

#if 1
    // 出力データをファイル出力
    sprintf(filename, "SemiBlindICA_out_data_%d.bin", hark_count);
    fp = fopen(filename, "wb");
    fwrite(out_data[0], (length/2+1)*sizeof(COMPLEX_FLOAT), channels, fp);
    fclose(fp);

    // イベントログをファイル出力
    event_log_fout("event_log.bin");
#endif

ErrEnd:
    if(in_data != NULL){
        h_cudaFree2D(&temp_fft_buffer_t[0]);
//        free(in_data[0]);
//        free(in_data);
    }
    if(out_data != NULL){
        h_cudaFree2D(&fft_data_t[0]);
//        free(out_data[0]);
//        free(out_data);
    }
    HAReleaseSemiBlindICA();
    cudaProfilerStop();
    return 0;
}
#endif // STANDALON_TEST
#endif // BUILD_MODULE_SEMI_BLIND_ICA

/* EOF */

