/*
    TCFbEOutputCash.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

#include "TCFbEOutputCash.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ
__fastcall TCFbEOutputCash::TCFbEOutputCash(TCFbECommandLineParser* Parser, TCFbEDataLoader* DataLoader, bool IsVerbose)
{                                    
    // 変数の初期化
    this->Parser = Parser;
    this->DataLoader = DataLoader;
    this->FResultTable = new TCFbERowColumnCachedTable();
    this->FNeighborsList = new TObjectList();
    this->FDistributionList = new TObjectList();

    try {
        if (IsVerbose) {
            fprintf(stderr, "(3/4) Estimation is started with the following algorithms.\n");
            fprintf(stderr, "  %s\n", Parser->SimilarityComputationName);
            fprintf(stderr, "  %s\n", Parser->PredictionName);
            if (Parser->NeighborsSize > 0) {
                fprintf(stderr, "  ( Neighbors size is %d )\n", Parser->NeighborsSize);
            } else {
                fprintf(stderr, "  ( All neighbors are used )\n");
            }

            fprintf(stderr, "    ..... completed:  0.00\%");
        }

        TCFbEToolkit::GetToolkit()->SetApplicationPriority(tpLower);

        // ResultTable の準備
        for (int j = 0; j < this->DataLoader->EstimatingData->GetNumberOfColumns(); j++) {
            this->FResultTable->AddColumn(this->DataLoader->EstimatingData->GetColumnLabelList()->Strings[j].c_str());
        }
        for (int i = 0; i < this->DataLoader->EstimatingData->GetNumberOfRows(); i++) {
            this->FResultTable->AddRow(this->DataLoader->EstimatingData->GetRowLabelList()->Strings[i].c_str());
        }

        // 予測すべき列の Cell を Enabled = true にする
        if (Parser->TargetColumnLabelList->Count > 0) {
            if (Parser->TargetColumnLabelList->Text.Trim() != "*") {    // 指定列を予測
                for (int i = 0; i < this->DataLoader->EstimatingData->GetNumberOfRows(); i++) {
                    for (int k = 0; k < Parser->TargetColumnLabelList->Count; k++) {
                        int j = this->FResultTable->GetColumnLabelList()->IndexOf(Parser->TargetColumnLabelList->Strings[k]);
                        this->FResultTable->SetValueByIndex(i, j, 0.0);
                    }
                }
            } else {    // 全ての列が予測対象
                for (int i = 0; i < this->DataLoader->EstimatingData->GetNumberOfRows(); i++) {
                    for (int j = 0; j < this->DataLoader->EstimatingData->GetNumberOfColumns(); j++) {
                        this->FResultTable->SetValueByIndex(i, j, 0.0);
                    }
                }
            }
        } else if (Parser->TargetColumnLabelList->Count == 0) {    // Estimating Data の Enabled = false の列を予測
            for (int i = 0; i < this->DataLoader->EstimatingData->GetNumberOfRows(); i++) {
                for (int j = 0; j < this->DataLoader->EstimatingData->GetColumnLabelList()->Count; j++) {
                    if (!this->DataLoader->EstimatingData->GetEnabledByIndex(i, j)) {
                        this->FResultTable->SetValueByIndex(i, j, 0.0);
                    }
                }
            }
        }

        // 予測の開始
        this->DataLoader->LearningData->PrepareCF(Parser->ValueBase, Parser->AlgorithmBase, Parser->GetSimilarityComputationAlgorithm(), Parser->GetPredictionAlgorithm(), Parser->NeighborsSize, Parser->DefaultValueEnabled, Parser->DefaultValue, Parser->InverseCaseFrequencyEnabled, Parser->CaseAmplifier);

        for (int i = 0; i < this->FResultTable->GetNumberOfRows(); i++) {
            int    TargetRowIndex;
            if (this->DataLoader->EstimatingData == this->DataLoader->LearningData) {
                TargetRowIndex = i;
            } else {
                TargetRowIndex = this->DataLoader->LearningData->CopyRowFrom(this->DataLoader->EstimatingData, i, false);
            }

            for (int j = 0; j < this->FResultTable->GetNumberOfColumns(); j++) {
                TCFbECell* Result = this->FResultTable->GetCellByIndex(i, j);
                if (Result->GetEnabled()) {
                    int TargetColumnIndex = this->DataLoader->LearningData->GetColumnLabelList()->IndexOf(this->FResultTable->GetColumnLabelList()->Strings[j]);
                    this->DataLoader->LearningData->TemporallyUnable(TargetRowIndex, TargetColumnIndex);    // 予測対象のメトリクスを一時的に Enabled = false にする

                    try {
                        Result->SetValue(this->DataLoader->LearningData->GetPredictedValueByIndex(TargetRowIndex, TargetColumnIndex));
                        Result->SetRank(1.0);    // 成功のときは Rank = 1.0 とする
                    } catch (...) {
                        Result->SetEnabled(false);
                        Result->SetRank(0.0);    // 失敗のときは Rank = 0.0 とする
                    }

                    this->DataLoader->LearningData->RecoverEnabled();

                    // NearestNeighbors と類似度の分布を調べる（調べるべき最初の Column についてだけ調べる
                    if (this->FNeighborsList->Count <= i) {
                        TObjectList*    NeighborList = new TObjectList;
                        TList*          Distribution = new TList();
                        try {
                            if (Parser->AlgorithmBase == abColumn) {    // アイテムベースのとき
                                this->DataLoader->LearningData->GetNearestNeighbors(TargetColumnIndex, TargetRowIndex, Parser->NeighborsSize, NeighborList);
                                this->CountDistribution(this->DataLoader->LearningData, TargetColumnIndex, Distribution);
                            } else {
                                this->DataLoader->LearningData->GetNearestNeighbors(TargetRowIndex, TargetColumnIndex, Parser->NeighborsSize, NeighborList);
                                this->CountDistribution(this->DataLoader->LearningData, TargetRowIndex, Distribution);
                            }
                        } catch (...) {    // 失敗したのであきらめる
                            delete Distribution;
                            delete NeighborList;
                        }
                        this->FNeighborsList->Add(NeighborList);
                        this->FDistributionList->Add(Distribution);
                    }
                }
            }

            if (this->DataLoader->EstimatingData != this->DataLoader->LearningData) {
                this->DataLoader->LearningData->DeleteRow(this->DataLoader->LearningData->GetRowLabelList()->Strings[TargetRowIndex].c_str());
            }

            if (IsVerbose) {
                fprintf(stderr, "\b\b\b\b\b\b%5.2f\%", (double)i / (double)this->DataLoader->EstimatingData->GetNumberOfRows() * 100.0);
            }
        }

        // j 列目の値が予測されたかどうかを調べておく
        this->FIsResultedColumn.Length = this->FResultTable->GetNumberOfColumns();
        for (int j = 0; j < this->FResultTable->GetNumberOfColumns(); j++) {
            this->FIsResultedColumn[j] = false;
            for (int i = 0; i < this->FResultTable->GetNumberOfRows(); i++) {
                if (this->FResultTable->GetCellByIndex(i, j)->GetEnabled()) {
                    this->FIsResultedColumn[j] = true;
                    break;
                }
            }
        }

        TCFbEToolkit::GetToolkit()->SetApplicationPriority(tpNormal);

        if (IsVerbose) {
            fprintf(stderr, "\b\b\b\b\b\b100.00\%\n\n");
        }

    } catch (...) {
        delete this->FResultTable;
        delete this->FNeighborsList;
        delete this->FDistributionList;
        throw;
    }

    return;
}

//---------------------------------------------------------------------------
__fastcall TCFbEOutputCash::~TCFbEOutputCash()
{
    this->FIsResultedColumn.Length = 0;
    delete this->FDistributionList;
    delete this->FNeighborsList;
    delete this->FResultTable;
}

//---------------------------------------------------------------------------
AnsiString __fastcall TCFbEOutputCash::GetResultRowLabel(int RowIndex)
{
    return this->FResultTable->GetRowLabelList()->Strings[RowIndex];
}

//---------------------------------------------------------------------------
AnsiString __fastcall TCFbEOutputCash::GetResultColumnLabel(int ColumnIndex)
{
    return this->FResultTable->GetColumnLabelList()->Strings[ColumnIndex];
}

//---------------------------------------------------------------------------
// 以下，protected メソッドの定義
//---------------------------------------------------------------------------
// DataSet における TargetRowIndex 行目の行に対する類似度の分布を StoredList に格納して返す
void __fastcall TCFbEOutputCash::CountDistribution(TCFbEDataSetEx* DataSet, int TargetRowIndex, TList* StoredList)
{
    StoredList->Clear();
    for (int k = 0; k < 20; k++) {
        StoredList->Add((void*)0);
    }

    double MaxSimilarity = 0.0;
    double MinSimilarity = 0.0;
    bool First = true;
    for (int i = 0; i < DataSet->GetSimilarityComputationTable()->GetNumberOfRows(); i++) {
        if (i != TargetRowIndex) {
            double Similarity = DataSet->GetSimilarityByIndex(TargetRowIndex, i);
            if (First || (Similarity > MaxSimilarity)) {
                MaxSimilarity = Similarity;
            }
            if (First || (Similarity < MinSimilarity)) {
                MinSimilarity = Similarity;
                First = false;
            }
        }
    }

    if ((MaxSimilarity > 1.0) || (MinSimilarity < -1.0)) {
        for (int i = 0; i < DataSet->GetSimilarityComputationTable()->GetNumberOfRows(); i++) {
            if (i != TargetRowIndex) {
                double NormalizedSimilarity = (DataSet->GetSimilarityByIndex(TargetRowIndex, i) - MinSimilarity) / (MaxSimilarity - MinSimilarity);
                int Index = Floor(NormalizedSimilarity * 20.0 + 0.5);
                if (Index >= 20) {
                    Index = 19;
                }
                StoredList->Items[Index] = (void*)(1 + (int)StoredList->Items[Index]);
            }
        }
    } else {
        for (int i = 0; i < DataSet->GetSimilarityComputationTable()->GetNumberOfRows(); i++) {
            if (i != TargetRowIndex) {
                int Index = Floor((DataSet->GetSimilarityByIndex(TargetRowIndex, i) + 1.0) * 10.0);
                if (Index >= 20) {
                    Index = 19;
                }
                StoredList->Items[Index] = (void*)(1 + (int)StoredList->Items[Index]);
            }
        }
    }


    return;
}

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

