//#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>

#define FILE_SAMPLE "sample.txt"    // データファイル
#define FILE_TEST   "test.txt"      // データファイル
#define DAT_N (500)                 // 1行のデータ個数

// データ
typedef struct
{
    double      a_data[DAT_N];  // a[]
    char        a_fname[_MAX_PATH];
    int         b_lines;
    char **     b_fname;
    double **   b_data;         // b[][]
    double      c_data[DAT_N];  // c[]
} ALL_DATA;

// Error コード
enum ERR_CODE
{
    EDR_Normal = 0  , // 正常終了
    EDR_FileOpen    , // ファイルが開けない
    EDR_FewData     , // データが足りない
    EDR_MemAlloc    , // メモリ確保失敗
    EDR_Zero        , // 要素が全てゼロ
};
char *getStrERR_CODE(int code)
{
    switch(code)
    {
    case EDR_Normal      :return "正常終了";
    case EDR_FileOpen    :return "ファイルが開けない";
    case EDR_FewData     :return "データが足りない";
    case EDR_MemAlloc    :return "メモリ確保失敗";
    case EDR_Zero        :return "要素が全てゼロ";
    }
    return "";
}

void        aldat_init(ALL_DATA *d);                          // ALL_DATA初期化
ERR_CODE    aldat_alloc(ALL_DATA *d, int lines);              // ALL_DATA確保
void        aldat_free(ALL_DATA *d);                          // ALL_DATA解放
ERR_CODE    data_read(char *file1, char *file2, ALL_DATA *d); // データ読み込み
char *      f2s_n(double d, int n);                           // 有効数字n桁小数文字列生成

#ifdef _DEBUG
  #define debug_out(x) debug_file_out(x)
  void debug_file_out(ALL_DATA *d);
#else
  #define debug_out(x)
#endif

//---------------------------------------------------------------------------
// ALL_DATA初期化
//---------------------------------------------------------------------------
void aldat_init(ALL_DATA *d)
{
    if(NULL == d)
    {
        return;
    }
    memset(d, 0x00, sizeof(ALL_DATA));
}

//---------------------------------------------------------------------------
// ALL_DATA確保
// 戻り値が EDR_Normal 以外の時は、当関数でfreeしているので呼び元でfreeしなくて良い
//---------------------------------------------------------------------------
ERR_CODE aldat_alloc(ALL_DATA *d, int lines)
{
    ERR_CODE  r = EDR_Normal;
    int i;

    // b の行数
    d->b_lines = lines;
    // b
    if(NULL == (d->b_data = (double **)malloc(sizeof(double *) * lines)))
    {
        r = EDR_MemAlloc;
        goto END_aldat_alloc;
    }
    memset(d->b_data, 0x00, sizeof(double *) * lines);
    // b[]
    if(NULL == (d->b_fname = (char **)malloc(sizeof(char *) * lines)))
    {
        r = EDR_MemAlloc;
        goto END_aldat_alloc;
    }
    memset(d->b_data, 0x00, sizeof(char *) * lines);
    // b[][] と b[]のfilename
    for(i=0; i<lines; i++)
    {
        if(NULL == (d->b_data[i] = (double *)malloc(sizeof(double) * DAT_N)))
        {
            r = EDR_MemAlloc;
            goto END_aldat_alloc;
        }
        if(NULL == (d->b_fname[i] = (char *)malloc(sizeof(char) * _MAX_PATH)))
        {
            r = EDR_MemAlloc;
            goto END_aldat_alloc;
        }
    }

    r = EDR_Normal;
END_aldat_alloc:;
    if(EDR_Normal != r)
    {
        aldat_free(d);
    }
    return r;
}

//---------------------------------------------------------------------------
// ALL_DATA解放
//---------------------------------------------------------------------------
void aldat_free(ALL_DATA *d)
{
    int i;

    if(NULL == d)
    {
        return;
    }
    for(i=0; i < d->b_lines; i++)
    {
        if(NULL == d->b_data[i] && NULL == d->b_fname[i])
        {
            break;
        }
        if(d->b_data[i])
        {
            free(d->b_data[i]);
        }
        if(d->b_fname[i])
        {
            free(d->b_fname[i]);
        }
    }
    free(d->b_data);
    free(d->b_fname);
    aldat_init(d);
}

//---------------------------------------------------------------------------
// 関数     = データ読み込み
// 引数     = file1  : データファイル名 (sample.txt)
//            file2  : データファイル名 (test.txt)
//            d      : データ
// 戻り値   = enum ERR_CODE;
// 注意     = ・dは呼び元で用意すること
//            ・メモリ初期化/確保とエラー時の解放はは当関数でするが、
//              使用後の解放は呼び元ですること
//---------------------------------------------------------------------------
ERR_CODE data_read(char *file1, char *file2, ALL_DATA *d)
{
    ERR_CODE res = EDR_Normal; // 戻り値
    FILE *fp = NULL;        // ファイル
    char *buf = NULL;       // 1行読み込みバッファ
    int buf_size;           //   〃  サイズ
    int i, j, line;         // 汎用
    char *token;            // 汎用

    // 初期化
    aldat_init(d);

    // buf のサイズ (大きい方のファイルのサイズ)
    if (NULL == (fp = fopen(file1, "rb")))
    {
        res = EDR_FileOpen;
        goto END_DATAREAD;
    }
    fseek(fp, 0, SEEK_END);
    i = (int)ftell(fp) + 1;
    fclose(fp);
    if (NULL == (fp = fopen(file2, "rb")))
    {
        res = EDR_FileOpen;
        goto END_DATAREAD;
    }
    fseek(fp, 0, SEEK_END);
    j = (int)ftell(fp) + 1;
    fclose(fp);
    fp = NULL;
    buf_size = (j > i) ? j : i;
    // buff 確保
    if(NULL == (buf = (char*)malloc(buf_size)))
    {
        res = EDR_MemAlloc;
        goto END_DATAREAD;
    }

    // test.txt を空読みしてデータ数(行数)を数える
    fp = fopen(file2, "r");
    for(line = 0; NULL != fgets(buf, buf_size, fp); line++);
    if(line < 1)
    {
        res = EDR_FewData;
        goto END_DATAREAD;
    }
    fclose(fp);
    fp = NULL;
    // データ領域確保
    if(EDR_Normal != (res = aldat_alloc(d, line)))
    {
        goto END_DATAREAD;
    }

    // sample.txt
    fp = fopen(file1, "r");
    fgets(buf, buf_size, fp);
    // filename
    if(NULL == (token = strtok(buf, "\t")))
    {
        res = EDR_FewData;
        goto END_DATAREAD;
    }
    strncpy(d->a_fname, token, _MAX_PATH);
    // data
    for (j = 0; j < DAT_N; j++) {
        token = strtok(NULL, "\t");
        if (NULL == token) {
            res = EDR_FewData;
            goto END_DATAREAD;
        }
        d->a_data[j] = atof(token);
    }
    fclose(fp);
    fp = NULL;

    // test.txt
    fp = fopen(file2, "r");
    for(i = 0; i < d->b_lines; i++)
    {
        fgets(buf, buf_size, fp);
        // filename
        if(NULL == (token = strtok(buf, "\t")))
        {
            res = EDR_FewData;
            goto END_DATAREAD;
        }
        strncpy(d->b_fname[i], token, _MAX_PATH);
        // data
        for (j = 0; j < DAT_N; j++) {
            token = strtok(NULL, "\t");
            if (NULL == token) {
                res = EDR_FewData;
                goto END_DATAREAD;
            }
            d->b_data[i][j] = atof(token);
        }
    }
    fclose(fp);
    fp = NULL;

    res = EDR_Normal;
END_DATAREAD:;
    if(res != EDR_Normal)
    {
        aldat_free(d);
    }
    if(buf)
    {
        free(buf);
        buf = NULL;
    }
    buf_size = 0;
    if(fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return res;
}

//---------------------------------------------------------------------------
// 関数 = 有効数字n桁小数文字列生成
// 動作 = 例えば n == 6 のとき、
//              0.0             → "0"
//              0.123456789     → "0.123457"
//              0.000123456789  → "0.000123457"
//              0.0120000       → "0.012"
//          を返す
// メモ = ・n < 16
//        ・未完成
//---------------------------------------------------------------------------
char *f2s_n(double d, int n)
{
    static char b[32];

    if(d == 0.0)
    {
        return "0";
    }
    sprintf(b, "%.*f", n, d);

    return &b[0];
}

//---------------------------------------------------------------------------
//      データ出力 (debug)
//---------------------------------------------------------------------------
#ifdef _DEBUG
void debug_file_out(ALL_DATA *d)
{
    int i, j;
    FILE *fp;

    fp = fopen("dbg_s.txt", "w");
    fprintf(fp, "%s\t", d->a_fname);
    for(j = 0; j < DAT_N; j++)
    {
        fprintf(fp, "%s\t", f2s_n(d->a_data[j], 6));
    }
    fprintf(fp, "\n");
    fclose(fp);
    fp = fopen("dbg_t.txt", "w");
    for(i = 0; i < d->b_lines; i++)
    {
        fprintf(fp, "%s\t", d->b_fname[i]);
        for(j = 0; j < DAT_N; j++)
        {
            fprintf(fp, "%s\t", f2s_n(d->b_data[i][j], 6));
        }
        fprintf(fp, "\n");
    }
    fclose(fp);
}
#endif

//---------------------------------------------------------------------------
//      main
//---------------------------------------------------------------------------
int main()
{
    int result = 0;
    ALL_DATA data;
    ERR_CODE res;
    int i, j;
    double v1,v2,v3;

    // 初期化
    aldat_init(&data);

    // read
    if(EDR_Normal != (res = data_read(FILE_SAMPLE, FILE_TEST, &data)))
    {
        result = (int)res;
        goto END_MAIN;
    }

    // (debugモード時) 読んだデータを確認のためファイル dbg_s.txt, dbg_t.txt に書き出す
    debug_out(&data);

    // 計算
    for(i=0; i < data.b_lines; i++)
    {
        v1 = v2 = v3 = 0.0;
        for(j=0; j< DAT_N; j++)
        {
            v1 += data.a_data[j] * data.b_data[i][j];
            v2 += data.a_data[j] * data.a_data[j];
            v3 += data.b_data[i][j] * data.b_data[i][j];
        }
        if(v2 == 0.0 || v3 == 0.0)
        {
            fprintf(stderr, "%d行目、", i + 1);
            result = EDR_Zero;
            goto END_MAIN;
        }
        data.c_data[i] = v1 / (sqrt(v2) * sqrt(v3));
    }

    // 結果
    for(i=0; i < data.b_lines; i++)
    {
        printf("%s\t%s\n", data.b_fname[i], f2s_n(data.c_data[i], 15));
    }

    // end
    result = (int)EDR_Normal;
END_MAIN:;
    aldat_free(&data);
    if(EDR_Normal != result)
    {
        fprintf(stderr, "%s\n", getStrERR_CODE(result));
    }
    return result;
}
