/*
    TCFbEDataSet.cpp    June 6, 2004.

    Copyright (C) 2003-2004 CFbE Research Group,
    Software Engineering Laboratory,
    Graduate School of Information Science,
    Nara Institute of Science and Technology,
    All rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, (at your option) or
    any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GNU Emacs; see the file COPYING.  If not, write to the
    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/
//---------------------------------------------------------------------------
#pragma hdrstop

#define BUFFERLEN    256

#include "TCFbEDataSet.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)            
//---------------------------------------------------------------------------
// コンストラクタ
__fastcall TCFbEDataSet::TCFbEDataSet(void)
{
    this->FValueBase = vbUnknown;
    this->FAlgorithmBase = abUnknown;
    this->FSourceTable = new TCFbETable();
    this->FSimilarityComputationTable = new TCFbETable();
    this->FPredictionMediateTable = new TCFbETable();
    this->FPredictionTable = new TCFbETable();
    this->FAppliedTableArePrepared = false;
    this->FSimilarityList = new TObjectList();
    this->FSimilarityComputationAlgorithm = NULL;
    this->FPredictionAlgorithm = NULL;
    this->FNeighborhoodSize = -1;
    this->TargetRowIndexOfSimilarity = -1;
    this->FPredictedValueList = new TObjectList();
    this->TargetRowIndexOfPrediction = -1;

    this->TemporaryUnableRowIndex = -1;
    this->TemporaryUnableColumnIndex = -1;
}

//---------------------------------------------------------------------------
// コンストラクタ
// SourceCsvStringList からデータを構築する
// Row の 1 行目にレコードのラベルが
// Column の 1 行目にアイテムのラベルが
// 後は値が入っているとみなす
__fastcall TCFbEDataSet::TCFbEDataSet(TStringList* SourceCsvStringList)
{
    this->FValueBase = vbUnknown;
    this->FAlgorithmBase = abUnknown;
    this->FSourceTable = new TCFbETable();
    this->FSimilarityComputationTable = new TCFbETable();
    this->FSimilarityComputationTable = new TCFbETable();
    this->FPredictionMediateTable = new TCFbETable();
    this->FPredictionTable = new TCFbETable();
    this->FAppliedTableArePrepared = false;
    this->FSimilarityList = new TObjectList();
    this->FSimilarityComputationAlgorithm = NULL;
    this->FPredictionAlgorithm = NULL;
    this->FNeighborhoodSize = -1;
    this->TargetRowIndexOfSimilarity = -1;
    this->FPredictedValueList = new TObjectList();
    this->TargetRowIndexOfPrediction = -1;

    this->TemporaryUnableRowIndex = -1;
    this->TemporaryUnableColumnIndex = -1;
    
    // 空のデータセット
    if (SourceCsvStringList->Count == 0) {
        return;
    }

    // Column ラベルの取得
    TStringList* Delimited = new TStringList();
    this->CsvToStrings2(SourceCsvStringList->Strings[0].c_str(), Delimited);
    for (int j = 1; j < Delimited->Count; j++) {
        this->FSourceTable->AddColumn(Delimited->Strings[j]);
    }

    // row の追加
    for (int i = 1; i < SourceCsvStringList->Count; i++) {
        Delimited->Clear();
        this->CsvToStrings2(SourceCsvStringList->Strings[i].c_str(), Delimited);
        if (Delimited->Count == 0) {
            continue;
        }

        int AddedRowIndex = this->FSourceTable->AddRow(Delimited->Strings[0]);

        for (int j = 1; j < Delimited->Count; j++) {
            if (Delimited->Strings[j].IsEmpty()) {
                continue;
            }
            
            try {
                this->FSourceTable->CellsByIndex[AddedRowIndex][j-1]->Value = Delimited->Strings[j].ToDouble();
            } catch (EConvertError &e) {
                // 型変換できない
            }
        }
    }

    delete Delimited;
}

//---------------------------------------------------------------------------
__fastcall TCFbEDataSet::~TCFbEDataSet()    // デストラクタ
{
    delete this->FPredictedValueList;
    delete this->FSimilarityList;
    if (this->FPredictionAlgorithm != NULL) {
        delete this->FPredictionAlgorithm;
    }
    if (this->FSimilarityComputationAlgorithm != NULL) {
        delete this->FSimilarityComputationAlgorithm;
    }
    delete this->FPredictionTable;
    delete this->FPredictionMediateTable;
    delete this->FSimilarityComputationTable;
    delete this->FSourceTable;
}

//---------------------------------------------------------------------------
//    CF の準備を行う（PredictionTable を準備する
//    ValueBase:    計算に使う値（Value / StandardizedValue / NormalizedValue / Order
//    AlgorithmBase:    Row(User)-based か，Column(Item)-based か
//    SimilarityComputationAlgorithm:    類似度計算アルゴリズム（TCFbEDataSet が解放する
//    PredictionAlgorithm:    予測アルゴリズム（TCFbEDataSet が解放する
//    NeighborhoodSize:    ネイバーフッドサイズ
//  DefaultValueEnabled:    Default Value 埋め込み機能を有効にするかどうか
//    DefaultValue:    Default Value 埋め込み機能が有効なときに埋め込む値
//    InverseCaseFrequencyEnabled:    Inverse Case Frequency 機能を有効にするかどうか
//    CaseAmplifier:    Case Amplification 機能を使用するときの amplifier（使用しないときは 1.0 を指定）
void __fastcall TCFbEDataSet::PrepareCF(TCFbEValueBase ValueBase, TCFbEAlgorithmBase AlgorithmBase, TCFbESimilarityComputationAlgorithm* SimilarityComputationAlgorithm, TCFbEPredictionAlgorithm* PredictionAlgorithm, int NeighborhoodSize, bool DefaultValueEnabled, double DefaultValue, bool InverseCaseFrequencyEnabled, double CaseAmplifier)
{
    this->IsUpdated();

    if (this->FSimilarityComputationAlgorithm != NULL) {
        delete this->FSimilarityComputationAlgorithm;
    }
    this->FSimilarityComputationAlgorithm = SimilarityComputationAlgorithm;
    if (this->FSimilarityComputationAlgorithm != NULL) {
        this->FSimilarityComputationAlgorithm->SetDataSet(this);
    }

    if (this->FPredictionAlgorithm != NULL) {
        delete this->FPredictionAlgorithm;
    }
    this->FPredictionAlgorithm = PredictionAlgorithm;
    if (this->FPredictionAlgorithm != NULL) {
        this->FPredictionAlgorithm->SetDataSet(this);
    }

    this->FNeighborhoodSize = NeighborhoodSize;

    this->FAlgorithmBase = AlgorithmBase;
    this->FValueBase = ValueBase;

    TCFbETable* BufferTable = new TCFbETable();

    // Value / StandardizedValue / NormalizedValue / Order の準備
    if (this->FValueBase == vbStandardizedValue) {
        BufferTable->Assign(this->FSourceTable);
        BufferTable->Standardize();

    } else if (this->FValueBase == vbNormalizedValue) {
        BufferTable->Assign(this->FSourceTable);
        BufferTable->Normalize();

    } else if (this->FValueBase == vbOrder) {
        BufferTable->Assign(this->FSourceTable);
        BufferTable->Ordering();

    } else if (this->FValueBase == vbDoubleStandardizedValue) {
        this->FPredictionMediateTable->Assign(this->FSourceTable);
        this->FPredictionMediateTable->Standardize();
        BufferTable->Assign(this->FPredictionMediateTable);
        BufferTable->RowStandardize();

    } else if (this->FValueBase == vbDoubleNormalizedValue) {
        this->FPredictionMediateTable->Assign(this->FSourceTable);
        this->FPredictionMediateTable->Normalize();
        BufferTable->Assign(this->FPredictionMediateTable);
        BufferTable->RowNormalize();

    } else if (this->FValueBase == vbDoubleOrder) {
        this->FPredictionMediateTable->Assign(this->FSourceTable);
        this->FPredictionMediateTable->Ordering();
        BufferTable->Assign(this->FPredictionMediateTable);
        BufferTable->RowOrdering();
        
    } else {
        BufferTable->Assign(this->FSourceTable);
    }

    // Default Value の設定
    this->FDefaultValueEnabled = DefaultValueEnabled;
    this->FDefaultValue = DefaultValue;
    if (this->DefaultValueEnabled) {
        for (int i = 0; i < BufferTable->NumberOfRows; i++) {
            for (int j = 0; j < BufferTable->NumberOfColumns; j++) {
                TCFbECell* Cell = BufferTable->CellsByIndex[i][j];
                if (!Cell->Enabled) {
                    Cell->Value = this->DefaultValue;
                }
            }
        }
    }

    // SimilarityComputationTable, PredictionTable の準備
    if (this->FAlgorithmBase == abRow) {    // User-based CF
        this->FSimilarityComputationTable->Assign(BufferTable);
        this->FPredictionTable->Assign(BufferTable);
    } else {    // Item-based CF
        this->FSimilarityComputationTable->AssignWithTurn(BufferTable);
        this->FPredictionTable->AssignWithTurn(BufferTable);
    }

    // Inverse Case Frequency
    this->FInverseCaseFrequencyEnabled = InverseCaseFrequencyEnabled;
    if (this->FInverseCaseFrequencyEnabled) {
        for (int j = 0; j < this->FSimilarityComputationTable->NumberOfColumns; j++) {
            double    ICFCoefficient = this->GetICFCoefficient(j);
            for (int i = 0; i < this->FSimilarityComputationTable->NumberOfRows; i++) {
                TCFbECell*    CurrentCell = this->FSimilarityComputationTable->CellsByIndex[i][j];
                if (CurrentCell->Enabled) {
                    CurrentCell->Value *= ICFCoefficient;
                }
            }
        }
    }

    this->FCaseAmplifier = CaseAmplifier;

    // Similarities キャッシュの初期化／追加
    this->TargetRowIndexOfSimilarity = -1;

    for (int i = 0; i < this->FPredictionTable->NumberOfRows; i++) {
        if (this->FSimilarityList->Count <= i) {
            this->FSimilarityList->Add(new TCFbECell());
        }
    }

    // 過剰な Similarities キャッシュの削除
    while (this->FSimilarityList->Count > this->FPredictionTable->NumberOfRows) {
        this->FSimilarityList->Delete(0);
    }

    // Predictions キャッシュの初期化／追加
    this->TargetRowIndexOfPrediction = -1;

    for (int j = 0; j < this->FSourceTable->NumberOfColumns; j++) {
        if (this->FPredictedValueList->Count <= j) {
            this->FPredictedValueList->Add(new TCFbECell());
        }
    }

    // 過剰な Predictions キャッシュの削除
    while (this->FPredictedValueList->Count > this->FSourceTable->NumberOfColumns) {
        this->FPredictedValueList->Delete(0);
    }

    this->FAppliedTableArePrepared = true;
}

//---------------------------------------------------------------------------
// TargetRowIndex, TargetColumnIndex のセルを一時的に Enabled = false にする
// 既に一時的に Enabled = false にしているセルがある場合は，まずそれを戻してから行う
void    __fastcall TCFbEDataSet::TemporaryUnable(int TargetRowIndex, int TargetColumnIndex)
{
    if ((this->TemporaryUnableRowIndex >= 0) && (this->TemporaryUnableColumnIndex >= 0)) {
        this->RecoverEnabled();
    }

    TCFbECell* TargetCell = this->FSourceTable->CellsByIndex[TargetRowIndex][TargetColumnIndex];
    if (!TargetCell->Enabled) {    // 目標のセルが Enabled ではない
        return;
    }

    this->TemporaryUnableRowIndex = TargetRowIndex;
    this->TemporaryUnableColumnIndex = TargetColumnIndex;
    
    TargetCell->Enabled = false;

    if(this->FAppliedTableArePrepared) {
        if (this->AlgorithmBase == abColumn) {
            this->SimilarityComputationTable->CellsByIndex[TargetColumnIndex][TargetRowIndex]->Enabled = false;
            this->PredictionTable->CellsByIndex[TargetColumnIndex][TargetRowIndex]->Enabled = false;
        } else {
            this->SimilarityComputationTable->CellsByIndex[TargetRowIndex][TargetColumnIndex]->Enabled = false;
            this->PredictionTable->CellsByIndex[TargetRowIndex][TargetColumnIndex]->Enabled = false;
        }
    }
}

//---------------------------------------------------------------------------
// 一時的に Enabled = false にしていたセルを元にもどす
void    __fastcall TCFbEDataSet::RecoverEnabled(void)
{
    if ((this->TemporaryUnableRowIndex < 0) && (this->TemporaryUnableColumnIndex < 0)) {
        return;
    }

    this->FSourceTable->CellsByIndex[this->TemporaryUnableRowIndex][this->TemporaryUnableColumnIndex]->Enabled = true;

    if(this->FAppliedTableArePrepared) {
        if (this->AlgorithmBase == abColumn) {
            this->SimilarityComputationTable->CellsByIndex[this->TemporaryUnableColumnIndex][this->TemporaryUnableRowIndex]->Enabled = true;
            this->PredictionTable->CellsByIndex[this->TemporaryUnableColumnIndex][this->TemporaryUnableRowIndex]->Enabled = true;
        } else {
            this->SimilarityComputationTable->CellsByIndex[this->TemporaryUnableRowIndex][this->TemporaryUnableColumnIndex]->Enabled = true;
            this->PredictionTable->CellsByIndex[this->TemporaryUnableRowIndex][this->TemporaryUnableColumnIndex]->Enabled = true;
        }
    }

    this->TemporaryUnableRowIndex = -1;
    this->TemporaryUnableColumnIndex = -1;
}

//---------------------------------------------------------------------------
// RowLabel, ColumnLabel のセルの値に Value を加算する
void __fastcall TCFbEDataSet::AddValue(AnsiString RowLabel, AnsiString ColumnLabel, double Value)
{
    this->IsUpdated();
    this->FSourceTable->AddValue(RowLabel, ColumnLabel, Value);
}

//---------------------------------------------------------------------------
// RowLabel, ColumnLabel のセルの値を Value に更新する
void __fastcall TCFbEDataSet::UpdateValue(AnsiString RowLabel, AnsiString ColumnLabel, double Value)
{
    this->IsUpdated();
    this->FSourceTable->UpdateValue(RowLabel, ColumnLabel, Value);
}

//---------------------------------------------------------------------------
// RowLabel, ColumnLabel のセルを削除する（Enabled = false にする
void __fastcall TCFbEDataSet::DeleteValue(AnsiString RowLabel, AnsiString ColumnLabel)
{
    this->IsUpdated();
    this->FSourceTable->DeleteValue(RowLabel, ColumnLabel);
}

//---------------------------------------------------------------------------
// DataSet の RowIndex 行目を更新する
int __fastcall TCFbEDataSet::UpdateRow(TCFbEDataSet* DataSet, int RowIndex)
{
    int UpdatedIndex = this->RowLabelList->IndexOf(DataSet->RowLabelList->Strings[RowIndex]);
    if (UpdatedIndex < 0) {    // 行が存在しない
        UpdatedIndex = this->AddRow(DataSet->RowLabelList->Strings[RowIndex]);
    }
    for (int j = 0; j < DataSet->NumberOfColumns; j++) {
        if (DataSet->EnabledByIndex[RowIndex][j]) {
            int    TargetColumn = this->ColumnLabelList->IndexOf(DataSet->ColumnLabelList->Strings[j]);
            if (TargetColumn != -1) {
                this->ValuesByIndex[UpdatedIndex][TargetColumn] = DataSet->ValuesByIndex[RowIndex][j];
            }
        }
    }

    return UpdatedIndex;
}

//---------------------------------------------------------------------------
// 行を追加する．
int __fastcall TCFbEDataSet::AddRow(AnsiString RowLabel)
{
    this->IsUpdated();
    return this->FSourceTable->AddRow(RowLabel);
}

//---------------------------------------------------------------------------
// 行を削除する．
void __fastcall TCFbEDataSet::DeleteRow(AnsiString RowLabel)
{
    this->IsUpdated();
    this->FSourceTable->DeleteRow(RowLabel);
}

//---------------------------------------------------------------------------
// 行の各要素を加算する．要素は ColumnLabelList で指定された順に Values に格納される．
void __fastcall TCFbEDataSet::AddRowElements(AnsiString RowLabel, TStringList* ColumnLabelList, TDoubleDynArray& Values)
{
    this->IsUpdated();
    this->FSourceTable->AddRowElements(RowLabel, ColumnLabelList, Values);
}

//---------------------------------------------------------------------------
// 行の各要素を更新する．要素は ColumnLabelList で指定された順に Values に格納される．
void __fastcall TCFbEDataSet::UpdateRowElements(AnsiString RowLabel, TStringList* ColumnLabelList, TDoubleDynArray& Values)
{
    this->IsUpdated();
    this->FSourceTable->UpdateRowElements(RowLabel, ColumnLabelList, Values);
}

//---------------------------------------------------------------------------
// 行の指定要素の Enabled を false にする．要素は ColumnLabelList で指定される．行自体の削除は行わない．
void __fastcall TCFbEDataSet::DeleteRowElements(AnsiString RowLabel, TStringList* ColumnLabelList)
{
    this->IsUpdated();
    this->FSourceTable->DeleteRowElements(RowLabel, ColumnLabelList);
}

//---------------------------------------------------------------------------
// 列を追加する．
int __fastcall TCFbEDataSet::AddColumn(AnsiString ColumnLabel)
{
    this->IsUpdated();
    return this->FSourceTable->AddColumn(ColumnLabel);
}

//---------------------------------------------------------------------------
// 列を削除する．
void __fastcall TCFbEDataSet::DeleteColumn(AnsiString ColumnLabel)
{
    this->IsUpdated();
    this->FSourceTable->DeleteColumn(ColumnLabel);
}

//---------------------------------------------------------------------------
// 列の各要素を加算する．要素は RowLabelList で指定された順に Values に格納される．
void __fastcall TCFbEDataSet::AddColumnElements(AnsiString ColumnLabel, TStringList* RowLabelList, TDoubleDynArray& Values)
{
    this->IsUpdated();
    this->FSourceTable->AddColumnElements(ColumnLabel, RowLabelList, Values);
}

//---------------------------------------------------------------------------
// 列の各要素を更新する．要素は RowLabelList で指定された順に Values に格納される．
void __fastcall TCFbEDataSet::UpdateColumnElements(AnsiString ColumnLabel, TStringList* RowLabelList, TDoubleDynArray& Values)
{
    this->IsUpdated();
    this->FSourceTable->UpdateColumnElements(ColumnLabel, RowLabelList, Values);
}

//---------------------------------------------------------------------------
// 列の指定要素の Enabled を false にする．要素は RowLabelList で指定される．列自体の削除は行わない．
void __fastcall TCFbEDataSet::DeleteColumnElements(AnsiString ColumnLabel, TStringList* RowLabelList)
{
    this->IsUpdated();
    this->FSourceTable->DeleteColumnElements(ColumnLabel, RowLabelList);
}

//---------------------------------------------------------------------------
// j 列目の Inverse Case Frequency Coefficient を得る
double __fastcall TCFbEDataSet::GetICFCoefficient(int j)
{
    int    EnabledCount = (this->FAlgorithmBase == abRow) ? this->FSourceTable->GetEnabledCountOfColumn(j) : this->FSourceTable->GetEnabledCountOfRow(j);
    if (EnabledCount == 0) {
        return 0.0;
    }
    return log((double)this->SimilarityComputationTable->NumberOfRows / (double)EnabledCount);
}

//---------------------------------------------------------------------------
// TargetRowIndex に対する NearestNeigobors を調べる
// TargetRowIndex: 類似度を調べる対象の行
// TargetColumnIndex: 予測対象の列（TargetColumnIndex の値が Enabled でない行は Neighbors に含めない 
// NeighborList に Neigobors を類似度が高い順に入れて返す
void __fastcall TCFbEDataSet::GetNearestNeighbors(int TargetRowIndex, int TargetColumnIndex, int NeighborhoodSize, TObjectList* NeighborList)
{
    NeighborList->Clear();
    if (NeighborhoodSize == 0) {
        NeighborhoodSize = MaxInt;
    }

    int NumberOfRows = this->FSimilarityComputationTable->NumberOfRows;

    for (int i = 0; i < NumberOfRows; i++) {
        TCFbECell*    CurrentPredictionCell = this->FPredictionTable->CellsByIndex[i][TargetColumnIndex];
        if (CurrentPredictionCell->Enabled && (i != TargetRowIndex)) {
            double    Similarity = this->SimilaritiesByIndex[TargetRowIndex][i];

            int k = 0;
            while (k < NeighborList->Count) {
                if ( ((TCFbENeighbor*)NeighborList->Items[k])->Similarity < Similarity) {
                    break;
                }
                k++;
            }

            if (k < NeighborhoodSize) {
                if (NeighborList->Count >= NeighborhoodSize) {
                    NeighborList->Delete(NeighborList->Count - 1);
                }
                TCFbECell*    CurrentSourceCell = (this->AlgorithmBase == abRow) ? this->FSourceTable->CellsByIndex[i][TargetColumnIndex] : this->FSourceTable->CellsByIndex[TargetColumnIndex][i];
                double    Value = CurrentSourceCell->Enabled ? CurrentSourceCell->Value : this->DefaultValue;
                NeighborList->Insert(k, new TCFbENeighbor(this->FPredictionTable->RowLabelList->Strings[i], Similarity, CurrentPredictionCell->Value, Value));
            }
        }
    }    
}

//---------------------------------------------------------------------------
// BaseDataSet にあって，this にない列を新たな列として追加する
void __fastcall TCFbEDataSet::FillColumns(TCFbEDataSet* BaseDataSet)
{
    for (int j = 0; j < BaseDataSet->NumberOfColumns; j++) {
        if (this->ColumnLabelList->IndexOf(BaseDataSet->ColumnLabelList->Strings[j]) == -1) {
            this->AddColumn(BaseDataSet->ColumnLabelList->Strings[j]);
        }
    }
}

//---------------------------------------------------------------------------
// TargetDataSet と首尾一貫性があるかどうかを調べる
bool __fastcall TCFbEDataSet::IsConsistentWith(TCFbEDataSet* TargetDataSet)
{
    if (this->NumberOfColumns != TargetDataSet->NumberOfColumns) {
        return false;
    }

    for (int j = 0; j < this->ColumnLabelList->Count; j++) {
        if (TargetDataSet->ColumnLabelList->IndexOf(this->ColumnLabelList->Strings[j]) == -1) {
            return false;
        }
    }
    return true;
}

//---------------------------------------------------------------------------
// ColumnIndex 列目の Normalize された値 NormalizedValue を DisNormalize して返す
double __fastcall TCFbEDataSet::DisNormalize(int ColumnIndex, double NormalizedValue)
{
    return this->FSourceTable->DisNormalize(ColumnIndex, NormalizedValue);
}

//---------------------------------------------------------------------------
// ColumnIndex 列目の Standardize された値 StandardizedValue を DisStandardize して返す
double __fastcall TCFbEDataSet::DisStandardize(int ColumnIndex, double StandardizedValue)
{
    return this->FSourceTable->DisStandardize(ColumnIndex, StandardizedValue);
}

//---------------------------------------------------------------------------
// ColumnIndex 列目の Ordering された値 Order を DisOrdering して返す
double __fastcall TCFbEDataSet::DisOrdering(int ColumnIndex, double Order)
{
    TList*    SortedList = new TList();
    if (this->AlgorithmBase == abColumn) {    // Item-based
        this->PredictionTable->GetSortedCellListOfRow(ColumnIndex, SortedList);
    } else {    // User-based
        this->PredictionTable->GetSortedCellListOfColumn(ColumnIndex, SortedList);
    }

    TDoubleDynArray Array;
    Array.Length = SortedList->Count;
    for (int k = 0; k < SortedList->Count; k++) {
        Array[k] = ((TCFbECell*)SortedList->Items[k])->Value;
    }

    double ResultedValue = this->FSourceTable->DisOrdering(ColumnIndex, Order, Array);
    delete SortedList;
    Array.Length = 0;
    
    return ResultedValue;
}

//---------------------------------------------------------------------------
// RowIndex 行目，ColumnIndex 列目の DoubleNormalize された値 DoubleNormalizedValue を DisDoubleNormalize して返す
double __fastcall TCFbEDataSet::DisDoubleNormalize(int RowIndex, int ColumnIndex, double DoubleNormalizedValue)
{
    return this->DisNormalize(ColumnIndex, this->FPredictionMediateTable->DisRowNormalize(RowIndex, DoubleNormalizedValue));
}

//---------------------------------------------------------------------------
// RowIndex 行目，ColumnIndex 列目の DoubleStandardize された値 DoubleStandardizedValue を DisDoubleStandardize して返す
double __fastcall TCFbEDataSet::DisDoubleStandardize(int RowIndex, int ColumnIndex, double DoubleStandardizedValue)
{
    return this->DisStandardize(ColumnIndex, this->FPredictionMediateTable->DisRowStandardize(RowIndex, DoubleStandardizedValue));
}

//---------------------------------------------------------------------------
// RowIndex 行目，ColumnIndex 列目の DoubleOrdering された値 DoubleOrder を DisDoubleOrdering して返す
double __fastcall TCFbEDataSet::DisDoubleOrdering(int RowIndex, int ColumnIndex, double DoubleOrder)
{
    TList*    AppliedSortedList = new TList();
    TList*    MediateSortedList = new TList();
    if (this->AlgorithmBase == abColumn) {    // Item-based
        this->PredictionTable->GetSortedCellListOfColumn(RowIndex, AppliedSortedList);
        this->FPredictionMediateTable->GetSortedCellListOfColumn(RowIndex, MediateSortedList);
    } else {    // User-based
        this->PredictionTable->GetSortedCellListOfRow(RowIndex, AppliedSortedList);
        this->FPredictionMediateTable->GetSortedCellListOfColumn(ColumnIndex, MediateSortedList);
    }

    TDoubleDynArray AppliedArray;
    AppliedArray.Length = AppliedSortedList->Count;
    for (int k = 0; k < AppliedSortedList->Count; k++) {
        AppliedArray[k] = ((TCFbECell*)AppliedSortedList->Items[k])->Value;
    }

    TDoubleDynArray MediateArray;
    MediateArray.Length = MediateSortedList->Count;
    for (int k = 0; k < MediateSortedList->Count; k++) {
        MediateArray[k] = ((TCFbECell*)MediateSortedList->Items[k])->Value;
    }

    double ResultedValue = this->FSourceTable->DisOrdering(ColumnIndex, this->FPredictionMediateTable->DisRowOrdering(RowIndex, DoubleOrder, AppliedArray), MediateArray);

    delete MediateSortedList;
    delete AppliedSortedList;
    MediateArray.Length = 0;
    AppliedArray.Length = 0;
    
    return ResultedValue;
}

//---------------------------------------------------------------------------
// 以下，protected メソッドの定義
//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetValues(AnsiString RowLabel, AnsiString ColumnLabel)
{
    return this->FSourceTable->Cells[RowLabel][ColumnLabel]->Value;
}

//---------------------------------------------------------------------------
void __fastcall TCFbEDataSet::SetValues(AnsiString RowLabel, AnsiString ColumnLabel, double Value)
{
    this->IsUpdated();
    this->FSourceTable->CellsByIndex[this->FSourceTable->RowIsUpdated(RowLabel)][this->FSourceTable->ColumnIsUpdated(ColumnLabel)]->Value = Value;
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetValuesByIndex(int RowIndex, int ColumnIndex)
{
    return this->FSourceTable->CellsByIndex[RowIndex][ColumnIndex]->Value;
}

//---------------------------------------------------------------------------
void __fastcall TCFbEDataSet::SetValuesByIndex(int RowIndex, int ColumnIndex, double Value)
{
    this->IsUpdated();
    this->FSourceTable->RowIsUpdated(RowIndex);
    this->FSourceTable->ColumnIsUpdated(ColumnIndex);
    this->FSourceTable->CellsByIndex[RowIndex][ColumnIndex]->Value = Value;
}

//---------------------------------------------------------------------------
bool __fastcall TCFbEDataSet::GetEnabled(AnsiString RowLabel, AnsiString ColumnLabel)
{
    return this->FSourceTable->Cells[RowLabel][ColumnLabel]->Enabled;
}

//---------------------------------------------------------------------------
void __fastcall TCFbEDataSet::SetEnabled(AnsiString RowLabel, AnsiString ColumnLabel, bool Value)
{
    this->IsUpdated();
    this->FSourceTable->CellsByIndex[this->FSourceTable->RowIsUpdated(RowLabel)][this->FSourceTable->ColumnIsUpdated(ColumnLabel)]->Enabled = Value;
}

//---------------------------------------------------------------------------
bool __fastcall TCFbEDataSet::GetEnabledByIndex(int RowIndex, int ColumnIndex)
{
    return this->FSourceTable->CellsByIndex[RowIndex][ColumnIndex]->Enabled;
}

//---------------------------------------------------------------------------
void __fastcall TCFbEDataSet::SetEnabledByIndex(int RowIndex, int ColumnIndex, bool Value)
{
    this->IsUpdated();
    this->FSourceTable->RowIsUpdated(RowIndex);
    this->FSourceTable->ColumnIsUpdated(ColumnIndex);
    this->FSourceTable->CellsByIndex[RowIndex][ColumnIndex]->Enabled = Value;
}

//---------------------------------------------------------------------------
int __fastcall TCFbEDataSet::GetNumberOfRows(void)
{
    return this->FSourceTable->NumberOfRows;
}

//---------------------------------------------------------------------------
int __fastcall TCFbEDataSet::GetNumberOfColumns(void)
{
    return this->FSourceTable->NumberOfColumns;
}

//---------------------------------------------------------------------------
TStringList* __fastcall TCFbEDataSet::GetRowLabelList(void)
{
    return this->FSourceTable->RowLabelList;
}

//---------------------------------------------------------------------------
TStringList* __fastcall TCFbEDataSet::GetColumnLabelList(void)
{
    return this->FSourceTable->ColumnLabelList;
}

//---------------------------------------------------------------------------
TCFbETable* __fastcall TCFbEDataSet::GetSimilarityComputationTable(void)
{
    if (!this->FAppliedTableArePrepared) {
        Exception("AppliedValues are not prepared.");
    }
    return this->FSimilarityComputationTable;
}

//---------------------------------------------------------------------------
TCFbETable* __fastcall TCFbEDataSet::GetPredictionTable(void)
{
    if (!this->FAppliedTableArePrepared) {
        Exception("AppliedValues are not prepared.");
    }
    return this->FPredictionTable;
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetSimilarities(AnsiString TargetRowLabel, AnsiString ComparedRowLabel)
{
    int TargetRowIndex = this->RowLabelList->IndexOf(TargetRowLabel);
    int ComparedRowIndex = this->RowLabelList->IndexOf(ComparedRowLabel);
    return this->GetSimilaritiesByIndex(TargetRowIndex, ComparedRowIndex);
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetSimilaritiesByIndex(int TargetRowIndex, int ComparedRowIndex)
{
    if (!this->FAppliedTableArePrepared) {
        Exception("AppliedValues were not preapared (in TCFbEDataSet::GetSimilaritiesByIndex).");
    }

    // キャッシュをクリア
    if (this->TargetRowIndexOfSimilarity != TargetRowIndex) {
        for (int i = 0; i < this->FSimilarityList->Count; i++) {
            ((TCFbECell*)this->FSimilarityList->Items[i])->Enabled = false;
        }
    }

    TCFbECell* CachedSimilarity = (TCFbECell*)this->FSimilarityList->Items[ComparedRowIndex];
    if (!CachedSimilarity->Enabled) {
        this->TargetRowIndexOfSimilarity = TargetRowIndex;

        CachedSimilarity->Value = this->FSimilarityComputationAlgorithm->ComputeSimilarity(TargetRowIndex, ComparedRowIndex);

        if (this->CaseAmplifier != 1.0) {
            if (CachedSimilarity->Value >= 0.0) {
                CachedSimilarity->Value = pow(CachedSimilarity->Value, this->CaseAmplifier);
            } else {
                CachedSimilarity->Value = -1.0 * Power(-1.0 * CachedSimilarity->Value, this->CaseAmplifier);
            }
        }
    }

    return CachedSimilarity->Value;
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetPredictedValues(AnsiString TargetRowLabel, AnsiString TargetColumnLabel)
{
    int TargetRowIndex = this->RowLabelList->IndexOf(TargetRowLabel);
    int TargetColumnIndex = this->ColumnLabelList->IndexOf(TargetColumnLabel);
    return this->GetPredictedValuesByIndex(TargetRowIndex, TargetColumnIndex);
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetPredictedValuesByIndex(int TargetRowIndex, int TargetColumnIndex)
{
    if (!this->FAppliedTableArePrepared) {
        Exception("AppliedValues were not preapared (in TCFbEDataSet::GetPredictedValuesByIndex).");
    }

    // キャッシュをクリア
    if (this->TargetRowIndexOfPrediction != TargetRowIndex) {
        for (int i = 0; i < this->NumberOfColumns; i++) {
            ((TCFbECell*)this->FPredictedValueList->Items[i])->Enabled = false;
        }
    }

    TCFbECell* CachedPredictedValue = (TCFbECell*)this->FPredictedValueList->Items[TargetColumnIndex];

    if (!CachedPredictedValue->Enabled) {
        this->TargetRowIndexOfPrediction = TargetRowIndex;

        double ResultValue;
        if (this->FAlgorithmBase == abColumn) {    // アイテムベースなら予測対象の行と列を入替えて実行
            ResultValue = this->FPredictionAlgorithm->Predict(TargetColumnIndex, TargetRowIndex, this->NeighborhoodSize);
        } else {
            ResultValue = this->FPredictionAlgorithm->Predict(TargetRowIndex, TargetColumnIndex, this->NeighborhoodSize);
        }

        switch (this->ValueBase) {
            case vbStandardizedValue:
                CachedPredictedValue->Value = this->DisStandardize(TargetColumnIndex, ResultValue);
                break;
            case vbNormalizedValue:
                CachedPredictedValue->Value = this->DisNormalize(TargetColumnIndex, ResultValue);
                break;
            case vbOrder:
                CachedPredictedValue->Value = this->DisOrdering(TargetColumnIndex, ResultValue);
                break;
            case vbDoubleStandardizedValue:
                CachedPredictedValue->Value = this->DisDoubleStandardize(TargetRowIndex, TargetColumnIndex, ResultValue);
                break;
            case vbDoubleNormalizedValue:
                CachedPredictedValue->Value = this->DisDoubleNormalize(TargetRowIndex, TargetColumnIndex, ResultValue);
                break;
            case vbDoubleOrder:
                CachedPredictedValue->Value = this->DisDoubleOrdering(TargetRowIndex, TargetColumnIndex, ResultValue);
                break;
            default:
                CachedPredictedValue->Value = ResultValue;
        }
    }

    return CachedPredictedValue->Value;
}

//---------------------------------------------------------------------------
// アップデートされたことをマークし，キャッシュをクリアする
void __fastcall TCFbEDataSet::IsUpdated(void)
{
    this->FAppliedTableArePrepared = false;
    this->TargetRowIndexOfSimilarity = -1;
    this->TargetRowIndexOfPrediction = -1;
}

//---------------------------------------------------------------------------
// CSV形式の文字列をTStringsに分解する
void __fastcall TCFbEDataSet::CsvToStrings2(char* str, TStrings *List)
{
    AnsiString    Buffer = "";
    char    tmp[BUFFERLEN];
    bool    quote = false;     // 引用中か

    int    strIndex = 0;
    int    tmpIndex = 0;

    while (str[strIndex] != '\0') {
        if((str[strIndex] == '\"') || (str[strIndex] == '\'')){    //引用符
            quote =!quote;
        } else if(!quote && (str[strIndex] == ',')) {    //区切り文字
            tmp[tmpIndex] = '\0';
            Buffer += AnsiString(tmp);
            List->Add(Buffer);
            Buffer = "";
            tmpIndex = 0;
        } else if(!quote && isspace(str[strIndex])) {    //空白(必要かな？)
            ;
        } else {    //その他
            tmp[tmpIndex++] = str[strIndex];
            if (tmpIndex + 1 > BUFFERLEN) {
                tmp[tmpIndex] = '\0';
                Buffer += AnsiString(tmp);
            }
        }
        strIndex++;
    }

    tmp[tmpIndex] = '\0';
    Buffer += AnsiString(tmp);
    List->Add(Buffer);
}

//---------------------------------------------------------------------------

