/*
    TCFbECustomTableEx.cpp    December 4, 2004.

    Copyright (C) 2003-2005 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 "TCFbECustomTableEx.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ（空キャッシュを作成
__fastcall TCFbECustomTableEx::TCFbECustomTableEx(void)
    : TCFbERowColumnCachedTable()
{
}

//---------------------------------------------------------------------------
// 指定された行列を備えた空テーブルを作成
__fastcall TCFbECustomTableEx::TCFbECustomTableEx(TStringList* RowLabelList, TStringList* ColumnLabelList)
    : TCFbERowColumnCachedTable(RowLabelList, ColumnLabelList)
{
}

//---------------------------------------------------------------------------
// コンストラクタ
__fastcall TCFbECustomTableEx::TCFbECustomTableEx(TStringList* SourceCsvStringList)
    : TCFbERowColumnCachedTable(SourceCsvStringList)
{
}

//---------------------------------------------------------------------------
// デストラクタ
__fastcall TCFbECustomTableEx::~TCFbECustomTableEx()
{
}

//---------------------------------------------------------------------------
// Normalize する
double __fastcall TCFbECustomTableEx::Normalize(double SourceValue, double MaxValue, double MinValue)
{
    if (TCFbEToolkit::GetToolkit()->Equals(MaxValue - MinValue, 0.0)) {
        return 0.0;
    }
    return (SourceValue - MinValue) / (MaxValue - MinValue);
}

//---------------------------------------------------------------------------
// Table の要素を部分的に Normalize する
void __fastcall TCFbECustomTableEx::PartialNormalize(TCFbECustomTableEx* DestTable, int ColumnIndex)
{
    double MaxValue;
    double MinValue;
    try {
        MaxValue = this->GetMaxOfColumn(ColumnIndex);
        MinValue = this->GetMinOfColumn(ColumnIndex);
    } catch (...) { // 列に enabled な要素がない
        return;     // この列は終わり
    }
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        if (!this->GetEnabledByIndex(i, ColumnIndex)) {
            continue;
        }
        DestTable->SetValueByIndex(i, ColumnIndex, this->Normalize(this->GetValueByIndex(i, ColumnIndex), MaxValue, MinValue));
    }
}

//---------------------------------------------------------------------------
// Table の要素を Normalize する
void __fastcall TCFbECustomTableEx::Normalize(TCFbECustomTableEx* DestTable)
{
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        this->PartialNormalize(DestTable, j);
    }
}

//---------------------------------------------------------------------------
// TargetRowIndex 行 TargetColumnIndex 列目のNormalize された値 Value を DisNormalize して返す
double __fastcall TCFbECustomTableEx::DisNormalize(double NormalizedValue, double MaxValue, double MinValue)
{
    return NormalizedValue * (MaxValue - MinValue) + MinValue;
}

//---------------------------------------------------------------------------
// Standardize する
double __fastcall TCFbECustomTableEx::Standardize(double SourceValue, double AverageValue, double StdevValue)
{
    if (TCFbEToolkit::GetToolkit()->Equals(StdevValue, 0.0)) {
        return 0.0;
    }
    return (SourceValue - AverageValue) / StdevValue;
}

//---------------------------------------------------------------------------
// Table の要素を部分的に Standardize する
void __fastcall TCFbECustomTableEx::PartialStandardize(TCFbECustomTableEx* DestTable, int ColumnIndex)
{
    double AverageValue;
    double StdevValue;
    try {
        AverageValue = this->GetAverageOfColumn(ColumnIndex);
        StdevValue = this->GetStdevOfColumn(ColumnIndex);
    } catch (...) { // 列に enabled な要素がない
        return;     // この列は終わり
    }

    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        if (!this->GetEnabledByIndex(i, ColumnIndex)) {
            continue;
        }
        DestTable->SetValueByIndex(i, ColumnIndex, this->Standardize(this->GetValueByIndex(i, ColumnIndex), AverageValue, StdevValue));
    }
}

//---------------------------------------------------------------------------
// Table の要素を Standardize する
void __fastcall TCFbECustomTableEx::Standardize(TCFbECustomTableEx* DestTable)
{
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        this->PartialStandardize(DestTable, j);
    }
}

//---------------------------------------------------------------------------
// ColumnIndex 列目の Standardize された値 StandardizedValue を DisStandardize して返す
double __fastcall TCFbECustomTableEx::DisStandardize(double StandardizedValue, double AverageValue, double StdevValue)
{
    return StandardizedValue * StdevValue + AverageValue;
}

//---------------------------------------------------------------------------
// Ordering する
void __fastcall TCFbECustomTableEx::Ordering(TList* SortedCellList)
{
    int i = 0;
    while (i < SortedCellList->Count) {
        int EqualsCount = 0;
        while (((i+EqualsCount < SortedCellList->Count))
            && (((TCFbECell*)SortedCellList->Items[i])->GetValue() == ((TCFbECell*)SortedCellList->Items[i+EqualsCount])->GetValue())) {
            EqualsCount++;
        }

        int IndexCount = i;

        for (int k = 0; k < EqualsCount; k++) {
            if (SortedCellList->Count - 1 != 0) {
                ((TCFbECell*)SortedCellList->Items[i])->SetValue((SortedCellList->Count - (IndexCount + 1 + (EqualsCount - 1) * 0.5)) / (SortedCellList->Count - 1));
            } else {
                ((TCFbECell*)SortedCellList->Items[i])->SetValue(0.0);
            }
            i++;
        }
    }
}

//---------------------------------------------------------------------------
// Table の要素を部分的に Ordering する
void __fastcall TCFbECustomTableEx::PartialOrdering(TCFbECustomTableEx* DestTable, int ColumnIndex)
{
    TList* SortedCellList = new TList();
    DestTable->GetSortedCellListOfColumn(ColumnIndex, SortedCellList);
    DestTable->Ordering(SortedCellList);

    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        if (DestTable->GetEnabledByIndex(i, ColumnIndex)) {
            DestTable->CellIsUpdatedByIndex(i, ColumnIndex);
        }
    }
    
    delete SortedCellList;
}

//---------------------------------------------------------------------------
// Table の要素を Ordering する
void __fastcall TCFbECustomTableEx::Ordering(TCFbECustomTableEx* DestTable)
{
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        this->PartialOrdering(DestTable, j);
    }
}

//---------------------------------------------------------------------------
// ColumnIndex 列目の Ordering された値 Order を DisOrdering して返す
double __fastcall TCFbECustomTableEx::DisOrdering(double Order, TDoubleDynArray& SortedOrders, TDoubleDynArray& SortedValues)
{
    if (SortedValues.Length != SortedOrders.Length) {
        SortedValues.Length = 0;
        throw Exception("SortedValues.Length differed from SortedOrders.Length (in TCFbECustomTableEx::DisRowOrdering).");
    }

    if (SortedOrders.Length <= 0) {
        throw Exception("There is no elememt in SortedOrders (in TCFbECustomTableEx::DisRowOrdering).");
    }

    // 既存のいずれかのメトリクスの中に，予測値と同じものがある場合
    for (int i = 0; i < SortedOrders.Length; i++) {
        if (TCFbEToolkit::GetToolkit()->Equals(SortedOrders[i], Order)) {
            double ReturnValue = SortedValues[i];
            SortedValues.Length = 0;
            return ReturnValue;
        }
    }

    double LowestOrder = SortedOrders[SortedOrders.High];
    double HighestOrder = SortedOrders[0];

    // orderOf(pa,j) < orderOf(mlowest,j)である場合
    if (Order < LowestOrder) {
        int i = SortedOrders.High;
        while (--i > 0) {
            if (SortedOrders[i] > LowestOrder) {
                break;
            }
        }

        double SecondLowestOrder = (i >= 0) ? SortedOrders[i] : HighestOrder;    // RowIndex 列目の値の中で2番目に小さいもの
        double LowestValue = SortedValues[SortedValues.High];

        if (TCFbEToolkit::GetToolkit()->Equals(SecondLowestOrder - LowestOrder, 0.0)) {
            SortedValues.Length = 0;
            return LowestValue * (Order - LowestOrder) + LowestValue;
        }

        double SecondLowestValue = SortedValues[i];
        SortedValues.Length = 0;

        return (SecondLowestValue - LowestValue) / (SecondLowestOrder - LowestOrder) * (Order - LowestOrder) + LowestValue;

    // (2) orderOf(pa,j) > orderOf(mhighest,j)である場合
    } else if (Order > HighestOrder) {
        int i = 0;
        while (++i < SortedOrders.Length) {
            if (SortedOrders[i] < HighestOrder) {
                break;
            }
        }

        double SecondHighestOrder = (i >= 0) ? SortedOrders[i] : LowestOrder;    // RowIndex 列目の値の中で2番目に大きいもの
        double HighestValue = SortedValues[0];

        if (TCFbEToolkit::GetToolkit()->Equals(HighestOrder - SecondHighestOrder, 0.0)) {
            SortedValues.Length = 0;
            return HighestValue * (Order - HighestOrder) + HighestValue;
        }

        double SecondHighestValue = SortedValues[i];
        SortedValues.Length = 0;

        return (HighestValue - SecondHighestValue) / (HighestOrder - SecondHighestOrder) * (Order - HighestOrder) + HighestValue;
    }

    // それ以外の場合
    int    HighIndex = 0;
    int    LowIndex = SortedOrders.High;

    for (int i = 0; i < SortedOrders.Length; i++) {
        double CurrentOrder = SortedOrders[i];
        if ((CurrentOrder < Order) && (CurrentOrder > SortedOrders[LowIndex])) {
            LowIndex = i;
        } else if ((CurrentOrder > Order) && (CurrentOrder < SortedOrders[HighIndex])) {
            HighIndex = i;
        }
    }

    double HighOrder    = SortedOrders[HighIndex];
    double LowOrder    = SortedOrders[LowIndex];
    double HighValue    = SortedValues[HighIndex];
    double LowValue    = SortedValues[LowIndex];
    SortedValues.Length = 0;

    if (TCFbEToolkit::GetToolkit()->Equals(HighOrder - LowOrder, 0.0)) {
        return LowValue * (Order - LowOrder) + LowValue;
    }

    return ((HighValue - LowValue) / (HighOrder - LowOrder)) * (Order - LowOrder) + LowValue;
}

//---------------------------------------------------------------------------
// j 列目の Inverse Case Frequency Coefficient を得る
double __fastcall TCFbECustomTableEx::GetICFCoefficient(int j)
{
    int EnabledCount = this->GetEnabledCountOfColumn(j);
    if (EnabledCount == 0) {
        return 0.0;
    }
    return log((double)this->GetNumberOfRows() / (double)EnabledCount);
}

//---------------------------------------------------------------------------
// 行の最大値を返す．
// 行の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMaxOfRow(int i)
{
    if (!this->MaxRowCache->GetEnabledByIndex(i, 0)) {
        bool IsFirst = true;
        double Max = 0.0;

        for (int j = 0; j < this->GetNumberOfColumns(); j++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled() && (IsFirst || (CurrentCell->GetValue() > Max))) {
                Max = CurrentCell->GetValue();
                IsFirst = false;
            }
        }
        if (IsFirst) {
            throw Exception("There is no elements in the row (in TCFbECustomTableEx::GetMaxOfRow).");
        }

        this->MaxRowCache->SetValueByIndex(i, 0, Max);
    }

    return this->MaxRowCache->GetValueByIndex(i, 0);
}

//---------------------------------------------------------------------------
// 行の最小値を返す．
// 行の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMinOfRow(int i)
{
    if (!this->MinRowCache->GetEnabledByIndex(i, 0)) {
        bool IsFirst = true;
        double Min = 0.0;

        for (int j = 0; j < this->GetNumberOfColumns(); j++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled() && (IsFirst || (CurrentCell->GetValue() < Min))) {
                Min = CurrentCell->GetValue();
                IsFirst = false;
            }
        }
        if (IsFirst) {
            throw Exception("There is no elements in the row (in TCFbECustomTableEx::GetMinOfRow).");
        }

        this->MinRowCache->SetValueByIndex(i, 0, Min);
    }

    return this->MinRowCache->GetValueByIndex(i, 0);
}

//---------------------------------------------------------------------------
// 行の平均値を返す．
// 行の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetAverageOfRow(int i)
{
    if (!this->AverageRowCache->GetEnabledByIndex(i, 0)) {
        int Count = 0;
        double Sum = 0;

        for (int j = 0; j < this->GetNumberOfColumns(); j++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled()) {
                Sum += CurrentCell->GetValue();
                Count++;
            }
        }

        if (Count == 0) {
            this->AverageRowCache->SetValueByIndex(i, 0, 0.0);
        } else {
            this->AverageRowCache->SetValueByIndex(i, 0, Sum / (double)Count);
        }
    }

    return this->AverageRowCache->GetValueByIndex(i, 0);
}

//---------------------------------------------------------------------------
// 行の中間値を返す．
// 行の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMedianOfRow(int i)
{
    if (!this->MedianRowCache->GetEnabledByIndex(i, 0)) {
        TDoubleDynArray EnabledValues;
        this->GetEnabledCellsInRow(i, EnabledValues);

        if (EnabledValues.Length == 0) {
            throw Exception("There is no elements in the row (in TCFbECustomTableEx::GetMedianOfRow).");
        }

        TCFbEToolkit::GetToolkit()->SortDoubleDynArray(EnabledValues, sdDescending);
        this->MedianRowCache->SetValueByIndex(i, 0, TCFbEToolkit::GetToolkit()->GetMedianOfDynArray(EnabledValues));
        EnabledValues.Length = 0;
    }

    return this->MedianRowCache->GetValueByIndex(i, 0);
}

//---------------------------------------------------------------------------
// 行の標準偏差を返す．
// 行の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetStdevOfRow(int i)
{
    if (!this->StdevRowCache->GetEnabledByIndex(i, 0)) {
        double Average;
        try {
            Average = this->GetAverageOfRow(i);
        } catch (...) {
            throw Exception("There is no elements in the row (in TCFbECustomTableEx::GetStdevOfRow).");
        }

        double  Sum = 0.0;
        int     Count = 0;
        for (int j = 0; j < this->GetNumberOfColumns(); j++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled()) {
                Sum += pow(CurrentCell->GetValue() - Average, 2.0);
                Count++;
            }
        }

        if (Count == 0) {
            this->StdevRowCache->SetValueByIndex(i, 0, 0.0);
        } else {
            this->StdevRowCache->SetValueByIndex(i, 0, sqrt(Sum / (double)Count));
        }
    }

    return this->StdevRowCache->GetValueByIndex(i, 0);
}

//---------------------------------------------------------------------------
// ComparedRowIndex 番目の行の BaseRowIndex 番目の行に対する Averaged Multiplier を返す．
double __fastcall TCFbECustomTableEx::GetAveragedMultiplierOfRow(int BaseRowIndex, int ComparedRowIndex)
{
    double    Sum = 0.0;
    int    Count = 0;

    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        TCFbECell* ComparedCell = this->GetCellByIndex(ComparedRowIndex, j);
        TCFbECell* BaseCell = this->GetCellByIndex(BaseRowIndex, j);

        if (ComparedCell->GetEnabled() && BaseCell->GetEnabled() && !TCFbEToolkit::GetToolkit()->Equals(ComparedCell->GetValue(), 0.0)) {
            Sum += fabs(BaseCell->GetValue() / ComparedCell->GetValue());
            Count++;
        }
    }

    if (Count == 0) {
        return 0.0;
    }

    return Sum / Count;
}

//---------------------------------------------------------------------------
// ComparedRowIndex 番目の行の BaseRowIndex 番目の行に対する Median of Multiplier を返す．
double __fastcall TCFbECustomTableEx::GetMedianOfMultiplierOfRow(int BaseRowIndex, int ComparedRowIndex)
{
    TDoubleDynArray EnabledValues;

    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        TCFbECell* ComparedCell = this->GetCellByIndex(ComparedRowIndex, j);
        TCFbECell* BaseCell = this->GetCellByIndex(BaseRowIndex, j);

        if (ComparedCell->GetEnabled() && BaseCell->GetEnabled() && !TCFbEToolkit::GetToolkit()->Equals(ComparedCell->GetValue(), 0)) {
            EnabledValues.Length++;
            EnabledValues[EnabledValues.High] = fabs(BaseCell->GetValue() / ComparedCell->GetValue());
        }
    }

    double ReturnValue = 0.0;
    if (EnabledValues.Length > 0) {
        TCFbEToolkit::GetToolkit()->SortDoubleDynArray(EnabledValues, sdDescending);
        ReturnValue = TCFbEToolkit::GetToolkit()->GetMedianOfDynArray(EnabledValues);
        EnabledValues.Length = 0;
    }

    return ReturnValue;
}

//---------------------------------------------------------------------------
// 列の最大値を返す．
// 列の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMaxOfColumn(int j)
{
    if (!this->MaxColumnCache->GetEnabledByIndex(0, j)) {
        bool IsFirst = true;
        double Max = 0.0;

        for (int i = 0; i < this->GetNumberOfRows(); i++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled() && (IsFirst || (CurrentCell->GetValue() > Max))) {
                Max = CurrentCell->GetValue();
                IsFirst = false;
            }
        }
        if (IsFirst) {
            throw Exception("There is no elements in the column (in TCFbECustomTableEx::GetMaxOfColumn).");
        }

        this->MaxColumnCache->SetValueByIndex(0, j, Max);
    }

    return this->MaxColumnCache->GetValueByIndex(0, j);
}

//---------------------------------------------------------------------------
// 列の最小値を返す．
// 列の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMinOfColumn(int j)
{
    if (!this->MinColumnCache->GetEnabledByIndex(0, j)) {
        bool IsFirst = true;
        double Min = 0.0;

        for (int i = 0; i < this->GetNumberOfRows(); i++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled() && (IsFirst || (CurrentCell->GetValue() < Min))) {
                Min = CurrentCell->GetValue();
                IsFirst = false;
            }
        }
        if (IsFirst) {
            throw Exception("There is no elements in the column (in TCFbECustomTableEx::GetMinOfColumn).");
        }

        this->MinColumnCache->SetValueByIndex(0, j, Min);
    }

    return this->MinColumnCache->GetValueByIndex(0, j);
}

//---------------------------------------------------------------------------
// 列の平均値を返す．
// 列の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetAverageOfColumn(int j)
{
    if (!this->AverageColumnCache->GetEnabledByIndex(0, j)) {
        int Count = 0;
        double Sum = 0;

        for (int i = 0; i < this->GetNumberOfRows(); i++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled()) {
                Sum += CurrentCell->GetValue();
                Count++;
            }
        }

        if (Count == 0) {
            this->AverageColumnCache->SetValueByIndex(0, j, 0.0);
        } else {
            this->AverageColumnCache->SetValueByIndex(0, j, Sum / (double)Count);
        }
    }

    return this->AverageColumnCache->GetValueByIndex(0, j);
}

//---------------------------------------------------------------------------
// 列の中間値を返す．
// 列の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetMedianOfColumn(int j)
{
    if (!this->MedianColumnCache->GetEnabledByIndex(0, j)) {
        TDoubleDynArray EnabledValues;
        this->GetEnabledCellsInColumn(j, EnabledValues);

        if (EnabledValues.Length == 0) {
            throw Exception("There is no elements in the column (in TCFbECustomTableEx::GetMedianOfColumn).");
        }

        TCFbEToolkit::GetToolkit()->SortDoubleDynArray(EnabledValues, sdDescending);
        this->MedianColumnCache->SetValueByIndex(0, j, TCFbEToolkit::GetToolkit()->GetMedianOfDynArray(EnabledValues));
        EnabledValues.Length = 0;
    }

    return this->MedianColumnCache->GetValueByIndex(0, j);
}

//---------------------------------------------------------------------------
// 列の標準偏差を返す．
// 列の要素がない場合は例外を送出する．
double __fastcall TCFbECustomTableEx::GetStdevOfColumn(int j)
{
    if (!this->StdevColumnCache->GetEnabledByIndex(0, j)) {
        double Average;
        try {
            Average = this->GetAverageOfColumn(j);
        } catch (...) {
            throw Exception("There is no elements in the column (in TCFbECustomTableEx::GetStdevOfColumn).");
        }

        double  Sum = 0.0;
        int     Count = 0;
        for (int i = 0; i < this->GetNumberOfRows(); i++) {
            TCFbECell* CurrentCell = this->GetCellByIndex(i, j);
            if (CurrentCell->GetEnabled()) {
                Sum += pow(CurrentCell->GetValue() - Average, 2.0);
                Count++;
            }
        }

        if (Count == 0) {
            this->StdevColumnCache->SetValueByIndex(0, j, 0.0);
        } else {
            this->StdevColumnCache->SetValueByIndex(0, j, sqrt(Sum / (double)Count));
        }
    }

    return this->StdevColumnCache->GetValueByIndex(0, j);
}

//---------------------------------------------------------------------------
// ComparedColumnIndex 番目の列の BaseColumnIndex 番目の列に対する Averaged Multiplier を返す．
double __fastcall TCFbECustomTableEx::GetAveragedMultiplierOfColumn(int BaseColumnIndex, int ComparedColumnIndex)
{
    double    Sum = 0.0;
    int    Count = 0;

    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        TCFbECell* ComparedCell = this->GetCellByIndex(i, ComparedColumnIndex);
        TCFbECell* BaseCell = this->GetCellByIndex(i, BaseColumnIndex);

        if (ComparedCell->GetEnabled() && BaseCell->GetEnabled() && ComparedCell->GetValue() != 0) {
            Sum += fabs(BaseCell->GetValue() / ComparedCell->GetValue());
            Count++;
        }
    }

    if (Count == 0) {
        return 0.0;
    }

    return Sum / Count;
}

//---------------------------------------------------------------------------
// ComparedColumnIndex 番目の列の BaseColumnIndex 番目の列に対する Median of Multiplier を返す．
double __fastcall TCFbECustomTableEx::GetMedianOfMultiplierOfColumn(int BaseColumnIndex, int ComparedColumnIndex)
{
    TDoubleDynArray EnabledValues;

    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        TCFbECell* ComparedCell = this->GetCellByIndex(i, ComparedColumnIndex);
        TCFbECell* BaseCell = this->GetCellByIndex(i, BaseColumnIndex);

        if (ComparedCell->GetEnabled() && BaseCell->GetEnabled() && ComparedCell->GetValue() != 0) {
            EnabledValues.Length++;
            EnabledValues[EnabledValues.High] = fabs(BaseCell->GetValue() / ComparedCell->GetValue());
        }
    }

    double ReturnValue = 0.0;
    if (EnabledValues.Length > 0) {
        TCFbEToolkit::GetToolkit()->SortDoubleDynArray(EnabledValues, sdDescending);
        ReturnValue = TCFbEToolkit::GetToolkit()->GetMedianOfDynArray(EnabledValues);
        EnabledValues.Length = 0;
    }

    return ReturnValue;
}

//---------------------------------------------------------------------------
// ChildCache を ChildCacheName という名前のキャッシュとして追加する
void __fastcall TCFbECustomTableEx::AddChildCache(TCFbECachedTable* ChildCache, const char* ChildCacheName, bool IsLinkedToParent)
{
    if (StrComp(ChildCacheName, "Max.row") == 0) {
        this->MaxRowCache = (TCFbERowCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Min.row") == 0) {
        this->MinRowCache = (TCFbERowCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Average.row") == 0) {
        this->AverageRowCache = (TCFbERowCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Median.row") == 0) {
        this->MedianRowCache = (TCFbERowCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Stdev.row") == 0) {
        this->StdevRowCache = (TCFbERowCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Max.column") == 0) {
        this->MaxColumnCache = (TCFbEColumnCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Min.column") == 0) {
        this->MinColumnCache = (TCFbEColumnCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Average.column") == 0) {
        this->AverageColumnCache = (TCFbEColumnCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Median.column") == 0) {
        this->MedianColumnCache = (TCFbEColumnCachedTable*)ChildCache;

    } else if (StrComp(ChildCacheName, "Stdev.column") == 0) {
        this->StdevColumnCache = (TCFbEColumnCachedTable*)ChildCache;
    }

    TCFbERowColumnCachedTable::AddChildCache(ChildCache, ChildCacheName, IsLinkedToParent);
}

//---------------------------------------------------------------------------
// ChildCacheName という名前のキャッシュを削除する
void __fastcall TCFbECustomTableEx::DeleteChildCache(const char* ChildCacheName)
{
    if (StrComp(ChildCacheName, "Max.row") == 0) {
        this->MaxRowCache = NULL;

    } else if (StrComp(ChildCacheName, "Min.row") == 0) {
        this->MinRowCache = NULL;

    } else if (StrComp(ChildCacheName, "Average.row") == 0) {
        this->AverageRowCache = NULL;

    } else if (StrComp(ChildCacheName, "Median.row") == 0) {
        this->MedianRowCache = NULL;

    } else if (StrComp(ChildCacheName, "Stdev.row") == 0) {
        this->StdevRowCache = NULL;

    } else if (StrComp(ChildCacheName, "Max.column") == 0) {
        this->MaxColumnCache = NULL;

    } else if (StrComp(ChildCacheName, "Min.column") == 0) {
        this->MinColumnCache = NULL;

    } else if (StrComp(ChildCacheName, "Average.column") == 0) {
        this->AverageColumnCache = NULL;

    } else if (StrComp(ChildCacheName, "Median.column") == 0) {
        this->MedianColumnCache = NULL;

    } else if (StrComp(ChildCacheName, "Stdev.column") == 0) {
        this->StdevColumnCache = NULL;
    }

    TCFbERowColumnCachedTable::DeleteChildCache(ChildCacheName);
}

//---------------------------------------------------------------------------
// 以下，protected method
//---------------------------------------------------------------------------

