/*
    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->GetCellByIndex(AddedRowIndex, j-1)->SetValue(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->FPredictionAlgorithm != NULL) {
        delete this->FPredictionAlgorithm;
    }
    this->FPredictionAlgorithm = PredictionAlgorithm;
               
    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->FDefaultValueEnabled) {
        for (int i = 0; i < BufferTable->GetNumberOfRows(); i++) {
            for (int j = 0; j < BufferTable->GetNumberOfColumns(); j++) {
                TCFbECell* Cell = BufferTable->GetCellByIndex(i, j);
                if (!Cell->GetEnabled()) {
                    Cell->SetValue(this->FDefaultValue);
                }
            }
        }
    }

    // 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->GetNumberOfColumns(); j++) {
            double    ICFCoefficient = this->GetICFCoefficient(j);
            for (int i = 0; i < this->FSimilarityComputationTable->GetNumberOfRows(); i++) {
                TCFbECell*    CurrentCell = this->FSimilarityComputationTable->GetCellByIndex(i, j);
                if (CurrentCell->GetEnabled()) {
                    CurrentCell->SetValue(CurrentCell->GetValue() * ICFCoefficient);
                }
            }
        }
    }

    this->FCaseAmplifier = CaseAmplifier;

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

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

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

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

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

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

    this->FAppliedTableArePrepared = true;
}

//---------------------------------------------------------------------------
// TargetRowIndex 行目，TargetColumnIndex 列目のセルの Enabled を一時的に Value にする
// 目標のセルの値を変更したら true, そうでなければ false を返す
bool    __fastcall TCFbEDataSet::TemporaryChangeEnabled(int TargetRowIndex, int TargetColumnIndex, bool Value)
{
    TCFbECell* TargetCell = this->FSourceTable->GetCellByIndex(TargetRowIndex, TargetColumnIndex);
    if (TargetCell->GetEnabled() == Value) {    // 目標のセルが既に Value である
        return false;
    }

    TargetCell->SetEnabled(Value);

    this->FSourceTable->RowIsUpdated(TargetRowIndex);
    this->FSourceTable->ColumnIsUpdated(TargetColumnIndex);
    
    if(this->FAppliedTableArePrepared) {

        // 対象セルが含まれる行・列の normalize のやり直し
        TCFbETable* BufferTable = new TCFbETable();

        // Value / StandardizedValue / NormalizedValue / Order の準備
        switch (this->FValueBase) {
            case vbStandardizedValue:
            case vbNormalizedValue:
            case vbOrder:
                BufferTable->CopyColumnFrom(this->FSourceTable, TargetColumnIndex, true);
                if (this->FValueBase == vbStandardizedValue) {
                    BufferTable->PartialStandardize(0, true);
                } else if (this->FValueBase == vbNormalizedValue) {
                    BufferTable->PartialNormalize(0, true);
                } else if (this->FValueBase == vbOrder) {
                    BufferTable->PartialOrdering(0, true);
                }

                // Default Value の設定
                if (this->FDefaultValueEnabled) {
                    for (int i = 0; i < BufferTable->GetNumberOfRows(); i++) {
                        if (!BufferTable->GetCellByIndex(i, 0)->GetEnabled() && (i != TargetRowIndex)) {
                            BufferTable->GetCellByIndex(i, 0)->SetValue(this->FDefaultValue);
                        }
                    }
                }

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

                break;

            case vbDoubleStandardizedValue:
            case vbDoubleNormalizedValue:
            case vbDoubleOrder:
                this->FPredictionMediateTable->CopyColumnFrom(this->FSourceTable, TargetColumnIndex, true);
                if (this->FValueBase == vbDoubleStandardizedValue) {
                    this->FPredictionMediateTable->PartialStandardize(TargetColumnIndex, true);
                } else if (this->FValueBase == vbDoubleNormalizedValue) {
                    this->FPredictionMediateTable->PartialNormalize(TargetColumnIndex, true);
                } else if (this->FValueBase == vbDoubleOrder) {
                    this->FPredictionMediateTable->PartialOrdering(TargetColumnIndex, true);
                }

                BufferTable->Assign(this->FPredictionMediateTable);

                if (this->FValueBase == vbDoubleStandardizedValue) {
                    BufferTable->RowStandardize();
                } else if (this->FValueBase == vbDoubleNormalizedValue) {
                    BufferTable->RowNormalize();
                } else if (this->FValueBase == vbDoubleOrder) {
                    BufferTable->RowOrdering();
                }

                // Default Value の設定
                if (this->FDefaultValueEnabled) {
                    for (int i = 0; i < BufferTable->GetNumberOfRows(); i++) {
                        for (int j = 0; j < BufferTable->GetNumberOfColumns(); j++) {
                            TCFbECell* Cell = BufferTable->GetCellByIndex(i, j);
                            if (!Cell->GetEnabled()) {
                                Cell->SetValue(this->FDefaultValue);
                            }
                        }
                    }
                }

                // 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);
                }

                break;
            default:
                if (this->FAlgorithmBase == abRow) {    // User-based CF
                    this->FSimilarityComputationTable->GetCellByIndex(TargetRowIndex, TargetColumnIndex)->SetEnabled(Value);
                    this->FSimilarityComputationTable->RowIsUpdated(TargetRowIndex);
                    this->FSimilarityComputationTable->ColumnIsUpdated(TargetColumnIndex);
                    this->FPredictionTable->GetCellByIndex(TargetRowIndex, TargetColumnIndex)->SetEnabled(Value);
                    this->FPredictionTable->RowIsUpdated(TargetRowIndex);
                    this->FPredictionTable->ColumnIsUpdated(TargetColumnIndex);
                } else {
                    this->FSimilarityComputationTable->GetCellByIndex(TargetColumnIndex, TargetRowIndex)->SetEnabled(Value);
                    this->FSimilarityComputationTable->RowIsUpdated(TargetColumnIndex);
                    this->FSimilarityComputationTable->ColumnIsUpdated(TargetRowIndex);
                    this->FPredictionTable->GetCellByIndex(TargetColumnIndex, TargetRowIndex)->SetEnabled(Value);
                    this->FPredictionTable->RowIsUpdated(TargetColumnIndex);
                    this->FPredictionTable->ColumnIsUpdated(TargetRowIndex);
                }
                break;
        }

        // Inverse Case Frequency
        if (this->FInverseCaseFrequencyEnabled) {
            for (int j = 0; j < this->FSimilarityComputationTable->GetNumberOfColumns(); j++) {
                double    ICFCoefficient = this->GetICFCoefficient(j);
                for (int i = 0; i < this->FSimilarityComputationTable->GetNumberOfRows(); i++) {
                    TCFbECell*    CurrentCell = this->FSimilarityComputationTable->GetCellByIndex(i, j);
                    if (CurrentCell->GetEnabled()) {
                        CurrentCell->SetValue(CurrentCell->GetValue() * ICFCoefficient);
                    }
                }
            }
        }

        delete BufferTable;
    }
    
    return true;
}

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

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

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

//---------------------------------------------------------------------------
// SourceDataSet の SourceRowIndex 行目をコピーする
int __fastcall TCFbEDataSet::CopyRowFrom(TCFbEDataSet* SourceDataSet, int SourceRowIndex, bool AddColumn)
{
    return this->FSourceTable->CopyRowFrom(SourceDataSet->FSourceTable, SourceRowIndex, AddColumn);
}

//---------------------------------------------------------------------------
// 行を追加する．
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);
}

//---------------------------------------------------------------------------
// 列を追加する．
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);
}

//---------------------------------------------------------------------------
// 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->FSimilarityComputationTable->GetNumberOfRows() / (double)EnabledCount);
}

//---------------------------------------------------------------------------
// TargetRowIndex に対する NearestNeigobors を調べる
// TargetRowIndex: 類似度を調べる対象の行
// TargetColumnIndex: 予測対象の列（TargetColumnIndex の値が Enabled でない行は Neighbors に含めない 
// NeighborList に Neigobors を類似度が高い順に入れて返す
// 返された TObjectList は GetNearestNeighbors を呼んだクラスが削除する必要がある
void __fastcall TCFbEDataSet::GetNearestNeighbors(int TargetRowIndex, int TargetColumnIndex, int NeighborhoodSize, TObjectList* NeighborList)
{
    if (NeighborhoodSize == 0) {
        NeighborhoodSize = MaxInt;
    }

    int NumberOfRows = this->FSimilarityComputationTable->GetNumberOfRows();

    for (int i = 0; i < NumberOfRows; i++) {
        TCFbECell*    CurrentPredictionCell = this->FPredictionTable->GetCellByIndex(i, TargetColumnIndex);
        if (CurrentPredictionCell->GetEnabled() && (i != TargetRowIndex)) {
            double    Similarity = this->GetSimilarityByIndex(TargetRowIndex, i);

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

            if (k < NeighborhoodSize) {
                if (NeighborList->Count >= NeighborhoodSize) {
                    NeighborList->Delete(NeighborList->Count - 1);
                }
                TCFbECell*    CurrentSourceCell = (this->FAlgorithmBase == abRow) ? this->FSourceTable->GetCellByIndex(i, TargetColumnIndex) : this->FSourceTable->GetCellByIndex(TargetColumnIndex, i);
                double    Value = CurrentSourceCell->GetEnabled() ? CurrentSourceCell->GetValue() : this->FDefaultValue;
                NeighborList->Insert(k, new TCFbENeighbor(this->FPredictionTable->GetRowLabelList()->Strings[i], Similarity, CurrentPredictionCell->GetValue(), Value));
            }
        }
    }
}

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

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

    for (int j = 0; j < this->GetColumnLabelList()->Count; j++) {
        if (TargetDataSet->GetColumnLabelList()->IndexOf(this->GetColumnLabelList()->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->FAlgorithmBase == abColumn) {    // Item-based
        this->FPredictionTable->GetSortedCellListOfRow(ColumnIndex, SortedList);
    } else {    // User-based
        this->FPredictionTable->GetSortedCellListOfColumn(ColumnIndex, SortedList);
    }

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

    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->FAlgorithmBase == abColumn) {    // Item-based
        this->FPredictionTable->GetSortedCellListOfColumn(RowIndex, AppliedSortedList);
        this->FPredictionMediateTable->GetSortedCellListOfColumn(RowIndex, MediateSortedList);
    } else {    // User-based
        this->FPredictionTable->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])->GetValue();
    }

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

    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::GetSimilarityByIndex(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])->SetEnabled(false);
        }
    }

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

        CachedSimilarity->SetValue(this->FSimilarityComputationAlgorithm->ComputeSimilarity(this->FSimilarityComputationTable, TargetRowIndex, ComparedRowIndex));

        if (this->FCaseAmplifier != 1.0) {
            if (CachedSimilarity->GetValue() >= 0.0) {
                CachedSimilarity->SetValue(pow(CachedSimilarity->GetValue(), this->FCaseAmplifier));
            } else {
                CachedSimilarity->SetValue(-1.0 * Power(-1.0 * CachedSimilarity->GetValue(), this->FCaseAmplifier));
            }
        }
    }

    return CachedSimilarity->GetValue();
}

//---------------------------------------------------------------------------
double __fastcall TCFbEDataSet::GetPredictedValueByIndex(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->GetNumberOfColumns(); i++) {
            ((TCFbECell*)this->FPredictedValueList->Items[i])->SetEnabled(false);
        }
    }

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

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

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

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

    return CachedPredictedValue->GetValue();
}

//---------------------------------------------------------------------------
// CSV形式の文字列をTStringsに分解する
// このメソッドを高速化すれば，CSV データの読み込みを早くできる
// TStrings を使わない実装が考えられないか（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);
}

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


