//----------------------------------------------------------------------------//
//    module FMain.cpp                                                        //
//                                                                            //
//    Main form of the GUI for the PNC cluster/learning algorithm             //
//                                                                            //
//    copyright (c) 2003 by Lars Haendel                                      //
//    home: www.newty.de                                                      //
//                                                                            //
//    This program is free software and can be used under the terms of the    //
//    GNU licence. See header file for further information and disclaimer.    //
//                                                                            //
//----------------------------------------------------------------------------//


//----------------------------------------------------------------------------------------------------------------------
// Notes:
// The output variable may be changed, but the columns in the data object are not exchanged until the user hits the
// buttons 'Edit Types'/'Edit Sets'/'Tune' and the function DoIt() or DoItExt() is called.


//----------------------------------------------------------------------------------------------------------------------
// Naming convention:
//
// Enable   show and hide or enable and disable controls
// Ev       evaluate functions: set controls depending on some settings/parameters which (may) have changed
// Set      set functions: set control's values depending on some initilization or default values
// Act      actualize displayed values


//----------------------------------------------------------------------------------------------------------------------
// Most important objects:
// Name           Type                 Description
// ---------------------------------------------------
// data_L         TData*               learn data file
// data_T         TData*               test data file
// model          TCluster*            learned model
// tuneResults    TTune*               tune results
//
// prj            TProjectG            project
// learnPara      TParameter           parameters displayed below on tab sheet 'Learn'; will be used to learn
// stdPara        TParameter           default parameters
//
// loss           TLossFunction*       loss function results (temporary)
// pnc            TPnc*                used to learn model  (temporary)


//----------------------------------------------------------------------------------------------------------------------
// help contexts
// 1-4   tab sheets
// 5     load learn data
// 6     variable types dialog
// 7     general setting parameter tuning
// 8     defining parameter sets
// 9     select an optimal paramter set
// 10    parameters
// 11    model characteristics
// 12    using the model
// 13    options
// 14    toolbar
// 20    start tuning
// 21    start learning



//----------------------------------------------------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "FMain.h"
#pragma package(smart_init)
#pragma resource "*.dfm"


//----------------------------------------------------------------------------------------------------------------------
#include <stdio>                    // due to:  sprintf()
#include <dir>                      //          getcwd
#include <filectrl.hpp>             //          MinimizeName()
#include <htmlhelp>                 //          HTML help

#include "ThrLoadData.h"            //          ThrLoadData (thread object)
#include "ThrUseModel.h"            //          ThrUseModel (thread object)
#include "ThrLearnModel.h"          //          ThrLearnModel (thread object)
#include "ThrTune.h"                //          ThrTune (thread object)
#include "FAbout.h"                 //          about dialog
#include "FOptions.h"               //          project options dialog
#include "FGuiOptions.h"            //          program/gui options dialog
#include "FVarTypes.h"              //          variable type selection dialog
#include "FEditSets.h"              //          tune tasks dialog
#include "FOpenProject.h"           //          LoadProject
#include "fileutil.h"               //          FlagToString() etc.


//----------------------------------------------------------------------------------------------------------------------
#define PRGNAME               "PNC2 Rule Induction System"     // program's name and version
#define VERSION               "v1.0b"
#define PROJECT_EXTENSION     "bap"                            // extension for project files
#define SIM_FILE_EXTENSION    "sim"                            // extension of simulation output files
#define TUNE_FILE_EXT         "opt"                            // extension for tune results file
#define HELP_FILE             "Pnc2Gui.chm"                    // help file GUI

#define MIN_LEARN_DATA_TUPLES 10                               // min. # learn data tuples


//----------------------------------------------------------------------------------------------------------------------
// globals
bool f_DisableWarnings;             // flag: disable warning message boxes
bool f_DisableQuestions;            // flag: assume YES on silly questions


//----------------------------------------------------------------------------------------------------------------------
// message texts
static const char* szBusyCloseQuery  = "I'm busy. Do you want to exit anyway ??";
static const char* szUnsaveExitQuery = "Project not saved! Do you want to exit anyway ??";
static const char* szUnsaveNewQuery  = "Project not saved! Do you want to continue ??";
static const char* szLearnDataLoaded = "This will delete the current model and project. Do you want to continue ??";
static const char* szTestDataLoaded  = "Test data file already loaded. Do you want to load another file ??";
static const char* szModelLoaded     = "This will delete the current model and project. Do you want to continue ??";
static const char* szModuleIsEmpty   = "Model is empty! Reduce value of 'Min. rule mass' on tab sheet 'Use Model'.";


//----------------------------------------------------------------------------------------------------------------------
// write defaults to registry; used on first startup of the program and to change default settings in options dialog
// note: existing key are not overwriten if f_Overwrite is set to false
void WriteDefaultsToRegistry(TRegistry* registry, const bool& f_Overwrite=false)
{
   registry->RootKey = HKEY_CURRENT_USER;                                     // set root key
   registry->OpenKey(SZ_REGISTRY_KEY, true);                                  // open key creating it if it does not
                                                                              // exist

   // project settings
   if(!registry->ValueExists(SZ_N_G_MAX) || f_Overwrite)                      // N_G_Max
      registry->WriteInteger(SZ_N_G_MAX, DEF_N_G_MAX);
   if(!registry->ValueExists(SZ_N_BINS) || f_Overwrite)                       // N_Bins
      registry->WriteInteger(SZ_N_BINS, DEF_N_BINS);
   if(!registry->ValueExists(SZ_OVERLAP_FAC) || f_Overwrite)                  // overlap factor
      registry->WriteFloat(SZ_OVERLAP_FAC, DEF_OVERLAP_FAC);
   if(!registry->ValueExists(SZ_EQUAL_WIDTH_BINNING) || f_Overwrite)          // flag equal width binning
      registry->WriteBool(SZ_EQUAL_WIDTH_BINNING, DEF_EQUAL_WIDTH_BINNING);
   if(!registry->ValueExists(SZ_NORMALIZE_BY_RANGE) || f_Overwrite)           // flag normalize by range
      registry->WriteBool(SZ_NORMALIZE_BY_RANGE, DEF_NORMALIZE_BY_RANGE);

   // program/gui settings
   if(!registry->ValueExists(SZ_COM_CHAR) || f_Overwrite)                     // comment character
      registry->WriteString(SZ_COM_CHAR, DEF_COM_CHAR);
   if(!registry->ValueExists(SZ_DISABLE_WARNINGS) || f_Overwrite)             // flag diable warning messages
      registry->WriteBool(SZ_DISABLE_WARNINGS, DEF_DISABLE_WARNINGS);
   if(!registry->ValueExists(SZ_DISABLE_QUESTIONS) || f_Overwrite)            // flag assume YES on silly questions
      registry->WriteBool(SZ_DISABLE_QUESTIONS, DEF_DISABLE_QUESTIONS);

   registry->CloseKey();                                                      // close registry key
}



TFMain *FMain;
//----------------------------------------------------------------------------------------------------------------------
// constructor
__fastcall TFMain::TFMain(TComponent* Owner) : TForm(Owner)
{
   // a) compose complete program name
   sprintf(cmplPrgName, "%s  %s", PRGNAME, VERSION);


   // b) remember executable's directory
   char szTmp[256];
   getcwd(szTmp, 256);                               // read
   szExeDir = szTmp;                                 // copy to AnsiString
   if(!szExeDir.IsPathDelimiter(szExeDir.Length()))  // ensure backslash - now program can run from disk root directory
      szExeDir += '\\';


   // c) initialize pointer
   data_L = data_T = NULL;
   model           = NULL;
   tuneResults     = NULL;
   pnc             = NULL;
   loss            = NULL;


   // d) reset flags
   f_Loading = f_Testing = f_Tuning = f_Learning = false;


   // e) create and initialize program's registry key if they don't exist already
   registry = new TRegistry;
   WriteDefaultsToRegistry(registry);


   // f) read program/gui settings from registry   - note: project settings are read in actionList 'EmptyProject'
   registry->OpenKey(SZ_REGISTRY_KEY, true);                         // open key creating it if it does not exist
   f_DisableWarnings  = registry->ReadBool(SZ_DISABLE_WARNINGS);     // read flag
   f_DisableQuestions = registry->ReadBool(SZ_DISABLE_QUESTIONS);    // read flag
   ComChar            = registry->ReadString(SZ_COM_CHAR)[1];        // read comment char
   registry->CloseKey();                                             // close key

   // initialize HTML help and full help file name
   HtmlHelp(0, 0, HH_INITIALIZE, reinterpret_cast <DWORD> (&HtmlHelpCookie));
   szHtmlHelpFile = szExeDir + HELP_FILE;
}


//----------------------------------------------------------------------------------------------------------------------
// destructor
__fastcall TFMain::~TFMain()
{
   // release learn and test data
   if(data_L)
      data_L->Release();
   if(data_T)
      data_T->Release();

   delete model;
   delete tuneResults;

   // note: should not be necessary since objects always get deleted in On...Finished()
   delete loss;
   delete pnc;


   delete registry;

   // un-initialize HTML help
   HtmlHelp(0, 0, HH_UNINITIALIZE, reinterpret_cast <DWORD> (HtmlHelpCookie));
}


//----------------------------------------------------------------------------------------------------------------------
// initialize controls
void __fastcall TFMain::FormShow(TObject *Sender)
{
   // a) initialize everthing for an empty project
   ActionList(EmptyProject);


   // b) initialize control texts and constraints
   TbDataSplitting->Max = MAX_SPLIT;                      // data splitting slider bar
                                                          // note: minimum is calculated based upon # learn data tuples


   // c) listview on tab sheet 'Tuning Results'
   LvTuneResults->Columns->Clear();                                    // clear all columns
   LvTuneResults->Columns->Add()->Caption = "Id";                      // set column header names
   LvTuneResults->Columns->Add()->Caption = "Intervals";
   LvTuneResults->Columns->Add()->Caption = "Anti COD";
   LvTuneResults->Columns->Add()->Caption = "Eta";
   LvTuneResults->Columns->Add()->Caption = "Min. rule mass";
   if(prj.Regression())
      LvTuneResults->Columns->Add()->Caption = "Sigma";
   else
      LvTuneResults->Columns->Add()->Caption = "Kernel width";
   LvTuneResults->Columns->Add()->Caption = "Weights";
   LvTuneResults->Columns->Add()->Caption = "Prune";
   LvTuneResults->Columns->Add()->Caption = "Euclidean";
   LvTuneResults->Columns->Add()->Caption = "Rules";
   LvTuneResults->Columns->Add()->Caption = "Rules (reduced)";
   LvTuneResults->Columns->Add()->Caption = "Variables";
   LvTuneResults->Columns->Add()->Caption = "Hitrate";
   LvTuneResults->Columns->Add()->Caption = "Error";

   // set column width and enable auto sizing
   int w = LvTuneResults->Width/LvTuneResults->Columns->Count;         // width of each column
   for(int i=0;i<LvTuneResults->Columns->Count;i++)
   {
      LvTuneResults->Columns->Items[i]->AutoSize = true;
      LvTuneResults->Columns->Items[i]->Width = w;
   }
}


//----------------------------------------------------------------------------------------------------------------------
// handle otherwise unhandled exceptions
void __fastcall TFMain::ApplicationEventsException(TObject*, Exception* E)
{
   // exceptions of type U: Intendend to be shown to the user
   if(IsTypeUExcpetion())
      Application->MessageBox(GetLastExcpetionU().GetErrorText(), "Error", MB_OK | MB_ICONERROR);
   else

      // exception of type A and B: Only intended for algorithm development
      if(IsTypeABExcpetion())
          Application->MessageBox(GetLastExcpetionAB().GetErrorText(), MSG_IPI, MB_OK | MB_ICONERROR);
      else
         if(E->Message.IsEmpty())
            Application->MessageBox("C++ Exception ;-)", "Error", MB_OK | MB_ICONERROR);  // unknown exception
         else
            Application->MessageBox(E->Message.c_str(), "Error", MB_OK | MB_ICONERROR);   // display message
}


//----------------------------------------------------------------------------------------------------------------------
// open help: close all previous windows, get actual help context Id and show help. Called by the reponse handler to
// key F1 and help menu/toolbar items
void TFMain::OpenHelp()
{
      // close all help windows
      HtmlHelp(0, 0, HH_CLOSE_ALL, 0);

      // get help topic Id
      int ContextId = 0;
      if(Screen->ActiveForm!=FMain)                      // use help topic Id from active form if child window ...
         ContextId = Screen->ActiveForm->HelpContext;    // ... is displayed modally
      else
         if(ActiveControl)                               // else: try to initialize from active main form control
            ContextId = ActiveControl->HelpContext;


      // open the help file in its default window type using the default opening page
      HtmlHelp(NULL, szHtmlHelpFile.c_str(), HH_DISPLAY_TOPIC, 0);

      // show context sensitive help topic
      HtmlHelp(NULL, szHtmlHelpFile.c_str(), HH_HELP_CONTEXT, ContextId);
}


//----------------------------------------------------------------------------------------------------------------------
// open help in response to button 'F1'
// note: this function may be called while a child window is displayed modally
void __fastcall TFMain::ApplicationEventsShortCut(TWMKey &Msg, bool &Handled)
{
   if(Msg.CharCode==VK_F1)                         // if 'F1' was hit
   {
      OpenHelp();                                  // open help file
      Handled = true;                              // ... and eat the event :-)
   }
   else
      Handled = false;
}


//----------------------------------------------------------------------------------------------------------------------
// open program help file in response to menu entry 'Help|Pnc Gui Help'
void __fastcall TFMain::MnHelpGuiClick(TObject *Sender){ OpenHelp(); }


//----------------------------------------------------------------------------------------------------------------------
// show about dialog
void __fastcall TFMain::AboutClick(TObject*)
{
   TFAbout* about = new TFAbout(this, cmplPrgName, szExeDir); // create dialog   note: dialog deletes itself when closed
   about->ShowModal();                                        // show dialog (modal)
}


//----------------------------------------------------------------------------------------------------------------------
// show project options dialog
void __fastcall TFMain::MnProjectOptionsClick(TObject *Sender)
{
   FOptions->SetProject(&prj);               // pass project to dialog
   FOptions->ShowModal();                    // show modal

   if(FOptions->SomethingChanged())          // unset saved flag if somth. has changed
      SetSavedFlag(false);
}


//----------------------------------------------------------------------------------------------------------------------
// show program/gui options dialog
void __fastcall TFMain::MnGuiOptionsClick(TObject *Sender)
{
   FGuiOptions->ShowModal();
}


//----------------------------------------------------------------------------------------------------------------------
// menu item 'File|New' clicked: Clear everything. Warn user if an unsaved project exists.
void __fastcall TFMain::MnNewClick(TObject *Sender)
{
   // if unsaved model or tune results exist
   if(!f_Saved && (model || tuneResults) && !f_DisableWarnings)
      if(Application->MessageBox(szUnsaveNewQuery, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
         return;

   // else: clear all
   ActionList(EmptyProject);
}


//----------------------------------------------------------------------------------------------------------------------
// Menu item 'File|Load Project' clicked: Load project
void __fastcall TFMain::MnOpenProjectClick(TObject *Sender)
{
   // a) if unsaved model or tune results exist
   if(!f_Saved && (data_L || model || tuneResults) && !f_DisableWarnings)
      if(Application->MessageBox(szUnsaveNewQuery, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
         return;
   ActionList(EmptyProject);       // clear all


   // b) ask user for filename
   OpenDialog->Title = "Open project ...";
   OpenDialog->Options << ofFileMustExist << ofPathMustExist;
   OpenDialog->DefaultExt = PROJECT_EXTENSION;
   OpenDialog->Filter = AnsiString("project file (*.") + AnsiString(PROJECT_EXTENSION) + AnsiString(")|*.") +
                        AnsiString(PROJECT_EXTENSION);
   OpenDialog->FileName = szProjectFile;
   if(!OpenDialog->Execute())
      return;                                            // return if closed with 'Cancel' button
   szProjectFile = OpenDialog->FileName;                 // store project filename


   // c) open/load project file
   ifstream file(szProjectFile.c_str());    // open file
   try
   {
      prj.Load(file, /*dir*/ ExtractFileDir(szProjectFile).c_str());  // load project
      if(prj.Model())
         szModelFile = ChangeFileExt(szProjectFile, AnsiString(".") + TPnc::Extension());
      if(prj.TuningResults())
         szTuningFile = ChangeFileExt(szProjectFile, AnsiString(".") + TUNE_FILE_EXT);
      int line=0;   // dummy, not used
      f_LearnParaInitialized = learnPara.Load(file, line);
   }
   catch(TExceptionU excp)            // exception handling
   {
      ActionList(EmptyProject);       // in case of an error ... note: if an error occured all 3 objects point to NULL

      // display error message ...
      Application->MessageBox(excp.GetErrorText(), "Error loading project file", MB_OK | MB_ICONERROR);
      return;
   }
   file.close();     // close file


   // d) load model, tune results and data
   FOpenProject->Associate(&prj, szModelFile.c_str(), szTuningFile.c_str());
   FOpenProject->ShowModal();
   FOpenProject->WriteBack(model, data_L, data_T, tuneResults);       // write back loaded objects


   // e) initialize GUI according to loaded objects (model, tune results, learn and test data)
   // e1) learn data
   if(data_L)
   {
      stdPara.SetStandardValues(data_L);           // initialize standard parameters

      if(f_LearnParaInitialized)
         SetParameters(learnPara);                 // if there are some: set parameters from project file

      ActionList(ProjectToGUI);                    // write project settings to GUI

      Caption = AnsiString(cmplPrgName) + "     " + szProjectFile;   // display project filename in window caption


      // e2) test data
      if(data_T)
      {
         prj.SetData2(data_T);                     // set data filename in project
         f_IsWithOutput = FOpenProject->IsWithOutput();
      }

      // e3) model
      if(model)
         ActionList(ModelLoaded);


      // e4) tune results
      if(tuneResults)
         ActionList(TuneResultsLoaded);

      SetSavedFlag(true);                          // just opened ;-)
   }
   else
      ActionList(EmptyProject); // in case of an error ... note: if an error occured all 4 objects point to NULL
}


//----------------------------------------------------------------------------------------------------------------------
// save project, ask user for filename before if necessary
void __fastcall TFMain::MnSaveProjectClick(TObject*)
{
   if(szProjectFile.IsEmpty())
      MnSaveProjectAsClick(this);
   else
      SaveProject(szProjectFile);
}


//----------------------------------------------------------------------------------------------------------------------
// ask user for filename to save project
void __fastcall TFMain::MnSaveProjectAsClick(TObject*)
{
   // ask user for filename
   SaveDialog->Title = "Save project as ...";
   SaveDialog->Options << ofPathMustExist << ofOverwritePrompt << ofNoReadOnlyReturn;
   SaveDialog->Filter = AnsiString("project file (*.") + AnsiString(PROJECT_EXTENSION) + AnsiString(")|*.")
                        + AnsiString(PROJECT_EXTENSION);
   SaveDialog->DefaultExt = PROJECT_EXTENSION;           // set default extension
   SaveDialog->FileName = szProjectFile;
   if(!SaveDialog->Execute())
      return;                                            // return if closed with 'Cancel' button

   SaveProject(SaveDialog->FileName);                    // save
}


//----------------------------------------------------------------------------------------------------------------------
// save project, model and tune results (if existing) to file
void TFMain::SaveProject(AnsiString szFileName)
{
   // a) store filename and set simulation output file  note: done here to allow a relative path
   szProjectFile = szFileName;
   prj.SetSimulationOutputFileName(ExtractRelativePath(ExtractFilePath(szProjectFile), szSimOutputFile).c_str());


   // b) save model if it exists
   if(model)
   {
      // compose model filename taking project filename exchanging its extension
      szModelFile = ChangeFileExt(szProjectFile, AnsiString(".") + TPnc::Extension());

      // error if the two files are the same, i.e. the user has specified the model extension as project extension
      if(szModelFile == szProjectFile)
      {
         char szText[256];
         sprintf(szText, "Do NOT use the extension '%s' for project files! Please retry!", TPnc::Extension());
         Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
         return;
      }

      // try to open and save
      ofstream file(szModelFile.c_str(), ios::out);                          // open
      if(!file)                                                              // check for errors
      {
         char szText[256];                                                   // compose error text
         sprintf(szText, "Unable to open file '%s'!", szModelFile.c_str());
         Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
         return;
      }
      model->Save(file);            // save model data
      prj.Model() = true;           // set model flag   note: model cannot be empty, thus there is no check
   }


   // c) save tune results if they exist
   if(tuneResults)
   {
      // compose result filename
      szTuningFile = ChangeFileExt(szProjectFile, AnsiString(".") + TUNE_FILE_EXT);

      // error if the two files are the same, i.e. the user has specified the tune file extension as project extension
      if(szTuningFile == szProjectFile)
      {
         char szText[256];
         sprintf(szText, "Do NOT use the extension '%s' for project files! Please retry!", TUNE_FILE_EXT);
         Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
         return;
      }

      // try to open and save
      ofstream file(szTuningFile.c_str(), ios::out);                         // open
      if(!file)                                                              // check for errors
      {
         char szText[256];                                                   // compose error text
         sprintf(szText, "Unable to open file '%s'!", szTuningFile.c_str());
         Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
         return;
      }
      tuneResults->Save(file);             // save tune results
      prj.TuningResults() = true;          // set flag in project, that indicates that a tune results file exists
   }


   // d) save project: try to open file, check and save
   ofstream file(szProjectFile.c_str(), ios::out);                           // open
   if(!file)                                                                 // check
   {
      char szText[256];                                                      // compose error text
      sprintf(szText, "Unable to open file '%s'!", szProjectFile.c_str());
      Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
      return;
   }

   prj.Save(file);                                 // save project
   if(f_LearnParaInitialized)                      // and parameters from group box 'Parameters' in tab sheet "Learn'
   {
      file << endl << endl;                        // line feeds
      learnPara.Save(file);                        // parameters
   }


   // e) misc.
   SetSavedFlag(true);                                            // set flag: everything is saved now
   ActModelStatus();                                              // display model filename
   ActTuningResultsStatus();                                      // display tune results filename
   Caption = AnsiString(cmplPrgName) + "     " + szProjectFile;   // diaplay project filename in window caption
}


//----------------------------------------------------------------------------------------------------------------------
// Menu 'File|Load Model' clicked: Load model from file
void __fastcall TFMain::MnLoadModelClick(TObject *Sender)
{
// ToDo: check parameter Prune against f_HasPruningInfo and f_HasOriginalCuboids

   // a) if data or model is already loaded/learned
   if((data_L || model))
   {
      if(!f_DisableWarnings)                    // ask user if he wants to continue as project/model will be lost
         if(Application->MessageBox(szModelLoaded, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
            return;                             // abort
      ActionList(EmptyProject);                 // else: clear all
   }

   // b) set dialog caption/options and execute it
   OpenDialog->Title = "Import model file ...";
   OpenDialog->Options << ofFileMustExist << ofPathMustExist;
   OpenDialog->Options >> ofOldStyleDialog;
   OpenDialog->DefaultExt = TPnc::Extension();
   OpenDialog->Filter = "PNC model files (*." + AnsiString(TPnc::Extension()) + ")|*." + AnsiString(TPnc::Extension());
   OpenDialog->FileName = szModelFile;

   if(!OpenDialog->Execute())
      return;                             // return if closed with 'Cancel' button


   // c) create parameter and model objects and load selected file
   ifstream file(OpenDialog->FileName.c_str());                // open file
   IfTrueThrowTypeU(!file, "Unable to open model file!");      // and check success

   szModelFile = OpenDialog->FileName;                         // and additionally store in GUI variable

   model = new TCluster();                                     // instantiate model
   try
   {
      int line = 1;
      model->Load(file, line);                                 // load model (cuboids)
   }
   catch(TExceptionU excp)            // exception handling
   {
      Application->MessageBox(excp.GetErrorText(), "Error", MB_OK | MB_ICONERROR);     // display error message ...
      ActionList(DeleteModel);
   }
   file.close();     // close file


   // d) if parameters and model are loaded correctly -> ... show them
   if(model)
      ActionList(ModelLoaded);
}


//----------------------------------------------------------------------------------------------------------------------
// Menu item 'File|Export Model' clicked:
void __fastcall TFMain::MnSaveModelClick(TObject *Sender)
{
   // a) ask user for filename
   SaveDialog->Title = "Export model file ...";
   SaveDialog->Options << ofPathMustExist << ofOverwritePrompt << ofNoReadOnlyReturn;
   SaveDialog->Filter = AnsiString("project file (*.") + AnsiString(TPnc::Extension()) + AnsiString(")|*.")
                        + AnsiString(TPnc::Extension());
   SaveDialog->DefaultExt = TPnc::Extension();           // set default extension

   if(!szModelFile.IsEmpty())                           // inititalize from model filename if string is not empty
      SaveDialog->FileName = szModelFile;
   else
      if(!szProjectFile.IsEmpty())                       // else: initalize from project filename if it's not empty
         SaveDialog->FileName = ChangeFileExt(szProjectFile, AnsiString(".") + TPnc::Extension());
      else
         SaveDialog->FileName = "";  // else: clear filename

   if(!SaveDialog->Execute())
      return;                                            // return if closed with 'Cancel' button
   szModelFile = SaveDialog->FileName;                   // store filename


   // b) export model
   ofstream file(szModelFile.c_str(), ios::out);                                  // open
   if(!file)                                                                      // check for errors
   {
      char szText[256];                                                           // compose error text
      sprintf(szText, "Unable to open file '%s'!", szModelFile.c_str());
      Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
      return;
   }
   UpdatePara();                                                              // initialize (learn and) test parameters
   model->Save(file, /*compact saving*/true, /*alternate parameters*/&para);  // save model data

   if(model->nCuboidsRed()==0 && !f_DisableWarnings)                          // display warning if module is empty
      Application->MessageBox(szModuleIsEmpty, "Warning", MB_OK | MB_ICONWARNING);

   ActModelStatus();                                                          // display model filename
}


//----------------------------------------------------------------------------------------------------------------------
// Menu item 'File|Exit' clicked:  Ask user if the program is busy and terminate
void __fastcall TFMain::MnExitClick(TObject*)
{
   bool f_CanClose = true;
   FormCloseQuery(this, f_CanClose);
   if(f_CanClose)
      Application->Terminate();
}


//----------------------------------------------------------------------------------------------------------------------
// Gets called if application wants to terminate:  Ask the user if program is busy
void __fastcall TFMain::FormCloseQuery(TObject*, bool &CanClose)
{
   CanClose=true;

   // ask user if he really wants to exit if program is busy ...
   if((f_Loading || f_Testing || f_Tuning || f_Learning) && !f_DisableWarnings)
   {
      if(Application->MessageBox(szBusyCloseQuery, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
         CanClose=false;
   }
   else
      // ... or if unsaved model or tune results exist
      if((!f_Saved && (model || tuneResults)) && !f_DisableWarnings)
         if(Application->MessageBox(szUnsaveExitQuery, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
            CanClose=false;
}


//----------------------------------------------------------------------------------------------------------------------
// Gets called if user tries to change the tab sheet:  Check if selected tab sheet is allowed to be displayed
void __fastcall TFMain::PageControlChanging(TObject *Sender, bool &AllowChange)
{
   // disallow without user notice if program is busy
   AllowChange = true;
   if(f_Loading || f_Testing || f_Tuning || f_Learning)
     AllowChange = false;
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable menu and toolbar entries
void TFMain::EnableMenus(const bool& f_Enable)
{
   // menu 'File'
   MnNew         ->Enabled = f_Enable;
   MnOpenProject ->Enabled = f_Enable;
   MnLoadModel   ->Enabled = f_Enable;

   MnSaveProject  ->Enabled = !f_Saved && f_Enable && data_L; // enable only if learn data is present and project unsaved
   MnSaveProjectAs->Enabled = f_Enable && data_L;             // enable only if learn data is present
   MnSaveModel    ->Enabled = f_Enable && model;              // enable only if model is present

   TbNew         ->Enabled = f_Enable;
   TbOpenProject ->Enabled = f_Enable;
   TbLoadModel   ->Enabled = f_Enable;

   TbSaveProject ->Enabled = !f_Saved && f_Enable && data_L; // enable only if learn data is present and project unsaved
   TbSaveModel   ->Enabled = f_Enable && model;              // enable only if model is present


   // menu 'Options'
   MnOptions->Enabled = f_Enable;
}


//----------------------------------------------------------------------------------------------------------------------
// commands for some common actions
// - ClearResults:      clear displayed results and hide pie chart
// - ProjectToGUI:      write project settings to GUI
// - EmptyProject:      reset project and GUI
// - ClearTestData:     delete test data
// - LearnDataLoaded:   learn data loaded, enable controls
// - ModelLoaded:       'show' and initialize (prepare) model
// - TuneResultsLoaded  transfer loaded tune results to list view on tab sheet 'Tuning Results'
// - DeleteModel        delete model
void TFMain::ActionList(const TActionList& action)
{
   switch(action)
   {
      //----------------------------------------------------------------------------------------------------------------
      // clear displayed loss results and hide pie chart
      case ClearResults :
         MceChart->Visible = false;          // hide pie chart

         LaOverallLoss1->Caption = "??";     // clear displayed results
         LaInsideLoss1 ->Caption = "??";
         LaOutsideLoss1->Caption = "??";
         LaOverallLoss2->Caption = "??";
         LaInsideLoss2 ->Caption = "??";
         LaOutsideLoss2->Caption = "??";
         LaInsideRatio ->Caption = "??";

         LaTestCuboids->Caption = "??";
         LaInsideRatio->Caption = "??";
         break;



      //----------------------------------------------------------------------------------------------------------------
      // write project settings to GUI
      case ProjectToGUI :

         // group box 'Parameter tuning'
         EMaxSize    ->Text = AnsiString(prj.GetMaxSize()) + " %";            // maximum model size
         EMinCmpr    ->Text = AnsiString(prj.GetMinCompression()) + " %";     // minimum compression rate
         CbSkipTuning->Checked = !prj.DoTuning();                             //
         CbRandomize ->Checked = prj.Randomize();                             // flags
         CbSkipping->Checked = prj.Skipping();


         EnableLearnDataCtrls(data_L);                  // enable/disable controls
         ActLearnDataStatus(data_L);                    // display filename or hint how to load

         MnSaveProject  ->Enabled = (data_L!=NULL);     // enable/disable menu entry and tool bar button 'Save Project'
         MnSaveProjectAs->Enabled = (data_L!=NULL);
         TbSaveProject  ->Enabled = (data_L!=NULL);


         // note: changing of radio buttons generates an 'OnClick' events and the corresponding event handler may try
         //       to write updated settings to the project object which would raise an exception because no data is
         //       associated. Thus do not proceed!
         if(!data_L)
            break;


         // group box 'Learn Data'
         EOutCol->Text = prj.GetOutCol()+1;                          // output column
         EvOutputType();                                             // classification flag


         // initialize group box 'Tune parameters'
         ERepetition ->Text = prj.Get_N_R_Tune();                    // # repetitions/cross-validations
         if(prj.GetTuneType()==Rep)                                  // tune type
            RbRepetition->Checked = true;
         else
            if(prj.GetTuneType()==Cv)
               RbCrossValidation->Checked = true;
            else
               RbLoocv->Checked = true;
         EvTuneType();                                               // show or hide controls according
         SetNParameterSets();                                        // display # parameter sets defined for tuning


         // tab sheet 'Use Model'
         CbWritePredictions->Checked = prj.WritePredictions();
         CbModify1->Checked = prj.Modify1();
         CbModify2->Checked = prj.Modify2();

         szSimOutputFile = prj.GetSimulationOutputFileName();      // initialize simulation output filename from project

         break;



      //----------------------------------------------------------------------------------------------------------------
      // clear all and restore the state as it was when started  note: calls action lists DeleteModel,
      // DeleteTuningResults and ProjectToGUI
      case EmptyProject :

         PageControl->ActivePage = TsBasic;                    // change to tab sheet 'Basic'
         ActiveControl = BtLearnData;                          // set focus   note: important to prevent errors

         f_LearnParaInitialized = false;
         szSimOutputFile = szProjectFile = "";                 // clear filenames
         szModelFile = szTuningFile = "";
         Caption = AnsiString(cmplPrgName);                    // 'remove' project filename from main window caption


         if(data_L) data_L->Release();                         // release learn and test data
         data_L = NULL;

         prj.Reset();                                          // reset project ...


         // ... and initialize N_G_Max, N_Bins, overlap factor flag from registry
         registry->OpenKey(SZ_REGISTRY_KEY, true);                            // open key creating it if it does not exist
         prj.Set_N_G_Max  (registry->ReadInteger(SZ_N_G_MAX));                // read N_G_Max
         prj.Set_N_Bins   (registry->ReadInteger(SZ_N_BINS));                 // read N_Bins
         prj.SetOverlapFac(registry->ReadFloat(SZ_OVERLAP_FAC));              // read overlap factor
         prj.NormalizeByRange() = registry->ReadBool(SZ_NORMALIZE_BY_RANGE);  // read normalize by range flag
         prj.EqualWidthBinning()= registry->ReadBool(SZ_EQUAL_WIDTH_BINNING); // read equal width binning flag
         registry->CloseKey();                                                // close key


         ActionList(DeleteModel);                          // hide tab sheets and release model
         delete pnc;                                       // note: obsolete, should not be necessary
         pnc = NULL;

         ActionList(DeleteTuningResults);                  // hide tab sheet and delete tune results
         SetSavedFlag(false);


         // tab sheet 'Basics'
         LaLearnDataStatus->Caption = "";


         // group box 'Parameters'
         EIntervals  ->Text = "";                          // 'clear' parameters
         EWmin       ->Text = "";
         EEta        ->Text = "";
         ECardinality->Text = "";
         ESpecial    ->Text = "";
         ESigma      ->Text = "";

         // note: do not(!) reset the check boxes as this would cause an OnClick event
         // which would write through to the project. Either i or the developper of
         // TCheckbox is an idiot :-|



         // tab sheet 'Use Model'
         CbWritePredictions->Checked = DEF_WRITE_PREDICTIONS;
         LaTestTime->Caption = "";                             // clear "elapsed time" string
         ActionList(ClearTestData);
         CbModify1->Checked = false;                           // uncheck modify check boxes
         CbModify2->Checked = false;


         // actualize GUI
         ActionList(ProjectToGUI);
         break;


      //----------------------------------------------------------------------------------------------------------------
      // clear test data   note: calls ClearResults at the end
      case ClearTestData :

         if(data_T)
            data_T->Release();                                  // release test data
         data_T = NULL;
         prj.ClearData2();                                      // clear test data association in project
         EnableTestDataCtrls(false);                            // disable/hide controls
         ActTestDataStatus();                                   // and actualize displayed status


         // group box 'Simulation'
         ActTestStatus();
         LaTestStatus->Caption = "No results";
         LaTestTime  ->Caption = "0m00s";

         // clear loss results
         ActionList(ClearResults);;

      break;


      //----------------------------------------------------------------------------------------------------------------
      // learn data has been loaded, enable controls
      case LearnDataLoaded :

         prj.Initialize(data_L);                      // initialize project using learn data
         ActionList(ProjectToGUI);

         ActiveControl = EOutCol;                     // set focus to output column control

         // hints
         LaOutCol->Hint= "Which column of the data file contains the output values?\nEnter a value between 1 and "
                           + AnsiString(data_L->nVar()) + ".";
         EOutCol->Hint = LaOutCol->Hint;

         // min. slider data splitting
         TbDataSplitting->Min = max((int) ceil(500/data_L->nTup()), MIN_SPLIT);   // ensure at least 5 learn data tuples

         break;



      //----------------------------------------------------------------------------------------------------------------
      // model has been loaded
      case ModelLoaded :
         model->Prepare();                         // set cardinality
         ActModelParameters();                     // initialize displayed parameters and values
         ActModelStatus();                         // display model filename
         para = *(model->GetParameters());         // initialize from model parameters
         LaModelTime->Caption = "NA";              // clear time
         PageControl->ActivePage = TsModel;        // ... change to tab sheet 'Model'
         MnSaveModel->Enabled = true;              // enable menu entry 'Export Model' and toolbar button
         TbSaveModel->Enabled = true;              //
         break;



      //----------------------------------------------------------------------------------------------------------------
      // transfer loaded tuning results to list view on tab sheet 'Tuning Results'
      case TuneResultsLoaded :
         AddTuningResults();                       // add tune results to list view
         TsTuneResults->TabVisible = true;         // show tab sheet 'Tune Results'
         LaSelectText->Visible = true;             // show hint text how to select parameter set
         LaTuneTime->Caption = "NA";               // clear time
         ActTuningResultsStatus();                 // display file name
         break;



      //----------------------------------------------------------------------------------------------------------------
      // delete model
      case DeleteModel :
         delete model;                       // delete model
         model = NULL;
         szModelFile = "";

         GbModel->Visible = false;           // hide group box 'Model' in tab sheet 'Model'
         MnSaveModel->Enabled = false;       // disable 'Export Model' menu and toolbar button
         TbSaveModel->Enabled = false;

         TsModel->TabVisible = false;        // hide tab sheets 'Model' and 'Use Model'
         TsUseModel->TabVisible = false;

         CbModify1->Checked = false;         // uncheck checkboxes on tab sheet 'Use Model' to re-initialize them later
         CbModify2->Checked = false;         // if a new model is learned
         break;



      //----------------------------------------------------------------------------------------------------------------
      // delete tune results
      case DeleteTuningResults :
         TsTuneResults->TabVisible = false;  // hide tab sheet 'Tune Results'
         LvTuneResults->Items->Clear();      // delete all results in list view
         LaSelectText->Visible = false;      // hide hint
         BtSelect->Enabled = false;          // disable button 'Select'

         delete tuneResults;                 // delete
         tuneResults = NULL;
         break;



      //----------------------------------------------------------------------------------------------------------------
      // delete tune tasks
      case DeleteTuningTasks :
         prj.DeleteAllTasks();                        // tune tasks
         BtTune->Enabled = false;                     // disable button
         SetNParameterSets();                         // display # parameter sets
         break;
   }
}


   //-----------------------------------------------------------------------------------------------
   //
   //    Tab Sheet 'Basic'
   //

//----------------------------------------------------------------------------------------------------------------------
//
void __fastcall TFMain::TsBasicShow(TObject *Sender)
{
   SetTsLearnFocus();   // set focus to appropriate control
}


//----------------------------------------------------------------------------------------------------------------------
// set focus to appropriate control
void TFMain::SetTsLearnFocus()
{
   if(!data_L)
      ActiveControl = BtLearnData;
   else
      if(CbSkipTuning->Checked)
         ActiveControl = BtLearn;            // focus button 'Learn'
      else
         if(prj.nParaSets()>0)               // if parameter sets are defined ...
            ActiveControl = BtTune;          // ... focus button 'Tune'
         else
            ActiveControl = BtEditSets;      // ... else focus button 'Edit Sets'
}


//----------------------------------------------------------------------------------------------------------------------
// write through output column and set data and project in TVarTypes object
void TFMain::DoIt()
{
   prj.WriteThroughOutCol(f_IsWithOutput);
   FVarTypes->Associate(data_L, &prj, this);
   FEditSets->Associate(data_L, &prj, &stdPara);
}


//----------------------------------------------------------------------------------------------------------------------
//
void SetLabelFontStyleAndColor(TLabel* label, const bool& f_Bold, const TColor& color)
{
   TFontStyles style = label->Font->Style;            // get control's font style
   if(f_Bold)
      style << fsBold;                                // set bold style
   else
      style >> fsBold;                                // unset bold style

   label->Font->Color = color;                        // and set it to red
   label->Font->Style = style;                        // set style
}


//----------------------------------------------------------------------------------------------------------------------
// call DoIt() and calculate feature weights and initialize standard parameter object
// ToDo: find a better solution! Maybe use a thread to calculate feature weights
void TFMain::DoItExt()
{
   DoIt();                                                           // write through output column

   // set label text and bold style and color to red
   SetLabelFontStyleAndColor(LaLearnDataStatus, true, clRed);
   LaLearnDataStatus->Caption = "Calculating feature weigths";
   LaLearnDataStatus->Repaint();
   EnableMenus(false);                                               // disable menu entries
   ToolBar->Repaint();


   // calculate feature weights and update standard parameters if weights have been (re-)calculated
   if(data_L->CalculateWeights(prj.Get_N_Bins(), !prj.Regression(), prj.EqualWidthBinning()))
      stdPara.SetStandardValues(data_L);


   // unset label bold style and set color back to black
   SetLabelFontStyleAndColor(LaLearnDataStatus, false, clBlack);

   ActLearnDataStatus();
   EnableMenus(true);                                                // enable menu entries again
}


//----------------------------------------------------------------------------------------------------------------------
// Called while learn data is loaded:  Actualize progress bar and status text
void __fastcall TFMain::OnLoadLearnDataTimer(TObject*)
{
   LaLearnDataStatus->Caption = AnsiString(data_L->StatusText());
   PbLearnData->Position = data_L->LoadDataProgress();
}


//----------------------------------------------------------------------------------------------------------------------
// hide and show, enable and disable controls in tab sheet 'Basic'
void TFMain::EnableLearnDataCtrls(const bool& f_Enable)
{
   // a) group box 'Learn Data'
   LaOutCol        ->Visible = f_Enable;
   EOutCol         ->Visible = f_Enable;
   BtOutColUp      ->Visible = f_Enable;
   BtOutColDown    ->Visible = f_Enable;
   BtEditTypes     ->Visible = f_Enable;
   CbClassification->Visible = f_Enable;

   BtLearnDataPause->Visible = !f_Enable;                      // 'Pause' button
   BtLearnDataAbort->Visible = !f_Enable;                      // 'Abort' button


   // b) group box 'Tune parameters'
   CbSkipTuning->Enabled = f_Enable;                           // check box 'Skip tuning'


   // c) enable/disable controls in other group boxes
   //    note: only one of them, depending on the 'Skip tuning' check box, can be enabled
   EnableTuneCtrls(!CbSkipTuning->Checked && f_Enable);        // group box 'Tune parameters'
   EnableParameterCtrls(CbSkipTuning->Checked  && f_Enable);   // group box 'Parameters'
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status on tab sheet 'Use Model'    note: useful for display of filenames with MinimizeName()
void __fastcall TFMain::TsBasicResize(TObject *Sender)
{
   // call to update displayed filename (because maybe it has been or now has to be minimized)
   ActLearnDataStatus(true);
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status labels at the top of tab sheet 'Basic': display filename of loaded data or hint how to load data
void TFMain::ActLearnDataStatus(const bool& f_ActFilename)
{
   // display learn data filename (normal) or hint (bold) how to load it
   if(data_L)
   {
      LaLearnFileHeader->Caption = "File";

      // set filename    note: file name was set after it was specified in OnLoad...
      if(f_ActFilename)
         LaLearnFile->Caption=MinimizeName(data_L->LoadFileName(), LaLearnFile->Canvas, LaLearnFile->Width);


      LaLearnDataStatusHeader->Visible = true;
      LaLearnDataStatus      ->Visible = true;

      if(!f_Loading)
      {
         // display # variables and tuples
         char text[256];
         sprintf(text, "%d variables and %d tuples", data_L->nVar(), data_L->nTup());
         LaLearnDataStatus->Caption = text;
      }
      // note: "Loading" is displayed via OnTimer event handler
   }
   else
   {
      LaLearnFileHeader->Caption = "Hit button to load learn data file!";
      LaLearnFile      ->Caption = "";

      LaLearnDataStatusHeader->Visible = false;
      LaLearnDataStatus      ->Visible = false;
   }
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable controls in group box 'Tune parameters'
void TFMain::EnableTuneCtrls(const bool& f_Enable)
{
   RbRepetition     ->Enabled = f_Enable;
   RbCrossValidation->Enabled = f_Enable;
   RbLoocv          ->Enabled = f_Enable;
   LaRepetition     ->Enabled = f_Enable;
   LaSplitting      ->Enabled = f_Enable;
   LaRuns           ->Visible = f_Enable;
   TbDataSplitting  ->Enabled = f_Enable;
   CbRandomize      ->Enabled = f_Enable;
   CbSkipping       ->Enabled = f_Enable;
   ERepetition      ->Enabled = f_Enable;
   BtRepetitionUp   ->Enabled = f_Enable;
   BtRepetitionDown ->Enabled = f_Enable;

   LaMaxSize        ->Enabled = f_Enable && CbSkipping->Checked;  // do not enable if skipping is unchecked
   LaMinCmpr        ->Enabled = f_Enable && CbSkipping->Checked;
   EMaxSize         ->Enabled = f_Enable && CbSkipping->Checked;
   EMinCmpr         ->Enabled = f_Enable && CbSkipping->Checked;
   BtMaxSizeUp      ->Enabled = f_Enable && CbSkipping->Checked;
   BtMaxSizeDown    ->Enabled = f_Enable && CbSkipping->Checked;
   BtMinCmprUp      ->Enabled = f_Enable && CbSkipping->Checked;
   BtMinCmprDown    ->Enabled = f_Enable && CbSkipping->Checked;

   BtEditSets       ->Enabled = f_Enable;

   // enable button 'Tune' only if parameter sets are defined
   BtTune->Enabled = (prj.nParaSets()>0 && f_Enable);
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable controls in group box 'Parameters'
void TFMain::EnableParameterCtrls(const bool& f_Enable)
{
   // a) classification or regression parameters

   // disallow enabling of parameter 'Intervals' if classification check box is set
   if(!CbClassification->Checked || !f_Enable)
   {
      LaIntervals  ->Enabled = f_Enable;
      EIntervals   ->Enabled = f_Enable;
      LaSigma      ->Enabled = f_Enable;
      ESigma       ->Enabled = f_Enable;
   }


   // disallow enabling of 'classification only' parameters (W_Kernel ...) if it's not a classification task
   if(CbClassification->Checked || !f_Enable)
   {
      LaSpecial    ->Enabled = f_Enable;
      ESpecial     ->Enabled = f_Enable;
   }


   // b) other parameters
   GbParameters ->Enabled = f_Enable;
   LaWmin       ->Enabled = f_Enable;
   LaEta        ->Enabled = f_Enable;
   LaCardinality->Enabled = f_Enable;
   CbWeights    ->Enabled = f_Enable;
   CbPrune      ->Enabled = f_Enable;
   CbEuclidean  ->Enabled = f_Enable;
   EWmin        ->Enabled = f_Enable;
   EEta         ->Enabled = f_Enable;
   ECardinality ->Enabled = f_Enable;


   // c) misc.
   CbPruneAnyWay->Enabled = !CbPrune->Checked && f_Enable;     // enable check box 'Prune anyway' if prune
                                                               // is unchecked
   CbKillCuboids->Enabled = f_Enable;                          // check box 'Kill small cuboids'
   BtDefault    ->Enabled = f_Enable;                          // button 'Defaults'
   BtLearn      ->Enabled = f_Enable;                          // button 'Learn'
}


//----------------------------------------------------------------------------------------------------------------------
// change output column
void TFMain::ChangeOutCol(int outcol)
{
   if(!data_L) // return if learn data is not loaded   note: may happen if 'File|New' is hit when focus is on EOutCol
      return;

   // a) check contraints and return if outcol has not changed
   if(outcol<0)                  outcol = 0;                   // minimum
   if(outcol>=data_L->nVar())    outcol = data_L->nVar()-1;    // maximum

   if(prj.GetOutCol()==outcol)                                 // return if output column has not changed
   {
      EOutCol->Text = prj.GetOutCol()+1;                       // re-write/correct text
      return;
   }

   SetSavedFlag(false);


   // b) ask user if something (defined tasks for tuning, tune results or learned model) gets lost if
   //    requested change is done

   // if classification is enabled: does this need to be changed to regression as new output column is continous ?
   bool f_Change = (!prj.Regression() && !prj.CouldBeClassification(outcol));

   // ask
   if((prj.nParaSets()>0 && f_Change) || tuneResults || model)
   {
      ActiveControl = EOutCol;   // give focus back
      EOutCol->SelectAll();      // and select/highlight text


      char szText[STS];
      if(f_Change)
         strcpy(szText, "You'll loose all parameters sets, tune results and learned model!\nDo you\
 really want to change the output column ??");
      else
         strcpy(szText, "You'll loose the tune results and learned model!\nDo you\
 really want to change the output column ??");


      if(Application->MessageBox(szText, "Question", MB_YESNO | MB_ICONQUESTION)== ID_NO)
      {
         EOutCol->Text = prj.GetOutCol()+1;        // write back actual value to edit field
         return;                                   // return if user selects 'No'
      }
   }  // else continue, i.e. make change


   // c) delete model, tune tasks and results
   ActionList(DeleteModel);                        // model
   ActionList(DeleteTuningResults);                // tune results
   if(f_Change)                                    // if changed from classification to regression by force
      ActionList(DeleteTuningTasks);


   // d) now change the output column
   prj.SetOutCol(outcol);                          // set new output column
   EOutCol->Text = prj.GetOutCol()+1;              // re-write/correct text

   if(!f_Change)
      if(!prj.Regression())
         prj.CorrectTasksIntervals();


   // e) update GUI
   EvOutputType();                                 // display parameters and classification check box
}


//----------------------------------------------------------------------------------------------------------------------
// function gets called from class TVarTypes: Tuning results or learned model would be lost if (input) variable
// types are changed. Thus ask user before.
bool TFMain::AllowChangeOfInputType()
{
   // a) ask user if tune results or model are present
   if(tuneResults || model)
      if(Application->MessageBox("You'll loose all tuning results and the learned model!\nDo you really want to\
 change the variable type ??", "Question", MB_YESNO | MB_ICONQUESTION)== ID_NO)
         return false;              // disallow change if user selects 'No'


   // else
   // b)  delete model and tune results
   ActionList(DeleteModel);                        // model
   ActionList(DeleteTuningResults);                // tune results
   SetSavedFlag(false);

   return true;                                    // allow change
}


//----------------------------------------------------------------------------------------------------------------------
// adjust displayed parameters and classification check box depending on regression flag (TProjectG::f_Regression)
void TFMain::EvOutputType()
{
   // a) set and enable classification check box if output is/could be symbolic, else unset and disable it
   CbClassification->Checked = !prj.Regression();                                // set/unset and ...
   CbClassification->Enabled = prj.CouldBeClassification(prj.GetOutCol());       // ... enable/disable check box


   // b) group box 'Parameters'
   // enable/disable parameter 'Intervals', 'Kernel Width', 'min. Kernel Width' and 'Sigma'

   if(!prj.Regression())                                    // classifciation problem
   {
      LaIntervals->Enabled = false;                            // disable parameter 'Intervals'
      EIntervals ->Enabled = false;

      LaSigma    ->Enabled = false;                            // disable parameter 'Sigma'
      ESigma     ->Enabled = false;

      LaSpecial  ->Enabled = GbParameters->Enabled;            // enable parameter 'Kernel Width'
      ESpecial   ->Enabled = GbParameters->Enabled;


      // enforce # classes to be display for parameter 'Intervals' if parameter values are displayed at all
      if(f_LearnParaInitialized)
         EIntervals->Text = ValueToText1(prj.nClasses());
   }
   else                                                     // regression problem
   {
      LaIntervals->Enabled = GbParameters->Enabled;            // parameter 'Intervals': set to the same state as
      EIntervals ->Enabled = GbParameters->Enabled;            // the group box itself

      LaSigma    ->Enabled = GbParameters->Enabled;            // parameter 'Sigma': do.
      ESigma     ->Enabled = GbParameters->Enabled;

      LaSpecial  ->Enabled = false;                            // disbale parameter 'Kernel Width'
      ESpecial   ->Enabled = false;

      // set value for parameter 'Intervals' if parameter values are displayed at all
      if(f_LearnParaInitialized)
         EIntervals->Text = ValueToText1(learnPara.N_Int);
   }
}


//----------------------------------------------------------------------------------------------------------------------
// evaluate selected tune type: set tune type in TProjectG evaluating tune type radio group and
// show or hide controls for # repetitions/cross-validations and data splitting
void TFMain::EvTuneType()
{
   // a) evaluate tune type radio group and set type in project
   if(RbCrossValidation->Checked)
   {
      prj.SetTuneType(Cv);                // set to cross-validation
      prj.Set_N_R_Tune(DEF_N_R_TUNE);     // set new default value
   }
   else
      if(RbRepetition->Checked)
      {
         prj.SetTuneType(Rep);            // set to repetition
         prj.Def_N_R_Tune();              // set new default value
      }
      else
         prj.SetTuneType(Loocv);          // set to loocv



   // b) show and hide controls
   ERepetition     ->Visible=!RbLoocv->Checked;
   LaRepetition    ->Visible=!RbLoocv->Checked;
   BtRepetitionUp  ->Visible=!RbLoocv->Checked;
   BtRepetitionDown->Visible=!RbLoocv->Checked;
   LaSplitting     ->Visible=!RbLoocv->Checked && RbRepetition->Checked;
   TbDataSplitting ->Visible=!RbLoocv->Checked && RbRepetition->Checked;
   CbRandomize     ->Visible=!RbLoocv->Checked; // hide as irrelevant for LOOCV   

   //c) set # repetitions/cross-validations text
   ERepetition->Text = AnsiString(prj.Get_N_R_Tune());


   // d) set data splitting
   char szText[256];
   sprintf(szText, "Data Splitting %d\%", TbDataSplitting->Position);
   LaSplitting->Caption = szText;
   TbDataSplitting->Position = prj.GetDataSplitting();


   SetSavedFlag(false); // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// display actual number of tune parameter sets or a hint how to define some
void TFMain::SetNParameterSets()
{
   if(prj.nParaSets()>0)
      LaRuns->Caption = "You've defined " + AnsiString(prj.nParaSets()) + " parameter set(s).";
   else
      LaRuns->Caption = "Hit 'Edit Tasks' to define parameter sets!";
}


//----------------------------------------------------------------------------------------------------------------------
// initialize parameters in group box 'Parameters' in tab sheet 'Basic' with given values
void TFMain::SetParameters(const TParaSet& para)
{
   f_LearnParaInitialized = true;            // set flag

   // a) copy from standard parameter object
   learnPara = para;


   // b) set texts in parameter edit fields
   if(!CbClassification->Checked)                                                // parameter 'Intervals'
      EIntervals->Text = ValueToText1(learnPara.N_Int);                          // regression: # output intervals
   else
      EIntervals->Text = ValueToText1(prj.nClasses());                           // classifciation: # classes
   EWmin        ->Text = ValueToText1(learnPara.w_COD, 0, PREC_PARA);            // w_COD
   EEta         ->Text = ValueToText1(learnPara.Eta);                            // eta
   ECardinality ->Text = ValueToText1(learnPara.p_min);                          // min. cuboid mass
   ESigma       ->Text = ValueToText1(learnPara.Sigma, 0, PREC_PARA);            // sigma
   ESpecial     ->Text = ValueToText1(learnPara.W_Kernel);                       // special (kernel width)


   // c) set check box states of boolean parameters
   CbWeights  ->Checked = learnPara.Weights;                                     // use weights
   CbPrune    ->Checked = learnPara.Prune;                                       // prune cuboids
   CbEuclidean->Checked = (learnPara.Metric==2);                                 // euclidean distance

   // d)
   CbPruneAnyWay->Checked = prj.PruneAnyway();
   CbKillCuboids->Checked = prj.KillCuboids();
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Load' on tab sheet 'Basic' clicked:  Open file open dialog and load selected test
// data file
void __fastcall TFMain::BtLearnDataClick(TObject *Sender)
{
   // a) if learn data is already loaded (or model imported)
   if(data_L || model)
   {
      if(!f_DisableWarnings)    // ask user if he wants to load another file
         if(Application->MessageBox(szLearnDataLoaded, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
            return;
      ActionList(EmptyProject);
   }


   // b) set dialog caption/options and execute it
   OpenDialog->Title = "Open learn data file ...";
   OpenDialog->Options << ofFileMustExist << ofPathMustExist;
   OpenDialog->DefaultExt = "dat";
   OpenDialog->Filter = "ASCII data files (*.dat)|*.dat|Any file (*.*)|*.*";
   OpenDialog->FileName = "";
   if(!OpenDialog->Execute())
      return;                                // return if closed with 'Cancel' button

   // display filename
   LaLearnFile->Caption = MinimizeName(OpenDialog->FileName, LaLearnFile->Canvas, LaLearnFile->Width);


   // c) set flags and load selected data file in additional thread showing actual progress
   f_Loading = true;
   f_Stop = false;
   data_L = new TData();       // new data object

   // new thread object, note: deletes itself
   LoadDataThread = new ThrLoadData(false, data_L, OpenDialog->FileName.c_str(), &CallLoadLearnDataFinish, &f_Stop);


   // enable/show and disable/hide controls
   PbLearnData     ->Position= 0;                     // reset progress bar
   PbLearnData     ->Visible = true;                  // show progress bar
   BtLearnData     ->Enabled = false;                 // disable 'Load Data'
   BtLearnDataPause->Enabled = true;                  // enable 'Pause'
   BtLearnDataAbort->Enabled = true;                  // enable 'Abort'
   LoadDataTimer->OnTimer = &OnLoadLearnDataTimer;    // set OnTimer function
   LoadDataTimer   ->Enabled = true;                  // start progress indication timer
   ActiveControl = BtLearnDataPause;

   EnableMenus(false);                                // disbale menu entries
   ActLearnDataStatus(false);                         // ... but do not(!) actualize filename because maybe it is not
                                                      // initialized in TData object yet
}


//----------------------------------------------------------------------------------------------------------------------
// Called after learn data has been loaded:  Enable learn data and other controls

   // callback wrapper function
   void CallLoadLearnDataFinish(const bool& f_Error){
   FMain->OnLoadLearnDataFinish(f_Error);}

void TFMain::OnLoadLearnDataFinish(const bool& _f_Error)
{
   bool f_Error = _f_Error;                    // copy

   // a) enable/disable controls
   f_Loading = false;                          // reset flag
   LoadDataTimer   ->Enabled = false;          // disable progress indication timer
   BtLearnDataPause->Enabled = false;          // disable 'Pause' button
   BtLearnDataAbort->Enabled = false;          // disable 'Abort' button
   BtLearnData     ->Enabled = true;           // enable 'Load' button
   PbLearnData     ->Visible = false;          // hide progress bar
   EnableMenus(true);                          // enable menu entries


   // b) check min. # learn data tuples
   if(data_L->nTup()<MIN_LEARN_DATA_TUPLES)
   {
      char szText[256];
      sprintf(szText, "Minimum number of learn data tuples is %d!  File loading aborted!", MIN_LEARN_DATA_TUPLES);
      Application->MessageBox(szText, "Error", MB_OK | MB_ICONERROR);
      f_Error=true;
   }


   // c) if an error occured while loading -> delete data object and return (action list 'EmptyProject')
   if(f_Error)
   {
      ActionList(EmptyProject);
      ActiveControl = BtLearnData;
   }
   else
     ActionList(LearnDataLoaded);
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Pause' on tab sheet 'Learn' clicked:  Pause loading of learn data file
void __fastcall TFMain::BtLearnDataPauseClick(TObject *Sender)
{
   BtLearnDataPause   ->Visible   = false;          // hide 'Pause' button
   BtLearnDataPause   ->Refresh();
   LoadDataThread     ->Suspended = true;           // suspend thread
   LoadDataTimer      ->Enabled   = false;          // stop progress indication timer
   BtLearnDataContinue->Visible   = true;           // show 'Continue' button
   BtLearnDataContinue->Refresh();

   ActiveControl = BtLearnDataContinue;             // set focus to button 'Continue'
   LaLearnDataStatus->Caption = "Paused";           // display status
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Continue' on tab sheet 'Learn' clicked:  Resume loading of learn data file
void __fastcall TFMain::BtLearnDataContinueClick(TObject *Sender)
{
   BtLearnDataContinue->Visible = false;            // hide 'Continue' button
   BtLearnDataContinue->Refresh();
   LoadDataThread->Suspended = false;               // continue thread
   LoadDataTimer->Enabled = true;                   // enable progress indication timer again

   BtLearnDataPause->Visible = true;                // show 'Pause' button
   BtLearnDataPause->Refresh();
   ActiveControl = BtLearnDataPause;
   LaLearnDataStatus->Caption = AnsiString(data_L->StatusText());
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Abort' on tab sheet 'Basic' clicked:  Ask user and abort loading of learn data file
void __fastcall TFMain::BtLearnDataAbortClick(TObject *Sender)
{
   // a) suspend thread   note: necessary cause otherwise the code in c) could cause an exception
   BtLearnDataPauseClick(this);

   // b) ask user if he really wants to abort
   if(f_DisableQuestions || Application->MessageBox("I'm loading the learn data file! Do you really want to abort ??"
                                                   , "Question", MB_YESNO | MB_ICONQUESTION)== ID_YES)
   {
      // c) abort
      BtLearnDataContinue->Visible = false;    // hide button 'Continue'
      f_Stop = true;                           // set stop flag, this will cause thread to terminate
   }

   BtLearnDataContinueClick(this);             // continue thread
}


//----------------------------------------------------------------------------------------------------------------------
// respond to changes of classifciation check box
void __fastcall TFMain::CbClassificationClick(TObject *Sender)
{
   // a) return if nothing has changed
   const bool f_Regression = !CbClassification->Checked;    // abbreviation
   if(prj.Regression()==f_Regression)
      return;


   // b) ask user if tune results or parameter sets are present because they would be lost
   //    or inform him that his change does not have any effect until ...
   if(tuneResults || prj.nParaSets()>0)
   {
      if(Application->MessageBox("All defined parameters sets and tune results will be\
 lost.\nDo you really want to change the problem type ??", "Question", MB_YESNO | MB_ICONQUESTION)== ID_NO)
      {
         // if user selects 'No' ...
         CbClassification->Checked = !prj.Regression();     // ... restore old setting ...
         return;                                            // ... and return
      }
   }

   // inform user that this does not have any effect on learned model until model is re-learned
   else
      if(model)
         Application->MessageBox("This change does not have any effect on the learned model until you re-learn it!",
                         "Information", MB_OK | MB_ICONINFORMATION);


   // c) delete tune tasks and results
   ActionList(DeleteTuningTasks);               // tune tasks
   ActionList(DeleteTuningResults);             // tune results
   SetSavedFlag(false);                         // project unsaved now

   // d) update regression project
   prj.Regression() = f_Regression;


   // e) update GUI
   EvOutputType();
   SetNParameterSets();                         // display # parameter sets (maybe they've been deleted)
}


//----------------------------------------------------------------------------------------------------------------------
// output column controls

// update output column
void __fastcall TFMain::EOutColExit(TObject *Sender){ ChangeOutCol(atof(EOutCol->Text.c_str())-1); }
void __fastcall TFMain::EOutColKeyDown(TObject*, WORD& Key, TShiftState)
{
   if(Key==VK_RETURN)   EOutColExit(this);                    // enter pressed -> update output column
   if(Key==VK_END)      ChangeOutCol(data_L->nVar()-1);       // 'End' pressed -> set maximal possible output column
   if(Key==VK_HOME)     ChangeOutCol(0);                      // 'Pos 1' pressed -> set minimal possible output column
   if(Key==VK_UP)       ChangeOutCol(prj.GetOutCol()+1);      // 'Arrow Up' pressed -> increment output column
   if(Key==VK_DOWN)     ChangeOutCol(prj.GetOutCol()-1);      // 'Arrow Down' pressed -> decrement output column
   if(Key==VK_PRIOR)    ChangeOutCol(prj.GetOutCol()+10);     // 'Page Up' pressed -> decrement output column by 10
   if(Key==VK_NEXT)     ChangeOutCol(prj.GetOutCol()-10);     // 'Page Down' pressed -> decrement output column by 10
}

void __fastcall TFMain::EOutColClick(TObject*){ EOutCol->SelectAll();}   // select text
void __fastcall TFMain::BtOutColUpClick(TObject*)                        // button with arrow up clicked
{
   ChangeOutCol(prj.GetOutCol()+1);                                        // increment output column by one update
   ActiveControl = EOutCol;                                                // give focus back
}
void __fastcall TFMain::BtOutColDownClick(TObject*)                      // button with arrow down clicked
{
   ChangeOutCol(prj.GetOutCol()-1);       // decrement output columns by one and update
   ActiveControl = EOutCol;               // give focus back
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Edit Types' in tab sheet 'Basic' clicked:  Open variable type selection dialog
void __fastcall TFMain::BtEditTypesClick(TObject *Sender)
{
   DoIt();                    // write through output column and set data and project in TVarTypes object
   FVarTypes->ShowModal();    // show dialog
}


//----------------------------------------------------------------------------------------------------------------------
// respond to tune type radio buttons and 'Skip tuning' check box changes
void __fastcall TFMain::RbRepetitionClick     (TObject*) { EvTuneType(); }
void __fastcall TFMain::RbCrossValidationClick(TObject*) { EvTuneType(); }
void __fastcall TFMain::RbLoocvClick          (TObject*) { EvTuneType(); }
void __fastcall TFMain::CbSkipTuningClick(TObject*)
{
   if(!data_L)
      return;

   // a) initialize parameter texts in group box 'Parameters' if this has not been done so far
   if(CbSkipTuning->Checked && !f_LearnParaInitialized)
   {
      DoItExt();                  // write through output column, calculate weights and initialize standard parameters
      SetParameters(stdPara);     // set standard parameters
   }

   // b) enable/disable controls in group box 'Parameters' or 'Tune parameters'
   EnableTuneCtrls(!CbSkipTuning->Checked);
   EnableParameterCtrls(CbSkipTuning->Checked);

   prj.SetTuning(!CbSkipTuning->Checked); // write to project

   // set appropriate focus
   if(PageControl->ActivePage==TsBasic)
      SetTsLearnFocus();

   SetSavedFlag(false); // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// changes of data splitting settings
void __fastcall TFMain::TbDataSplittingChange(TObject *Sender)
{
   char text[256];
   sprintf(text, "Data Splitting %d\%", TbDataSplitting->Position);
   LaSplitting->Caption = text;                                                  // update text about slider bar
   prj.SetDataSplitting(TbDataSplitting->Position);                              // set data splitting in project

   SetSavedFlag(false); // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// write check box changes to project
void __fastcall TFMain::CbRandomizeClick(TObject *Sender)
{
   prj.Randomize() = CbRandomize->Checked;
   SetSavedFlag(false); // project not saved anymore
}

void __fastcall TFMain::CbSkippingClick(TObject *Sender)
{
   prj.Skipping() = CbSkipping->Checked;
   SetSavedFlag(false);    // project not saved anymore

   EnableTuneCtrls(!CbSkipTuning->Checked);  // enable/disable skipping parameters using this call
}


//----------------------------------------------------------------------------------------------------------------------
// controls for # repetitions/cross-validation
void __fastcall TFMain::ERepetitionExit(TObject*)
{
   Set_N_R_Tune(atof(ERepetition->Text.c_str()));     // set (will be checked)
}

void __fastcall TFMain::BtRepetitionUpClick(TObject*)  // arrow up clicked
{
   int inc = 1;                                     // increment by one for Cross-Validation
   if(RbRepetition->Checked)                        // increment by five for repetition
      inc = 5;
   Set_N_R_Tune(prj.Get_N_R_Tune()+inc);            // set in project and re-write(will be checked against constraints)
   ActiveControl = ERepetition;                     // give focus back
}
void __fastcall TFMain::BtRepetitionDownClick(TObject*)// arrow down clicked
{
   int inc = 1;                                    // decrement by one for Cross-Validation
   if(RbRepetition->Checked)                       // decrement by five for repetition
      inc = 5;
   Set_N_R_Tune(prj.Get_N_R_Tune()-inc);           // set in project and re-write (will be checked against constraints)
   ActiveControl = ERepetition;                    // give focus back
}
void __fastcall TFMain::ERepetitionClick(TObject*)
{
   ERepetition->SelectAll();
}

void __fastcall TFMain::ERepetitionKeyDown(TObject*, WORD &Key, TShiftState)
{
   if(Key==VK_RETURN)
      ERepetitionExit(this);                                   // enter pressed -> update
   else
   {                                                           // else: alter repetition count
      if(Key==VK_END)      Set_N_R_Tune(MAX_N_R_TUNE);             // 'End'        pressed -> set maximal possible
      if(Key==VK_HOME)     Set_N_R_Tune(0);                        // 'Pos 1'      pressed -> set minimal possible
      if(Key==VK_UP)       Set_N_R_Tune(prj.Get_N_R_Tune()+1);     // 'Arrow Up'   pressed -> increment
      if(Key==VK_DOWN)     Set_N_R_Tune(prj.Get_N_R_Tune()-1);     // 'Arrow Down' pressed -> decrement
      if(Key==VK_PRIOR)    Set_N_R_Tune(prj.Get_N_R_Tune()+10);    // 'Page Up'    pressed -> increment by 10
      if(Key==VK_NEXT)     Set_N_R_Tune(prj.Get_N_R_Tune()-10);    // 'Page Down'  pressed -> decrement by 10
   }
}

void TFMain::Set_N_R_Tune(const int N_R_Tune)
{
   if(!data_L)// return if learn data is not loaded   note: may happen if 'File|New' is hit when focus is on ERepetition
      return;


   prj.Set_N_R_Tune(N_R_Tune);                                    // set in project

   ERepetition->Text = prj.Get_N_R_Tune();                         // re-write text
   ERepetition->SelectAll();                                      // select text

   SetSavedFlag(false);                                           // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// controls for maximum model size
void __fastcall TFMain::EMaxSizeExit(TObject*)
{
   SetMaxSize(atof(EMaxSize->Text.c_str()));
}

void __fastcall TFMain::BtMaxSizeUpClick(TObject*)            // arrow up clicked
{
   int act = prj.GetMaxSize();               // get actual setting
   int inc =1;                               // increment by 1 if actual value is below 15, by 5 else
   if(act>=15)
      inc = 5;
   SetMaxSize(act+inc);                      // set new value
   ActiveControl = EMaxSize;                 // give focus back
}
void __fastcall TFMain::BtMaxSizeDownClick(TObject*)   // arrow down clicked
{
   int act = prj.GetMaxSize();               // get actual setting
   int dec =1;                               // decrement by 1 if actual value is below 15, by 5 else
   if(act>15)
      dec = 5;
   SetMaxSize(act-dec);                      // set new value
   ActiveControl = EMaxSize;                 // give focus back
}

void __fastcall TFMain::EMaxSizeKeyDown(TObject*, WORD &Key, TShiftState)
{
   if(Key==VK_RETURN)
      EMaxSizeExit(this);                                     // enter pressed -> update
   else
   {                                                          // else: alter max size setting
      if(Key==VK_END)      SetMaxSize(MAX_MAX_SIZE);          // 'End'        pressed -> set maximal possible
      if(Key==VK_HOME)     SetMaxSize(MIN_MAX_SIZE);          // 'Pos 1'      pressed -> set minimal possible
      if(Key==VK_UP)       SetMaxSize(prj.GetMaxSize()+1);    // 'Arrow Up'   pressed -> increment by 1
      if(Key==VK_DOWN)     SetMaxSize(prj.GetMaxSize()-1);    // 'Arrow Down' pressed -> decrement by 1
      if(Key==VK_PRIOR)    SetMaxSize(prj.GetMaxSize()+10);   // 'Page Up'    pressed -> increment by 10
      if(Key==VK_NEXT)     SetMaxSize(prj.GetMaxSize()-10);   // 'Page Down'  pressed -> decrement by 10
   }
}
void __fastcall TFMain::EMaxSizeClick(TObject*){ EMaxSize->SelectAll(); }

void TFMain::SetMaxSize(const int maxSize)
{
   prj.SetMaxSize(maxSize);                                   // set in project
                                                              // (will be checked against constraints)
   EMaxSize->Text = AnsiString(prj.GetMaxSize()) + " %";      // re-write text
   EMaxSize->SelectAll();                                     // select text

   SetSavedFlag(false);                                       // project not saved anymore
}



//----------------------------------------------------------------------------------------------------------------------
// controls for minimum compression
void __fastcall TFMain::EMinCmprExit(TObject*)
{
   SetMinCmpr(atof(EMinCmpr->Text.c_str()));
}
void __fastcall TFMain::BtMinCmprUpClick(TObject*)           // arrow up clicked
{
   int act = prj.GetMinCompression();                          // get actual setting
   SetMinCmpr(act+5);                                          // increment by 5
   ActiveControl = EMinCmpr;                                   // give focus back
}
void __fastcall TFMain::BtMinCmprDownClick(TObject*)         // arrow down clicked
{
   int act = prj.GetMinCompression();                          // get actual setting
   SetMinCmpr(act-5);                                          // set new value
   ActiveControl = EMinCmpr;                                   // give focus back
}

void __fastcall TFMain::EMinCmprKeyDown(TObject*, WORD& Key, TShiftState)
{
   if(Key==VK_RETURN)
      EMinCmprExit(this);                                               // enter pressed -> update
   else
   {                                                                    // else: alter max size setting
      if(Key==VK_END)      SetMinCmpr(MAX_MIN_COMPRESSION);             // 'End'        pressed -> set maximal possible
      if(Key==VK_HOME)     SetMinCmpr(MIN_MIN_COMPRESSION);             // 'Pos 1'      pressed -> set minimal possible
      if(Key==VK_UP)       SetMinCmpr(prj.GetMinCompression()+1);       // 'Arrow Up'   pressed -> increment by 1
      if(Key==VK_DOWN)     SetMinCmpr(prj.GetMinCompression()-1);       // 'Arrow Down' pressed -> decrement by 1
      if(Key==VK_PRIOR)    SetMinCmpr(prj.GetMinCompression()+10);      // 'Page Up'    pressed -> increment by 10
      if(Key==VK_NEXT)     SetMinCmpr(prj.GetMinCompression()-10);      // 'Page Down'  pressed -> decrement by 10
   }
}
void __fastcall TFMain::EMinCmprClick(TObject *Sender){ EMinCmpr->SelectAll(); }

void TFMain::SetMinCmpr(const int minCmpr)
{
   prj.SetMinCompression(minCmpr);                                // set in project
                                                                  // (will be checked against constraints)
   EMinCmpr->Text = AnsiString(prj.GetMinCompression()) + " %";   // re-write text
   EMinCmpr->SelectAll();                                         // select text

   SetSavedFlag(false);                                           // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Edit Tasks' in tab sheet 'Basic' clicked:  Open tune tasks dialog
void __fastcall TFMain::BtEditSetsClick(TObject *Sender)
{
   DoItExt();                       // write through output column, calculate weights and initialize standard parameters
   FEditSets->ShowModal();          // show dialog
   SetNParameterSets();             // afterwards: display # runs

   BtTune->Enabled = (prj.nParaSets()>0);    // enable button 'Tune' if tune parameter sets are defined
   SetTsLearnFocus();                        // set appropriate focus

   if(FEditSets->SomethingChanged())         // ask if somth. has changed and eventually unset save flag
      SetSavedFlag(false);
}




//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Basic' group box 'Parameters': functions which get called if one of the edit fields (TEdit) exits,
// i.e. looses focus -> parse and check entered text using CheckParameterString() and update learn parameter
// note: the commands are always the same: a) buffer text, b) select text in edit control, c) check parameter string
//       and in case of an error give the focus back to edit control and write back buffered text. Or set new value if
//       all's ok.
void __fastcall TFMain::EIntervalsExit(TObject *Sender)
{
   // note: will be disabled for classification tasks
   AnsiString tmp = EIntervals->Text;                                                   // buffer text
   EIntervals->SelectAll();
   if(CheckParameterString(true, tmp, LaIntervals->Caption, stdPara.N_Int, MIN_N_INT, INT_N_INT, MAX_N_INT))
   {
      ActiveControl = EIntervals;                                                       // error? -> give focus back
      EIntervals->Text = tmp;                                                           // write back
   }
   else
   {
      learnPara.N_Int = atof(tmp.c_str());                                              // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }
}


void __fastcall TFMain::EWminExit(TObject *Sender)
{
   AnsiString tmp = EWmin->Text;                                                        // buffer text
   EWmin->SelectAll();
   if(CheckParameterString(true, tmp, LaWmin->Caption, stdPara.w_COD, MIN_W_COD, INT_W_COD, Max_w_COD(data_L)))
   {
      ActiveControl = EWmin;                                                            // error? -> give focus back
      EWmin->Text = tmp;                                                                // write back
   }
   else
   {
      learnPara.w_COD = atof(tmp.c_str());                                              // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }

}

void __fastcall TFMain::EEtaExit(TObject *Sender)
{
   AnsiString tmp = EEta->Text;                                                         // buffer text
   EEta->SelectAll();
   if(CheckParameterString(true, tmp, LaEta->Caption, stdPara.Eta, MIN_ETA, INT_ETA, MAX_ETA))
   {
      ActiveControl = EEta;                                                             // error? -> give focus back
      EEta->Text = tmp;                                                                 // write back
   }
   else
   {
      learnPara.Eta = atof(tmp.c_str());                                                // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }
}

void __fastcall TFMain::ECardinalityExit(TObject *Sender)
{
   AnsiString tmp = ECardinality->Text;                                                 // buffer text
   ECardinality->SelectAll();
   if(CheckParameterString(true, tmp, LaCardinality->Caption, stdPara.p_min, MIN_P_MIN, INT_P_MIN, Max_P_Min(data_L)))
   {
      ActiveControl = ECardinality;                                                     // error? -> give focus back
      ECardinality->Text = tmp;                                                         // write back
   }
   else
   {
      learnPara.p_min = atof(tmp.c_str());                                              // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }
}

void __fastcall TFMain::ESpecialExit(TObject *Sender)
{
   AnsiString tmp = ESpecial->Text;                                                     // buffer text
   ESpecial->SelectAll();
   if(CheckParameterString(true, tmp, LaSpecial->Caption, stdPara.W_Kernel, MIN_W_KERNEL, INT_W_KERNEL, MAX_W_KERNEL))
   {
      ActiveControl = ESpecial;                                                         // error? -> give focus back
      ESpecial->Text = tmp;                                                             // write back
   }
   else
   {
      learnPara.W_Kernel = atof(tmp.c_str());                                           // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }

}


void __fastcall TFMain::ESigmaExit(TObject *Sender)
{
   AnsiString tmp = ESigma->Text;                                                       // buffer text
   ESigma->SelectAll();
   if(CheckParameterString(true, tmp, LaSigma->Caption, stdPara.Sigma, MIN_SIGMA, INT_SIGMA, MAX_SIGMA))
   {
      ActiveControl = ESigma;                                                           // error? -> give focus back
      ESigma->Text = tmp;                                                               // write back
   }
   else
   {
      learnPara.Sigma = atof(tmp.c_str());                                              // set new value
      SetSavedFlag(false);                                                              // project not saved anymore
   }
}


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Basic' group box 'Parameters': One of the check boxes was clicked -> update corresponding learn
// parameter value

// actualize learn parameter 'Prune' and enable/disable 'Prune Any Way' check box
void __fastcall TFMain::CbPruneClick(TObject *Sender)
{
   learnPara.Prune = CbPrune->Checked;             // actualize setting

   if(!CbPrune->Checked)
      CbPruneAnyWay->Enabled = CbPrune->Enabled;
   else
      CbPruneAnyWay->Enabled = false;

   SetSavedFlag(false);                            // project not saved anymore
}

// actualize project setting 'PruneAnyway'
void __fastcall TFMain::CbPruneAnyWayClick(TObject*)
{
   prj.PruneAnyway() = CbPruneAnyWay->Checked;
   SetSavedFlag(false);                            // project not saved anymore
}


// actualize project setting 'KillCuboids'
void __fastcall TFMain::CbKillCuboidsClick(TObject *Sender)
{
   prj.KillCuboids() = CbKillCuboids->Checked;
   SetSavedFlag(false);                            // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// actualize learn parameter 'p' (city-block or euclidian distance)
void __fastcall TFMain::CbEuclideanClick(TObject *Sender)
{
   learnPara.Metric = 1;
   if(CbEuclidean->Checked)                        // mapping of parameter 'p' from 'false/true' to '1/2'
      learnPara.Metric = 2;

   SetSavedFlag(false);                            // project not saved anymore
}

//----------------------------------------------------------------------------------------------------------------------
// actualize learn parameter 'Weights'
void __fastcall TFMain::CbWeightsClick(TObject *Sender)
{
   learnPara.Weights = CbWeights->Checked;
   SetSavedFlag(false);                            // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Basic' group box 'Parameters': One of the edit fields was clicked -> select/highlight text
void __fastcall TFMain::EIntervalsClick  (TObject *Sender){ EIntervals  ->SelectAll(); }
void __fastcall TFMain::EWminClick       (TObject *Sender){ EWmin       ->SelectAll(); }
void __fastcall TFMain::EEtaClick        (TObject *Sender){ EEta        ->SelectAll(); }
void __fastcall TFMain::ECardinalityClick(TObject *Sender){ ECardinality->SelectAll(); }
void __fastcall TFMain::ESpecialClick    (TObject *Sender){ ESpecial    ->SelectAll(); }
void __fastcall TFMain::ESigmaClick      (TObject *Sender){ ESigma      ->SelectAll(); }


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Basic' group box 'Parameters': User hits enter -> forward to exit functions
void __fastcall TFMain::EIntervalsKeyDown  (TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)EIntervalsExit(this); }
void __fastcall TFMain::EWminKeyDown       (TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)EWminExit(this);}
void __fastcall TFMain::EEtaKeyDown        (TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)EEtaExit(this);}
void __fastcall TFMain::ECardinalityKeyDown(TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)ECardinalityExit(this);}
void __fastcall TFMain::ESpecialKeyDown    (TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)ESpecialExit(this);}
void __fastcall TFMain::ESigmaKeyDown      (TObject*,WORD& Key,TShiftState){if(Key==VK_RETURN)ESigmaExit(this);}





   //-----------------------------------------------------------------------------------------------
   //
   //    Tab Sheet 'Tuning Results'
   //


//----------------------------------------------------------------------------------------------------------------------
// double click on list view: same as button 'Select' if item (parameter set) is highlighted
void __fastcall TFMain::LvTuneResultsDblClick(TObject* )
{
   if(BtSelect->Enabled)
      BtSelectClick(this);
}


//----------------------------------------------------------------------------------------------------------------------
// column header in listview on tab sheet 'Tuning Results' clicked: Sort with repect to the column clicked
void __fastcall TFMain::LvTuneResultsColumnClick(TObject*, TListColumn *Column)
{
   ColumnToSort = Column->Index;    // store column for LvTuningResultsCompare()
   LvTuneResults->AlphaSort();      // sort
}


//----------------------------------------------------------------------------------------------------------------------
// OnCompare event handler for sorting of rows in listview on tab sheet 'Tuning Results'
void __fastcall TFMain::LvTuneResultsCompare(TObject*, TListItem* Item1, TListItem* Item2, int Data, int &Compare)
{
   // a) convert list item text to floating point numbers mapping 'true'/'false' to '1'/'0'
   float a,b;
   if(ColumnToSort==0)                    // first column: access using 'Caption'
   {
      a = atof(Item1->Caption.c_str());
      b = atof(Item2->Caption.c_str());
   }
   else                                   // all other columns: access using 'SubItems->Strings[]'
   {
      int ix = ColumnToSort-1;

      const char* szA = Item1->SubItems->Strings[ix].c_str();   // get first item's text
      const char* szB = Item2->SubItems->Strings[ix].c_str();   // get 2th item's text

      if(strcmp(szA, "true")==0)       // convert to float
         a = 1;
      else
         if(strcmp(szA, "false")==0)
            a = 0;
         else
            a = atof(szA);

      if(strcmp(szB, "true")==0)       // convert to float
         b = 1;
      else
         if(strcmp(szB, "false")==0)
            b = 0;
         else
            b = atof(szB);
   }

   // b) compare
   if(a==b)
      Compare = 0;
   else
      if(a>b)
         Compare = 1;
      else
         Compare = -1;
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Tune' in tab sheet 'Basic' clicked: Start parameter tuning
void __fastcall TFMain::BtTuneClick(TObject *Sender)
{
   // 1. ask user if there are existing tune results because these will be lost
   if(tuneResults)
      if(!f_DisableWarnings && Application->MessageBox("Previous tune results will be lost! Do you want to continue ??"
                     , "Warning", MB_YESNO | MB_ICONWARNING)==ID_NO)
         return;                       // return if user selects 'No'
      else
         ActionList(DeleteTuningResults);    // else: delete old tune results


   SetSavedFlag(false);                // unset flag that project is saved


   // 2. misc.
   DoItExt();                          // write through output column and calculate weights etc.


   // 3. instantiate tune results object and thread object
   f_Tuning = true;
   f_Stop   = false;
   tuneResults = new TTune(prj.ToParaSetList(), &prj, data_L);    // new tune results object
   TuneThread = new ThrTune(false, this, tuneResults);            // new thread object    note: deletes itself
   tuneStartTime = clock();                                       // get start time
   tuneTime = 0;                                                  // ini


   // 4. enable/show and disable/hide controls
   PbTune->Position = 0;                     // reset
   LaTuneTime->Caption = "";                 // clear string
   PbTune     ->Visible = true;              // show progress bar
   BtTunePause->Enabled = true;              // enable 'Pause'
   BtTuneAbort->Enabled = true;              // enable 'Abort'
   TuningTimer->Enabled = true;              // start progress indication timer
   EnableMenus(false);                       // disable menu entries

   TsTuneResults->TabVisible = true;         // show tab sheet 'Tune Results' and
   PageControl->ActivePage = TsTuneResults;  // ... change to it
   ActiveControl = BtTunePause;
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Pause' on tab sheet 'Tune Results' clicked:  Pause tuning
void __fastcall TFMain::BtTunePauseClick(TObject *Sender)
{
   BtTunePause   ->Visible   = false;                      // hide 'Pause' button
   BtTunePause   ->Refresh();

   TuneThread    ->Suspended = true;                       // suspend thread
   TuningTimer   ->Enabled   = false;                      // stop progress indication timer
   BtTuneContinue->Visible   = true;                       // show 'Continue' button
   BtTuneContinue->Refresh();

   ActiveControl = BtTuneContinue;

   tuneTime += clock()-tuneStartTime;                      // sum up time consumed so far
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Abort' on tab sheet 'Tuning Results' clicked:  Ask user and abort tuning
void __fastcall TFMain::BtTuneAbortClick(TObject *Sender)
{
   // a) suspend thread   note: necessary cause otherwise the code in c) could cause an exception
   BtTunePauseClick(this);

   // b) ask user if he really wants to abort
   if(f_DisableQuestions || Application->MessageBox("I'm tuning! Do you really want to abort ??" , "Question",
                      MB_YESNO | MB_ICONQUESTION)== ID_YES)
   {
      // c) abort
      BtTuneContinue->Visible = false;             // hide button 'Continue'
      f_Stop = true;                               // set stop flag, this will cause thread to terminate
   }

   BtTuneContinueClick(this);                      // continue thread
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Continue' on tab sheet 'Tune Results' clicked:  Resume parameter tuning
void __fastcall TFMain::BtTuneContinueClick(TObject *Sender)
{
   BtTuneContinue->Visible = false;                      // hide 'Continue' button
   BtTuneContinue->Refresh();
   TuneThread->Suspended = false;                        // continue thread
   TuningTimer->Enabled = true;                          // enable progress indication timer again

   BtTunePause->Visible = true;                          // show 'Pause' button
   BtTunePause->Refresh();
   ActiveControl = BtTunePause;
   LaTuneStatus->Caption = AnsiString(tuneResults->GetStatusText());

   tuneStartTime = clock();                              // get new start time
}


//----------------------------------------------------------------------------------------------------------------------
// write tune results (as necessary) to list view
void TFMain::AddTuningResults()
{
   for(int i=LvTuneResults->Items->Count;i<tuneResults->GetResultsOk();i++)
      AddTuningResult(i);
}


//----------------------------------------------------------------------------------------------------------------------
// write one(!) new tune result to list view
void TFMain::AddTuningResult(const int& id)
{
   TListItem* item = LvTuneResults->Items->Add();                       // add new list item

   // copy parameter strings to list view
   TParaSet para = tuneResults->GetParameter(id);                       // abbreviation

   item->Caption = LvTuneResults->Items->Count;                         // task id
   item->SubItems->Add(ValueToText1(para.N_Int));                       // # output intervals
   item->SubItems->Add(ValueToText1(para.w_COD, 0, PREC_PARA));         // w_COD
   item->SubItems->Add(ValueToText1(para.Eta));                         // eta
   item->SubItems->Add(ValueToText1(para.p_min));                       // min. cuboid mass
   if(prj.Regression())
      item->SubItems->Add(ValueToText1(para.Sigma, 0, PREC_PARA));      // sigma
   else
      item->SubItems->Add(ValueToText1(para.W_Kernel));                 // special (kernel width)

   // boolean parameters: initialize check boxes
   item->SubItems->Add(FlagToString(para.Weights));                     // use weights
   item->SubItems->Add(FlagToString(para.Prune));                       // prune cuboids
   item->SubItems->Add(FlagToString(para.Metric==2));

   for(int i=0;i<N_TUNE_RES;i++)
      item->SubItems->Add(ValueToText1(tuneResults->GetResults(id)[i], 0, 1));   // model characteristics
}


//----------------------------------------------------------------------------------------------------------------------
// timer functions: gets called while parameters are tuned, display progress
void __fastcall TFMain::TuningTimerTimer(TObject *Sender)
{
   PbTune->Position = tuneResults->GetProgress();                        // set progress bar

   clock_t time = tuneTime + clock()-tuneStartTime;                        // get elapsed time and ...
   LaTuneTime->Caption = WriteTime2(time);                                 // display it

   LaTuneStatus->Caption = AnsiString(tuneResults->GetStatusText());     // actualize status text

   AddTuningResults();   // add tune results as necessary to list view
}


//----------------------------------------------------------------------------------------------------------------------
// gets called if tune thread terminates: disable thread controls etc.
void TFMain::OnTuningFinished()
{
   // a)
   PbTune       ->Visible = false;        // hide progress bar
   BtTunePause  ->Enabled = false;        // disable 'Pause'
   BtTuneAbort  ->Enabled = false;        // disable 'Abort'
   TuningTimer  ->Enabled = false;        // stop progress indication timer
   EnableMenus(true);                     // enable menu entries
   ActiveControl = PageControl;           // give focus to page control (tab sheets)


   // b) add tune results as necessary to list view
   AddTuningResults();


   // c) check if at least one parameter set is displayed with results
   if(LvTuneResults->Items->Count==0)
   {
      Application->MessageBox("Tuning aborted! No results present!", "Error"
            , MB_OK | MB_ICONERROR);
      ActionList(DeleteTuningResults);       // delete tune results
      PageControl->ActivePage = TsBasic;     // ... change to tab sheet 'Basic'
      return;
   }


   // d)
   f_Tuning = false;
   LaTuneStatus->Caption = "Finished";
   LaSelectText->Visible = true;                // show hint text how to select parameter set

   TListItem* item = LvTuneResults->Selected; // enable select button if a parameter set is highlighted in list view
   if(item)
      BtSelect->Enabled = true;
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Select' on tab sheet 'Tuning Results' clicked: write actually highlighted parameter set to learn parameters
// and change back to tab sheet 'Basic'
void __fastcall TFMain::BtSelectClick(TObject *Sender)
{
   // a) get selected item and it's index, return if none is selected
   TListItem* item = LvTuneResults->Selected;
   if(!item)
      return;
   const int index = ((int) atof(item->Caption.c_str()))-1;

   // b) set parameters
   SetParameters(tuneResults->GetParameter(index));

   // c)
   if(prj.DoTuning())                       // if not enabled so far ...
      CbSkipTuning->Checked = true;         // ... enable 'Skip Tuning' checkbox now
   PageControl->ActivePage = TsBasic;       // change to tab sheet 'Basics'
}


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Tuning Results': enable button 'Select' if an list item is selected
void __fastcall TFMain::LvTuneResultsSelectItem(TObject*, TListItem *Item, bool Selected)
{
   BtSelect->Enabled = Selected && !f_Tuning;
}


//----------------------------------------------------------------------------------------------------------------------
// display tune results filename if present
void TFMain::ActTuningResultsStatus()
{
   if(!f_Tuning)     // note: do not change anything if model is learned rigth now, because status is displayed via
                     //       on timer event handler
      if(szTuningFile.IsEmpty())                           // display model filename if present
      {
         LaTuneStatusHeader->Caption = "Status";
         LaTuneStatus->Caption = "Finished";
      }
      else
      {
         LaTuneStatusHeader->Caption = "File";
         LaTuneStatus->Caption = MinimizeName(szTuningFile.c_str(), LaTuneStatus->Canvas, LaTuneStatus->Width);
      }
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status on tab sheet 'Tuning Results'    note: useful for display of filenames with MinimizeName()
void __fastcall TFMain::PageControlResize(TObject *Sender)
{
   ActTuningResultsStatus();
}

   //-----------------------------------------------------------------------------------------------
   //
   //    Tab Sheet 'Model'
   //

//----------------------------------------------------------------------------------------------------------------------
// button 'Default' on tab sheet 'Basic' clicked: restore default parameters
void __fastcall TFMain::BtDefaultClick(TObject *Sender)
{
   DoItExt();                             // calculate weights, initialize standard parameters
   prj.PruneAnyway() = DEF_PRUNE_ANYWAY;  // reset defaults and ...
   prj.KillCuboids() = DEF_KILL_CUBOIDS;
   SetParameters(stdPara);                // ... set/display them

   SetSavedFlag(false);                   // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Learn' in tab sheet 'Basic' clicked: Learn model
void __fastcall TFMain::BtLearnClick(TObject *Sender)
{
   // 1. ask user if there is an existing model because this will be lost
   if(model)
      if(!f_DisableWarnings && Application->MessageBox("The learned model will be lost! Do you want to continue ??"
                                                         , "Warning", MB_YESNO | MB_ICONWARNING)==ID_NO)
         return;              // return if user selects 'No'
      else
         ActionList(DeleteModel);       // else: release old model

   SetSavedFlag(false);       // unset flag that project is saved


   // 2. convert parameters from TParaSet and project settings (TProjectG) to TParameter which is used by TPnc
   para = ToTParameter(learnPara, &prj);           // use utility function ...

   if(prj.Regression())
      para.N_Int = learnPara.N_Int;                // ... and overwrite some settings
   else
      para.N_Int = prj.nClasses();


   // 3. misc.
   DoItExt();                           // write through output column and calculate weights etc.
   data_L->Sort(0);                     // sort tuples regarding output values (ToDo: move sorting in 2nd thread)


   // 4. instantiate pnc object and thread object
   int p_min = 0;                       // initialize
   if(prj.KillCuboids())                // if cuboids with a small mass should be deleted ...
      p_min = para.p_min;               // ... set min. cuboid mass
   f_Learning = true;
   f_Stop     = false;
   pnc        = new TPnc(data_L, para);

   // new thread object    note: deletes itself
   LearnModelThread = new ThrLearnModel(false, this, pnc, para.Prune || prj.PruneAnyway(), p_min);
   learnStartTime = clock();            // get start time
   learnTime = 0;


   // 5. enable/show and disable/hide controls
   PbLearning->Position = 0;                 // reset
   LaModelTime ->Caption = "";               // clear string
   PbModelSize  ->Position = 100;
   PbLearning   ->Visible = true;            // show progress bar
   BtModelPause ->Enabled = true;            // enable 'Pause'
   BtModelAbort ->Enabled = true;            // enable 'Abort'
   LearningTimer->Enabled = true;            // start progress indication timer
   EnableMenus(false);                       // disable menu entries

   PageControl->ActivePage = TsModel;        // ... change to tab sheet 'Model'
   ActiveControl = BtModelPause;
   ActModelParameters();                     // initialize displayed parameters and values
   ActionList(ClearResults);          // clear loss results
}


//----------------------------------------------------------------------------------------------------------------------
// gets called with learn thread has finished: update controls
void TFMain::OnLearnModelFinished(TCluster* _model)
{
   // a)
   PbLearning   ->Visible = false;        // hide progress bar
   BtModelPause ->Enabled = false;        // disable 'Pause'
   BtModelAbort ->Enabled = false;        // disable 'Abort'
   LearningTimer->Enabled = false;        // stop progress indication timer
   EnableMenus(true);                     // enable menu entries
   ActiveControl = PageControl;           // give focus to page control (tab sheets)   

   f_Learning = false;


   // b) check if learning was finished (i.e. not aborted) and prepare model (set cardinality)
   model = _model;
   if(f_Stop)                                // stoped, i.e. aborted ...
   {
      PageControl->ActivePage = TsBasic;     // ... change to tab sheet 'Basic'
      ActionList(DeleteModel);               // and delete model again
   }


   // c) delete learn object as it is not needed anymore
   delete pnc;
   pnc = NULL;


   // d)
   learnTime += clock()-learnStartTime;      // sum up time consumed so far
   ActModelParameters();                     // initialize displayed parameters and values
   MnSaveModel->Enabled = true;              // enable menu entry 'Export Model' and toolbar button
   TbSaveModel->Enabled = true;              //

   ActModelStatus();                         // display "Finished"
}


//----------------------------------------------------------------------------------------------------------------------
// timer functions: gets called while model is learned, display progress
void __fastcall TFMain::OnLearningTimer(TObject *Sender)
{
   if(!pnc)    // security check: return if no object is present
      return;


   PbLearning   ->Position = 100*pnc->GetProgress(learnPara.Prune || prj.PruneAnyway()); // set progress bar
   PbModelSize  ->Position = 100*pnc->GetSizeRatio();                                    // set model size bar
   LaModelStatus->Caption  = pnc->GetStatusText();                                       // set status text

   clock_t time = learnTime + clock()-learnStartTime;                                    // get elapsed time and ...
   LaModelTime->Caption = WriteTime2(time);                                              // display it
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Pause' on tab sheet 'Model' clicked:  Pause learning of pnc model
void __fastcall TFMain::BtModelPauseClick(TObject *Sender)
{
   BtModelPause   ->Visible   = false;                        // hide 'Pause' button
   BtModelPause   ->Refresh();

   LearnModelThread->Suspended = true;                        // suspend thread
   LearningTimer  ->Enabled   = false;                        // stop progress indication timer
   BtModelContinue->Visible   = true;                         // show 'Continue' button
   BtModelContinue->Refresh();

   ActiveControl = BtModelContinue;
   LaModelStatus->Caption = "Paused";                         // display status

   learnTime += clock()-learnStartTime;                       // sum up time consumed so far
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Abort' on tab sheet 'Model' clicked:  Ask user and abort learning of pnc model
void __fastcall TFMain::BtModelAbortClick(TObject *Sender)
{
   // a) suspend thread   note: necessary cause otherwise the code in c) could cause an exception
   BtModelPauseClick(this);

   // b) ask user if he really wants to abort
   if(f_DisableQuestions || Application->MessageBox("I'm learning! Do you really want to abort ??", "Question",
                                                     MB_YESNO|MB_ICONQUESTION)==ID_YES)
   {
      // c) abort
      BtModelContinue->Visible = false;          // hide button 'Continue'
      f_Stop = true;                             // set stop flag, this will cause thread to terminate
   }

   BtModelContinueClick(this);                   // continue thread
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Continue' on tab sheet 'Model' clicked:  Resume learning of pnc model
void __fastcall TFMain::BtModelContinueClick(TObject *Sender)
{
   BtModelContinue->Visible   = false;                        // hide 'Continue' button
   BtModelContinue->Refresh();

   LearnModelThread->Suspended = false;                       // continue thread
   LearningTimer  ->Enabled   = true;                         // enable progress indication timer again
   BtModelPause   ->Visible   = true;                         // show 'Pause' button
   BtModelPause   ->Refresh();

   ActiveControl = BtModelPause;
   learnStartTime = clock();                                  // get new start time
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status on tab sheet 'Model'   note: useful for display of filenames with MinimizeName()
void __fastcall TFMain::TsModelResize(TObject *Sender)
{
   ActModelStatus();
}


//----------------------------------------------------------------------------------------------------------------------
// write model parameters to labels in tab sheets 'Model' and 'Use Model'
// note: call immediately after learn thread was started ('para' will be used) and call again after learning is
// finished (TCluster::para will be used but the two objects are identical. Also call after model has been loaded
void TFMain::ActModelParameters()
{
   // a) get parameters, either from TPnc object (while learning) or from model (when finished)
   const TParameter* para;
   if(model)
      para = model->GetParameters();           // parameters from model
   else
      if(pnc)
         para = pnc->GetParameters();           // parameters from TPnc learn object
      else
         return;     // note: should be an error ?



   // b) tab sheet 'Model' group box 'Parameters'
   TsModel->TabVisible = true;     // show tab sheet 'Model'

   // b1) set label texts
   LaModelIntervalsValue  ->Caption = ValueToText1(para->N_Int);                          // # intervals
   LaModelWminValue       ->Caption = ValueToText1(para->w_COD, 0, PREC_PARA);            // w_COD
   LaModelEtaValue        ->Caption = ValueToText1(para->Eta);                            // eta
   LaModelWeightValue     ->Caption = AnsiString(FlagToString(para->Weights));            // use feature weights
   LaModelEuclideanValue  ->Caption = AnsiString(FlagToString(para->Metric==2));          // euclidean distance


   // c) tab sheet 'Model' group box 'Model'
   if(!model)
      return;

   TsUseModel->TabVisible = true;   // show tab sheet 'Use Model'


   // else:
   GbModel->Visible = true;         // show group box 'Model'

   LaNCuboids1Value ->Caption = model->nCuboids();                                      // # cuboids
   LaNCuboids2Value ->Caption = model->nCuboidsRed();                                   // # cuboids (reduced)
   LaCuboidMassValue->Caption = ValueToText1(model->AvrMass(), 0, 1);                   // average cuboid mass
   LaCuboidSizeValue->Caption = ValueToText1(model->AvrVarPerCub(para->Prune), 0, 1);   // avr. # variables per cuboid
   LaHitrateValue   ->Caption = WritePercentage(model->AvrHitRate(), 0, 1, true);       // average hitrate

   PbModelSize->Position = 100*model->GetSizeRatio();                                   // model size


   // histogram # cuboids versus mass
   model->CalculateHistogram();             // calculate histogram counts
   CubHistChart->Visible = true;             // show histogram control
   Series1->Clear();                         // clear
   for(int h=0;h<N_BARS;h++)                 // set bar values
      if(model->Histogram(h)==0)
         Series1->AddNull("");
      else
         if(h==N_BARS-1)         // last bar collects all masses above
            Series1->AddXY(h+1, model->Histogram(h), AnsiString(model->Histogram(h)), clBlue);
         else
            if(h<model->GetParameters()->p_min)
               Series1->AddXY(h+1, model->Histogram(h), AnsiString(model->Histogram(h)), clYellow);
            else
               Series1->AddXY(h+1, model->Histogram(h), AnsiString(model->Histogram(h)), clGreen);


   // d) tab sheet 'Use Model' group boxes '... Parameters'
   SetTestParameter1();    // initialize learn and test parameters
   SetTestParameter2();    // initialize test parameters
}

//----------------------------------------------------------------------------------------------------------------------
// display model filename if present
void TFMain::ActModelStatus()
{
   if(!f_Learning)   // note: do not change anything if model is learnd rigth now, because status is displayed vi
                     //       on timer event handler
      if(szModelFile.IsEmpty())                              // display model filename if present
      {
         LaModelStatusHeader->Caption = "Status";
         LaModelStatus->Caption = "Finished";
      }
      else
      {
         LaModelStatusHeader->Caption = "File";
         LaModelStatus->Caption = MinimizeName(szModelFile.c_str(), LaModelStatus->Canvas, LaModelStatus->Width);
      }
}



   //-------------------------------------------------------------------------------------------------------------------
   //
   //    Tab Sheet 'Use Model'
   //

//----------------------------------------------------------------------------------------------------------------------
// Gets called if tab sheet 'Use Model' is shown:  Enable test data controls if test data file is loaded etc.
void __fastcall TFMain::TsUseModelShow(TObject *Sender)
{
   EnableTestDataCtrls(data_T!=NULL);                          // a) enable test data controls if test data is loaded
   ActTestStatus();                                            //    ... file name if simulation output file is given
   ActTestDataStatus();

   if(!data_T)                                                 // set focus ...
      ActiveControl = BtTestData;                              // ... or else to 'Load' button
   // note: the focus was already given to button 'Test' or 'Set Output' in EnableTestDataCtrl() if test data is given

   if(!model)      // return if no model exists  note: function should not be called in this case
      return;


   // c) group box 'Loss Function Results'
   const bool f_Regression = model->GetParameters()->f_Regression;   // abbreviation


   // c1) hide 2nd row of loss result values if it's a classification task
   LaLossName2   ->Visible = f_Regression;
   LaOverallLoss2->Visible = f_Regression;
   LaInsideLoss2 ->Visible = f_Regression;
   LaOutsideLoss2->Visible = f_Regression;
   BeLossLine2   ->Visible = f_Regression;

   // c2) set loss names and height of separating vertical lines
   LaLossName1->Caption = "Mae";       // name ini
   LaLossName2->Caption = "Mse";

   int height = 80;                    // height ini
   if(!f_Regression)
   {
      height = 50;
      LaLossName1->Caption = "Mce";    // name
   }

   BeLossHorLine1->Height = height;    // set heights
   BeLossHorLine2->Height = height;
   BeLossHorLine3->Height = height;
}


//----------------------------------------------------------------------------------------------------------------------
// Called while deploying (testing) model:  Actualize progress bar and status text.
void __fastcall TFMain::OnTestTimer(TObject *Sender)
{
   PbTestModel->Position = 100*UseModelThread->Progress()/data_T->nTup();     // set gauge position

   clock_t time = testTime + clock()-testStartTime;                           // get elapsed time and ...
   LaTestTime->Caption = WriteTime2(time);                                    // display it

   ActLossFunctionResults();                                                  // display actual loss function results
}


//----------------------------------------------------------------------------------------------------------------------
// Called while test data is loaded:  Actualize progress bar and status text
void __fastcall TFMain::OnLoadTestDataTimer(TObject*)
{
   LaTestDataStatus->Caption = AnsiString(data_T->StatusText());
   PbTestData->Position = data_T->LoadDataProgress();                         // set gauge position
}


//----------------------------------------------------------------------------------------------------------------------
// hide and show, enable and disable controls in tab sheet 'Use Model'
void TFMain::EnableTestDataCtrls(const bool& f_Enable)
{
   // a) group boxes '... Parameters' (parameter editing)
   CbModify1->Enabled = f_Enable;
   CbModify2->Enabled = f_Enable;

   EnableTestTestParameterCtrls        (f_Enable && CbModify1->Checked); // enable only if check box 'Modify' is enabled
   EnableTestLearnAndTestParameterCtrls(f_Enable && CbModify2->Checked);


   // b) group box 'Simulation'
   LaSimulationFileHeader->Visible = f_Enable;
   LaTestStatusHeader    ->Visible = f_Enable;
   LaTestTimeHeader      ->Visible = f_Enable;


   BtTestOutput->Enabled = f_Enable;
   LaTestStatus->Visible = f_Enable;
   LaTestTime  ->Visible = f_Enable;

   // enable only if additionally either the test data is with output or an output filename is given
   BtTest->Enabled = f_Enable && (f_IsWithOutput || !szSimOutputFile.IsEmpty());

   // enable only if additionally the output filename is given and the test data is with output
   CbWritePredictions->Enabled = f_Enable && !szSimOutputFile.IsEmpty() && f_IsWithOutput;


   // c) group box 'Loss Function Results'  note: enable loss related objects only if test data is with output 
   LaLossName1    ->Enabled = f_Enable && f_IsWithOutput;
   LaLossName2    ->Enabled = f_Enable && f_IsWithOutput;
   LaOverallLoss1 ->Enabled = f_Enable && f_IsWithOutput;
   LaOverallLoss2 ->Enabled = f_Enable && f_IsWithOutput;
   LaInsideLoss1  ->Enabled = f_Enable && f_IsWithOutput;
   LaInsideLoss2  ->Enabled = f_Enable && f_IsWithOutput;
   LaOutsideLoss1 ->Enabled = f_Enable && f_IsWithOutput;
   LaOutsideLoss2 ->Enabled = f_Enable && f_IsWithOutput;
   LaInsideRatio  ->Enabled = f_Enable;
   LaTestCuboids  ->Enabled = f_Enable;
   LaResultHeader1->Enabled = f_Enable && f_IsWithOutput;
   LaResultHeader2->Enabled = f_Enable && f_IsWithOutput;
   LaResultHeader3->Enabled = f_Enable && f_IsWithOutput;
   LaResultHeader4->Enabled = f_Enable;                     // inside ratio
   LaResultHeader5->Enabled = f_Enable;                     // # cuboids


   // d) if enable flag is set: give focus to button 'Test' or 'Set Output'
   if(f_Enable)
      if(f_IsWithOutput || !szSimOutputFile.IsEmpty())
         ActiveControl = BtTest;
      else
         ActiveControl = BtTestOutput;
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status on tab sheet 'Use Model'   note: useful for display of filenames with MinimizeName()
void __fastcall TFMain::TsUseModelResize(TObject *Sender)
{
   // call to update displayed filename (because mabe it have been or now has to be minimized)
   ActTestDataStatus();
   ActTestStatus();
}


//----------------------------------------------------------------------------------------------------------------------
// actualize test data status on tab sheet 'Use Model'
void TFMain::ActTestDataStatus(const bool& f_ActFilename)
{
   // group box 'Test Data': display bold hint if no test data file is loaded or sdisplay filename etc.
   if(data_T)
   {
      LaTestFileHeader->Caption = "File";

      // set filename   note: file name was set after it was specified in OnLoad...
      if(f_ActFilename)
         LaTestFile->Caption = MinimizeName(data_T->LoadFileName(), LaTestFile->Canvas, LaTestFile->Width);


      LaTestDataStatusHeader->Visible = true;
      LaTestDataStatus      ->Visible = true;


      if(!f_Loading)
      {
         // display # variables and tuples
         char text[256];
         sprintf(text, "%d variables and %d tuples", data_T->nVar(), data_T->nTup());
         LaTestDataStatus->Caption = text;
      }
      // note: "Loading" is displayed via OnTimer event handler
   }
   else
   {
      LaTestFileHeader->Caption = "Hit button to load test data file!";  // display hint that no test data is loaded
      LaTestFile      ->Caption = "";

      LaTestDataStatusHeader->Visible = false;
      LaTestDataStatus      ->Visible = false;
   }
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable controls in tab sheet 'Use Model' group box 'Test Parameters'
void TFMain::EnableTestTestParameterCtrls(const bool& f_Enable)
{
   // enable/disable parameter controls
   ETestCardinality ->Enabled = f_Enable;
   ETestSigma       ->Enabled = f_Enable;
   ETestSpecial     ->Enabled = f_Enable;

   LaTestCardinality->Enabled = f_Enable;
   LaTestSigma      ->Enabled = f_Enable;
   LaTestSpecial    ->Enabled = f_Enable;

   // disable pruning flag if it cannot be changed, i.e. must be either true (no original cuboids) or false
   // (no pruning info)
   CbTestPrune->Enabled = f_Enable;       // normal operation
   if(model)
      if(!model->HasPruningInformation() || !model->HasOriginalCuboids()) // but eventually disable
         CbTestPrune->Enabled = false;
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable controls in tab sheet 'Use Model' group box 'Learn and Test Parameters'
void TFMain::EnableTestLearnAndTestParameterCtrls(const bool& f_Enable)
{
   CbTestWeights  ->Enabled = f_Enable;
   CbTestEuclidean->Enabled = f_Enable;
}


//----------------------------------------------------------------------------------------------------------------------
// initialize parameter controls in tab sheet 'Use Model' group box 'Test Parameters' with parameters stored in
// model
void TFMain::SetTestParameter1()
{
   // a) check and get parameter object
   if(!model)                                           // check: return if no model is present
      return;
   const TParameter* para = model->GetParameters();     // get parameter object from model


   // b) copy parameters from model to project if modify flag is not set
   if(!CbModify1->Checked)
   {
      prj.p_min()        = para->p_min;
      prj.Sigma()        = para->Sigma;
      prj.W_Kernel()     = para->W_Kernel;
      prj.Prune()        = para->Prune;
   }


   // c) set parameter edit fields and check box states
   ETestCardinality->Text = prj.p_min();
   ETestSigma      ->Text = ValueToText1(prj.Sigma(), 0, PREC_PARA);
   ETestSpecial    ->Text = ValueToText1(prj.W_Kernel());
   CbTestPrune->Checked = prj.Prune();


   // d) adjust parameter names and visibility depending on regression flag
   LaTestSpecial  ->Visible = !para->f_Regression;   // classification only
   ETestSpecial   ->Visible = !para->f_Regression;

   LaTestSigma    ->Visible = para->f_Regression;    // regression only
   ETestSigma     ->Visible = para->f_Regression;
}


//----------------------------------------------------------------------------------------------------------------------
// initialize parameter controls in tab sheet 'Use Model' group box 'Learn and Test Parameters' with parameters
// stored in model
void TFMain::SetTestParameter2()
{
   // a) check and get parameter object
   if(!model)                                           // check: return if no model is present
      return;
   const TParameter* para = model->GetParameters();     // get parameter object from model


   // b) set parameters if modify flag is set
   if(!CbModify2->Checked)
   {
      prj.UseWeights() = para->Weights;
      prj.Euclid()     = (para->Metric==2);              // map from 'p' to euclidian distance flag
   }


   // c) set parameter check box states
   CbTestWeights  ->Checked = prj.UseWeights();
   CbTestEuclidean->Checked = prj.Euclid();
}


//----------------------------------------------------------------------------------------------------------------------
// Check box 'Edit' group box 'Test Parameters' in tab sheet 'Use Model' clicked: enable/disable test parameter
// editing
void __fastcall TFMain::CbModify1Click(TObject*)
{
   // enable or disable controls
   EnableTestTestParameterCtrls(CbModify1->Checked);

   // restore parameters from model if box is unchecked
   if(!CbModify1->Checked)
      SetTestParameter1();

   // set in project
   prj.Modify1() = CbModify1->Checked;

   SetSavedFlag(false);                    // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// Check box 'Edit' group box 'Learn and Test Parameters' in tab sheet 'Use Model' clicked: enable/disable test
// and learn parameter editing
void __fastcall TFMain::CbModify2Click(TObject*)
{
   // enable/disbale controls
   EnableTestLearnAndTestParameterCtrls(CbModify2->Checked);

   // restore parameters from model if box is unchecked
   if(!CbModify2->Checked)
      SetTestParameter2();

   // set in project
   prj.Modify2() = CbModify2->Checked;

   SetSavedFlag(false);                    // project not saved anymore
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Load' on tab sheet 'Use Model' group box 'Test Data' clicked:  Open file open dialog and load selected
// test data file
void __fastcall TFMain::BtTestDataClick(TObject *Sender)
{
   // a) if test data is already loaded
   if(data_T)
   {
      if(!f_DisableWarnings)        // ask user if he wants to load another file
         if(Application->MessageBox(szTestDataLoaded, "Warning", MB_YESNO | MB_ICONWARNING)== ID_NO)
            return;
      ActionList(ClearTestData);
      SetSavedFlag(false);                    // project not saved anymore
   }


   // b) set dialog caption/options and execute it
   OpenDialog->Title = "Open test data file ...";
   OpenDialog->Options << ofFileMustExist << ofPathMustExist;
   OpenDialog->DefaultExt = "dat";
   OpenDialog->Filter = "ASCII data files (*.dat)|*.dat|Any file (*.*)|*.*";
   OpenDialog->FileName = "";
   if(!OpenDialog->Execute())
      return;                              // return if closed with 'Cancel' button
   SetSavedFlag(false);                    // project not saved anymore


   // c) set flags and load selected data file in additional thread showing actual progress
   f_Loading = true;
   f_Stop = false;
   data_T = new TData();                   // create data object

   // create thread object   note: deletes itself
   LoadDataThread = new ThrLoadData(false, data_T,OpenDialog->FileName,&CallLoadTestDataFinish,&f_Stop,model->GetDataData());


   // d) enable/show & disable/hide controls
   PbTestData->Position = 0;                          // reset progress bar
   PbTestData->Visible  = true;                       // show progress bar
   BtTestData     ->Enabled = false;                  // disable 'Load Data'
   BtTestDataPause->Enabled = true;                   // enable 'Pause'
   BtTestDataAbort->Enabled = true;                   // enable 'Abort'
   LoadDataTimer  ->OnTimer = &OnLoadTestDataTimer;   // set OnTimer function
   LoadDataTimer  ->Enabled = true;                   // start progress indication timer
   ActiveControl = BtTestDataPause;

   EnableMenus(false);                        // disable menu entries

   // set filename    note: done here; see note below
   LaTestFile->Caption = MinimizeName(OpenDialog->FileName, LaTestFile->Canvas, LaTestFile->Width);
   ActTestDataStatus(false);// ... but do not(!) actualize filename cause maybe it's not initialized in TData object yet
}


//----------------------------------------------------------------------------------------------------------------------
// Called by 'LoadData' thread object on termination: Disable/enable controls and display number of tuples and variables

   // callback wrapper function
   void CallLoadTestDataFinish(const bool& f_Error){
      FMain->OnLoadTestDataFinish(f_Error);}

void TFMain::OnLoadTestDataFinish(const bool& f_Error)
{
   // a) enable/disable controls
   f_Loading = false;                        // reset flag
   LoadDataTimer   ->Enabled = false;        // disable progress indication timer
   BtTestDataPause ->Enabled = false;        // disable button 'Pause'
   BtTestDataAbort ->Enabled = false;        // disable button 'Abort'
   BtTestData      ->Enabled = true;         // enable button 'Load'
   PbTestData      ->Visible = false;        // hide progress bar
   EnableMenus(true);                        // enable menu entries


   // b) if an error occured while loading -> release data object and return
   if(f_Error)
   {
      ActionList(ClearTestData);
      ActiveControl = BtTestData;            // set focus back to 'Load' button
      return;                                // and return
   }

   // c)
   prj.SetData2(data_T);                              // set data filename in project
   f_IsWithOutput = LoadDataThread->IsWithOutput();   // move flag in project and check in WriteThroughOutCol()


   // d) enable/show controls and set focus to 'Test' button
   if(!f_IsWithOutput)
      CbWritePredictions->Checked = true;    // ensure that it is checked if no output is present
   EnableTestDataCtrls(true);

   // e) display # variables and tuples
   ActTestDataStatus();
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Pause' on tab sheet 'Use Model' group box 'Test Data' clicked:  Pause loading of test data file
void __fastcall TFMain::BtTestDataPauseClick(TObject *Sender)
{
   BtTestDataPause->Visible = false;         // hide 'Pause' button
   BtTestDataPause->Refresh();

   LoadDataThread->Suspended = true;         // suspend thread
   LoadDataTimer->Enabled = false;           // stop progress indication timer
   BtTestDataContinue->Visible = true;       // show 'Continue' button
   BtTestDataContinue->Refresh();

   ActiveControl = BtTestDataContinue;
   LaTestDataStatus->Caption   = "Paused";   // display status
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Continue' on tab sheet 'Use Model' group box 'Test Data' clicked:  Continue loading of test data file
void __fastcall TFMain::BtTestDataContinueClick(TObject *Sender)
{
   BtTestDataContinue->Visible = false;                           // hide 'Continue' button
   BtTestDataContinue->Refresh();

   LoadDataThread->Suspended = false;                             // continue thread
   LoadDataTimer->Enabled = true;                                 // enable progress indication timer again

   BtTestDataPause->Visible = true;                               // show 'Pause' button
   BtTestDataPause->Refresh();

   ActiveControl = BtTestDataPause;
   LaTestDataStatus->Caption = AnsiString(data_L->StatusText());  // display status
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Abort' on tab sheet 'Use Model' group box 'Test Data' clicked:  Ask user and abort loading of test data
// file
void __fastcall TFMain::BtTestDataAbortClick(TObject *Sender)
{
   // a) suspend thread   note: necessary cause otherwise the code in c) could cause an exception
   BtTestDataPauseClick(this);

   // b) ask user if he really wants to abort
   if(f_DisableQuestions || Application->MessageBox("I'm loading the test data file! Do you really want to abort ??"
                                                   , "Question", MB_YESNO | MB_ICONQUESTION)== ID_YES)
   // c) abort
   {
      BtTestDataContinue->Visible = false;     // hide button 'Continue'
      f_Stop = true;                           // set stop flag, this will cause thread to terminate
   }

   BtTestDataContinueClick(this);              // continue thread
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Set Output' on tab sheet 'Use Model' group box 'Simulation' clicked:  Ask the user for the
// simulation output file
void __fastcall TFMain::BtTestOutputClick(TObject *Sender)
{
   // a) set dialog caption and options and execute it
   SaveDialog->Title = "Set simulation output file ...";
   SaveDialog->Options << ofPathMustExist << ofOverwritePrompt << ofNoReadOnlyReturn;
   SaveDialog->DefaultExt = SIM_FILE_EXTENSION;
   SaveDialog->Filter = "Simulation output file (*." + AnsiString(SIM_FILE_EXTENSION) + ")|*."
                        + AnsiString(SIM_FILE_EXTENSION);
   SaveDialog->FileName = szSimOutputFile;


   if(!SaveDialog->Execute())
      szSimOutputFile = "";                        // clear output file if closed with 'Cancel' button
   else
      szSimOutputFile = SaveDialog->FileName;      // else copy selected filename

   SetSavedFlag(false);                            // project not saved anymore


   // b) enable/disable check box 'Write Predictions' and button 'Test'
   EnableTestDataCtrls(true);

   // c) call evaluate function
   ActTestStatus();


   // note: simulation output filename in project is set just before saving
}


//----------------------------------------------------------------------------------------------------------------------
// update (learn and) test parameter object, i.e. initialize from model and perform modifications
void TFMain::UpdatePara()
{
   para = *(model->GetParameters());              // initialize from model parameters
   if(prj.Modify1())                               // if alternate settings should be used ...
   {
      para.p_min        = prj.p_min();             // ... overwrite with settings from project
      para.W_Kernel     = prj.W_Kernel();
      para.Sigma        = prj.Sigma();
      para.Prune        = prj.Prune();
   }

   if(prj.Modify2())                               // if alternate settings should be used ...
   {
      para.Weights = prj.UseWeights();             // ... overwrite with settings from project
      para.Metric = 1;
      if(prj.Euclid())                             // map from flag to 1 (city block distance) or 2 (euclidean distance)
         para.Metric = 2;
   }
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Test' on tab sheet 'Use Model' group box 'Status' clicked:  Start simulation
void __fastcall TFMain::BtTestClick(TObject *Sender)
{
   // a) initialize simulation output file (ofstream)
   ofstream* file = NULL;
   if(!szSimOutputFile.IsEmpty())                           // if filename specified
   {
      file = new ofstream(szSimOutputFile.c_str());         // create file ...  (will be deleted in thread object)
      if(!file)                                             // ... and check success
      {
         Application->MessageBox("Unable to open simulation output file!", "Error", MB_OK | MB_ICONERROR);
         return;                                            // abort if failed
      }
   }


   // b) set flags
   f_Testing = true;
   f_Stop    = false;


   // c) create loss object
   // note: pass # tuples to loss object if preditions shall be stored (and later written to output file)
   int nTup=-1;
   if(CbWritePredictions->Checked)
      nTup = data_T->nTup();

   // new loss object   note: gets deleted in finish function
   loss = new TLossFunction(model->GetDataData()->MeanY(), nTup);
   testStartTime = clock();                                        // get start time
   testTime = 0;


   // d) update test parameters
   UpdatePara();


   // e) create thread object: will call TPrediction::Test() in its Execute() function
   UseModelThread = new ThrUseModel(false, this, data_T, f_IsWithOutput, loss, model, &para, file);


   // f) enable/show and disable/hide controls
   LaTestTime  ->Caption  = "0m00s";                  // reset

   TFontStyles style = LaTestStatus->Font->Style;     // get control's font style
   style >> fsBold;                                   // unset bold style
   LaTestStatus->Font->Color = clBlack;               // set normal color
   LaTestStatus->Font->Style = style;                 // set style
   LaTestStatus->Caption  = "Testing";                // display status


   PbTestModel->Position = 0;          // reset progress bar position
   PbTestModel->Visible = true;        // show progress bar
   BtTest     ->Visible = false;       // hide 'Test' Button (will show 'Abort' button below)
   BtTestAbort->Enabled = true;
   BtTestPause->Enabled = true;        // enable 'Pause' Button
   TestTimer  ->Enabled = true;        // start progress indication timer
   ActiveControl = BtTestPause;
   EnableMenus(false);                 // disable menu entries
   ActionList(ClearResults);           // clear displayed results

   GbTestPara1->Enabled = false;       // disable group boxes while testing
   GbTestPara2->Enabled = false;
   GbTestData ->Enabled = false;
}


//----------------------------------------------------------------------------------------------------------------------
// Called by 'ThrUseModel' thread object on termination:  Diable/enable controls and display loss results
void TFMain::OnDeployFinish()
{
   // a) alter control states
   f_Testing = false;                  // reset
   TestTimer   ->Enabled = false;      // disable progress indication timer
   BtTestPause ->Enabled = false;      // disable button 'Pause'
   BtTestAbort ->Enabled = false;      // disable button 'Abort'
   BtTest      ->Visible = true;       // show 'Test' button
   PbTestModel ->Position = 0;         // reset progress bar
   PbTestModel ->Visible = false;      // hide progress bar
   EnableMenus(true);                  // enable menu entries

   GbTestPara1->Enabled = true;        // enable group boxes again
   GbTestPara2->Enabled = true;
   GbTestData ->Enabled = true;


   // display status
   TFontStyles style = LaTestStatus->Font->Style;            // get control's font style
   if(f_Stop)
   {
      style << fsBold;                                       // set bold style
      LaTestStatus->Font->Color = clRed;                     // set highlighted color
      LaTestStatus->Caption = "Aborted";
   }
   else
      LaTestStatus->Caption = "Finished";

   LaTestStatus->Font->Style = style;                        // set style

   // display warning if module is empty
   if(model->nCuboidsRed()==0 && !f_DisableWarnings)
      Application->MessageBox(szModuleIsEmpty, "Warning", MB_OK | MB_ICONWARNING);



   // b) display loss function results
   if(f_IsWithOutput)                              // if test data is with output ...
   {
      ActLossFunctionResults();                    // ... then display loss function results and additionally ...
      if(!model->GetParameters()->f_Regression)    // ... display a pie chart for classification problems
      {
         MceChart->Visible = true;                                                     // show chart
         Series2->Clear();                                                             // clear chart data
         Series2->Add(100*loss->Mce(), AnsiString(loss->Mce()), clRed);                // set pies
         Series2->Add(100-(100*loss->Mce()), AnsiString(loss->Mce()), clGreen);
      }
   }

   // c) ratio of inside predictions and # cuboids
   LaInsideRatio->Caption = WritePercentage(loss->InsideRatio(), 0, 1, true);
   LaTestCuboids->Caption = model->nCuboidsRed();


   // d) clean up and set focus to 'Test' button (again)
   delete loss;
   loss = NULL;
   ActiveControl = BtTest;
}


//----------------------------------------------------------------------------------------------------------------------
// display actual loss function results on tab sheet 'Use'
void TFMain::ActLossFunctionResults()
{
   const bool f_Classification = !model->GetParameters()->f_Regression;  // flag: classification task

   if(f_Classification)

   // a) classification: show mean classification error (Mce)
   {
      LaOverallLoss1->Caption = WritePercentage(loss->Mce(), 6, PREC_LOSS_PERCENTAGE, true);     // overall
      LaInsideLoss1 ->Caption = WritePercentage(loss->MceIns(), 6, PREC_LOSS_PERCENTAGE, true);  // inside
      LaOutsideLoss1->Caption = WritePercentage(loss->MceOut(), 6, PREC_LOSS_PERCENTAGE, true);  // outside
   }
   else

   // b) regression: show mean absolute and mean square error (Mae and Mse)
   {
      LaOverallLoss1->Caption = ValueToText1(loss->Mae(),    0, PREC_LOSS, true);    // Mae overall
      LaInsideLoss1 ->Caption = ValueToText1(loss->MaeIns(), 0, PREC_LOSS, true);    //     inside
      LaOutsideLoss1->Caption = ValueToText1(loss->MaeOut(), 0, PREC_LOSS, true);    //     outside
      LaOverallLoss2->Caption = ValueToText1(loss->Mse(),    0, PREC_LOSS, true);    // Mse overall
      LaInsideLoss2 ->Caption = ValueToText1(loss->MseIns(), 0, PREC_LOSS, true);    //     inside
      LaOutsideLoss2->Caption = ValueToText1(loss->MseOut(), 0, PREC_LOSS, true);    //     outside
   }


   // c) ratio of inside predictions note: partly redundant. Added here as this function gets called
   //    by an Ontimer event handler while testing is done ...
   LaInsideRatio->Caption = WritePercentage(loss->InsideRatio(), 0, 1, true);


   // d) # cuboids   note: if the model size is zero, then display it highlighted as warning
   if(model->nCuboidsRed()==0)
      SetLabelFontStyleAndColor(LaTestCuboids, true, clHighlight);
   else
      SetLabelFontStyleAndColor(LaTestCuboids, false, clWindowText);
   LaTestCuboids->Caption = model->nCuboidsRed();
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Pause' on tab sheet 'Use Model' group box 'Simulation' clicked:  Resume simulation
void __fastcall TFMain::BtTestPauseClick(TObject *Sender)
{
   BtTestPause   ->Visible   = false;                       // hide 'Pause' button
   BtTestPause   ->Refresh();

   UseModelThread  ->Suspended = true;                      // suspend thread
   TestTimer     ->Enabled   = false;                       // stop progress indication timer
   BtTestContinue->Visible   = true;                        // show 'Continue' button
   BtTestContinue->Refresh();

   ActiveControl = BtTestContinue;
   LaTestStatus  ->Caption   = "Paused";                    // display status

   testTime += clock()-testStartTime;                       // sum up time consumed so far
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Continue' on tab sheet 'Use Model' group box 'Simulation' clicked:  Resume simulation
void __fastcall TFMain::BtTestContinueClick(TObject *Sender)
{
   BtTestContinue->Visible   = false;                       // hide 'Continue' button
   BtTestContinue->Refresh();

   UseModelThread->Suspended = false;                       // continue thread
   TestTimer     ->Enabled   = true;                        // enable progress indication timer again

   BtTestPause   ->Visible   = true;                        // show 'Pause' button
   BtTestPause   ->Refresh();

   ActiveControl = BtTestPause;
   LaTestStatus  ->Caption   = "Testing";                   // display status

   testStartTime = clock();                                 // get new start time
}


//----------------------------------------------------------------------------------------------------------------------
// Button 'Abort' on tab sheet 'Use Model' group box 'Simulation' clicked:  Ask user and abort simulation
void __fastcall TFMain::BtTestAbortClick(TObject *Sender)
{
   // a) suspend thread   note: necessary cause otherwise the code in c) could cause an exception
   BtTestPauseClick(this);

   // b) ask user if he really wants to abort
   if(f_DisableQuestions || Application->MessageBox("I'm tesing! Do you really want to abort ??", "Question",
                                                      MB_YESNO|MB_ICONQUESTION)== ID_YES)
   {
      // c) abort
      BtTestContinue->Visible = false;          // hide button 'Continue'
      f_Stop = true;                            // set stop flag, this will cause thread to terminate
   }

   BtTestContinueClick(this);         // continue thread
}


//----------------------------------------------------------------------------------------------------------------------
// actualize status in group box 'Simulation' in tab sheet 'Use Model'
void TFMain::ActTestStatus()
{
   const bool f_File = !szSimOutputFile.IsEmpty();             // check if filename is given

   // display simulation output filename if it's specified
   if(f_File)
   {
      LaSimulationFile->Visible = true;
      LaSimulationFileHeader->Caption = "File";
      LaSimulationFile->Caption = MinimizeName(szSimOutputFile, LaSimulationFile->Canvas, LaSimulationFile->Width);   // set filename
   }
   else
   {
      // show hint that no simulation output file is specified
      LaSimulationFile->Visible = false;
      LaSimulationFileHeader->Caption = "Hit button to set output file!";
   }
}



//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Use Model' group box '... Parameters': functions which get called if one of the edit fields (TEdit)
// exits, i.e. looses focus -> parse and check entered text using CheckParameterString() and update corresponding learn
// parameter
// note: the commands are always the same: a) buffer text, b) select text in edit control, c) check parameter string
//       and in case of an error give the focus back to edit control and write back buffered text. Or set new value if
//       all's ok.
void __fastcall TFMain::ETestCardinalityExit(TObject *Sender)
{
   AnsiString tmp = ETestCardinality->Text;                                                // buffer text
   ETestCardinality->SelectAll();
   if(CheckParameterString(true, tmp, LaTestCardinality->Caption, DEF_P_MIN, MIN_P_MIN, INT_P_MIN))    // check text
   {
      ActiveControl = ETestCardinality;                                                    // error? -> give focus back
      ETestCardinality->Text = tmp;                                                        // write back
   }
   else
   {
      prj.p_min() = atof(tmp.c_str());                                                     // set new value
      SetSavedFlag(false);                                                                 // project not saved anymore
   }
}

void __fastcall TFMain::ETestSpecialExit(TObject *Sender)
{
   AnsiString tmp = ETestSpecial->Text;                                                    // buffer text
   ETestSpecial->SelectAll();
   if(CheckParameterString(true, tmp, LaTestSpecial->Caption, DEF_W_KERNEL, MIN_W_KERNEL, INT_W_KERNEL, MAX_W_KERNEL))
   {
      ActiveControl = ETestSpecial;                                                        // error? -> give focus back
      ETestSpecial->Text = tmp;                                                            // write back
   }
   else
   {
      prj.W_Kernel() = atof(tmp.c_str());                                                  // set new value
      SetSavedFlag(false);                                                                 // project not saved anymore
   }
}

void __fastcall TFMain::ETestSigmaExit(TObject *Sender)
{
   AnsiString tmp = ETestSigma->Text;                                                      // buffer text
   ETestSigma->SelectAll();
   if(CheckParameterString(true, tmp, LaTestSigma->Caption, DEF_SIGMA, MIN_SIGMA, INT_SIGMA, MAX_SIGMA))
   {
      ActiveControl = ETestSigma;                                                          // error? -> give focus back
      ETestSigma->Text = tmp;                                                              // write back
   }
   else
   {
      prj.Sigma() = atof(tmp.c_str());                                                     // set new value
      SetSavedFlag(false);                                                                 // project not saved anymore
   }
}


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Use Model' group box '.. Parameters': One of the check boxes clicked -> update corresponding parameter
void __fastcall TFMain::CbTestPruneClick(TObject*){ prj.Prune()=CbTestPrune->Checked; SetSavedFlag(false);}
void __fastcall TFMain::CbTestWeightsClick(TObject*){ prj.UseWeights()=CbTestWeights->Checked; SetSavedFlag(false);}
void __fastcall TFMain::CbTestEuclideanClick(TObject*){ prj.Euclid()=CbTestEuclidean->Checked; SetSavedFlag(false);}

//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Use Model' group box '... Parameters': User hits enter -> forward to exit functions
void __fastcall TFMain::ETestCardinalityKeyDown(TObject*, WORD& Key, TShiftState){
   if(Key==VK_RETURN)
   ETestCardinalityExit(this); }
void __fastcall TFMain::ETestSpecialKeyDown(TObject*, WORD& Key, TShiftState){
   if(Key==VK_RETURN)
   ETestSpecialExit(this); }
void __fastcall TFMain::ETestSigmaKeyDown(TObject*, WORD& Key, TShiftState){
   if(Key==VK_RETURN)
   ETestSigmaExit(this); }


//----------------------------------------------------------------------------------------------------------------------
// tab sheet 'Use Model' group box '.. Parameters': One of the edit fields/check boxes was clicked -> highlight text
void __fastcall TFMain::ETestCardinalityClick(TObject*) { ETestCardinality->SelectAll(); }
void __fastcall TFMain::ETestSpecialClick    (TObject*) { ETestSpecial    ->SelectAll(); }
void __fastcall TFMain::ETestSigmaClick      (TObject*) { ETestSigma      ->SelectAll(); }


//----------------------------------------------------------------------------------------------------------------------
// set saved flag and enable save project menu item and toolbar button if learn data is present
void TFMain::SetSavedFlag(const bool& f_Set)
{
   f_Saved = f_Set;                       // set flag
   MnSaveProject->Enabled = !f_Saved && data_L;     // enable/disable menu and tool bar button
   TbSaveProject->Enabled = !f_Saved && data_L;
}


//----------------------------------------------------------------------------------------------------------------------
// response to check box 'Write Predictions' on tab sheet 'Use Model'
void __fastcall TFMain::CbWritePredictionsClick(TObject *Sender)
{
   prj.WritePredictions() = CbWritePredictions->Checked;
   SetSavedFlag(false);
}



//----------------------------------------------------------------------------------------------------------------------
// change background in listview on tab sheet 'Tuning Results' to warn the user not to chose "bad" parameter sets
void __fastcall TFMain::LvTuneResultsCustomDrawSubItem(TCustomListView *Sender, TListItem *Item, int SubItem,
      TCustomDrawState State, bool &DefaultDraw)
{
   // initialize with default (white background)
   // note: there seems to be a bug in the component if one uses real white!
   Sender->Canvas->Brush->Color=TColor(RGB(254, 254, 254));


   // change background of some columns/parameters/characteristics if some thresholds are exceeded ...
   float nCub, nCubRed, fac, nVarPerCub, size;
   switch(SubItem)
   {
      // a) parameter 'Weights':  set to light yellow if disabled
      case 6 : if(strcmp("false", Item->SubItems->Strings[SubItem-1].c_str())==0)   // check if it is disabled and ...
               Sender->Canvas->Brush->Color=TColor(RGB(255, 255, 125));             // ... set color to yellow if it is
               break;

      // b) parameter 'Prune':  set to light red if pruning is disabled
      case 7 : if(strcmp("false", Item->SubItems->Strings[SubItem-1].c_str())==0)   // check if it is disabled and ...
               Sender->Canvas->Brush->Color=TColor(RGB(255, 125,125));              // ... set color to red if it is
               break;

      // c) # cuboids: Check if minimum compression rate approaches threshold
      case 9 : nCub = atof(Item->SubItems->Strings[SubItem-1].c_str());             // # cubooids
               fac = (nCub/prj.Get_N_L_Tune()-prj.GetMinCompression()/200.0)        // warn if actual value exceeds
                        /(prj.GetMinCompression()/100.0);                           // half of the threshold
               fac = 1-max((float) 0.0, min(fac, (float) 1.0));                     // bound to [0,1] and "invert"
               Sender->Canvas->Brush->Color=TColor(RGB(255, 255,fac*125+124));      // set color
               break;

      // d) # cubooids (reduced): Check if model size approaches threshold
      case 10: nCubRed    = atof(Item->SubItems->Strings[SubItem-1].c_str());       // # cuboids reduced
               nVarPerCub = atof(Item->SubItems->Strings[SubItem].c_str());         // avr. # variables per cuboid
               size = 2*nCubRed*nVarPerCub/(data_L->nVar()-1)/prj.Get_N_L_Tune();   // model size in percent
               fac = (size-prj.GetMaxSize()/200.0)/(prj.GetMaxSize()/100.0);        // warn if actual value exceeds
                                                                                    // half of the threshold
               fac = 1-max((float) 0.0, min(fac, (float) 1.0));                     // bound to [0,1] and "invert"
               Sender->Canvas->Brush->Color=TColor(RGB(255, 255,fac*125+124));
               break;
   }
}