/*
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|          Program Name: ParSer                                                                                         |
|                Author: Michael Krämer                                                                                 |
|                        Michael@dk8pp.de             (private)                                                         |
|                        Michael.Kraemer@renesas.com  (business)                                                        |
|                  Date: 15-Jan-2012                                                                                    |
|              Language: Microsoft Visual C++ Express 2010                                                              |
|               Version: 0.98                                                                                           |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|  Purpose: Find optimum set of resistors or capacitors for a given total resistance or capacitance.                    |
|                                                                                                                       |
|  To do for next version:                                                                                              |
|  - Terminate individual search loops, if values exceed the maximum deviation (partly done)                            |
|  - Terminate loop if target cannot be achieved with current value (partly done)                                       |
|  - multi threaded operation for speed improvement                                                                     |
|                                                                                                                       |
|  History: 0.93 initial released version                                                                               |
|           0.94 new email addresses included, comments updated, no functional changes, compiled with OW 1.6            |
|           0.96 changed to use WinLib. Registry is used to store parameters. Brush up of code. No functional changes.  |
|           0.97 terminations of search loop improved. Speed up of progress display.                                    |
|           0.98 moved away from Watcom and used MS Visual C instead. The original reason was the inferior resource     |
|                editor that comes with Watcom and the many problems of the free ResEdit (http://www.resedit.net/)      |
|                with Watcom. ResEdit works fine (so far) with Visual C and the VC code is much faster than the OW      |
|                code.                                                                                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Includes                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <math.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
#include "resource.h"
#include "WinLib.h"

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Defines                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define PARMSIZE 128            /* maximum size of a string or positional command line parameter (e.g. filename) */
#define STRINGSIZE 256          /* default size for strings */
#define VALSIZE 32              /* maximum size for values */
#define MAX_TARGET 100E6        /* maximum target value */
#define RDECADES 8              /* number of decades for resistors */
#define CDECADES 10             /* number of decades for capacitors */
#define MAXDECADES 10           /* max(RDECADES,CDECADES) */
#define NOS 500                 /* maximum number of solutions */

#define MY_HKEY HKEY_CURRENT_USER /* handle of registry key */
#define MY_INI_SECT "ParSer"    /* Section name or registry key used by this program */
#define MY_REG_KEY MY_REG_KEY_ROOT MY_INI_SECT /* Registry key used by this program */
#define PROGRAMNAME "Parallel-Serial (ParSer)"
#define PURPOSE "This program determines the best set of resistors or capacitors\r\r\n"\
                "to generate a predefined value.\r\r\n"\
                "\r\r\n"
#define PROGRAMVERSION 100      /* will be displayed as n.nn */

/* private messages */
#define USER_PRINTRESULTS 100   /* output results to the screen */
#define USER_PRINTCOUNT   101   /* print number of found solutions */

#define FLAG_DLG_E6      0x00000001 /* flag to indicate checked state */
#define FLAG_DLG_E12     0x00000002 /* flag to indicate checked state */
#define FLAG_DLG_E24     0x00000004 /* flag to indicate checked state */
#define FLAG_DLG_E48     0x00000008 /* flag to indicate checked state */
#define FLAG_DLG_E96     0x00000010 /* flag to indicate checked state */
#define FLAG_DLG_E192    0x00000020 /* flag to indicate checked state */
#define FLAG_DLG_TOPOS   0x00000100 /* flag to indicate checked state */
#define FLAG_DLG_TOPOPP  0x00000200 /* flag to indicate checked state */
#define FLAG_DLG_TOPOSS  0x00000400 /* flag to indicate checked state */
#define FLAG_DLG_TOPOPPP 0x00000800 /* flag to indicate checked state */
#define FLAG_DLG_TOPOSSS 0x00001000 /* flag to indicate checked state */
#define FLAG_DLG_TOPOSPP 0x00002000 /* flag to indicate checked state */
#define FLAG_DLG_TOPOSSP 0x00004000 /* flag to indicate checked state */
#define FLAG_DLG_IEC     0x00008000 /* flag to indicate IEC set is selected */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       structs and enums                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
*/

typedef enum {S, SS, PP, PPP, SPP, SSS, SSP, SSS1, SSS2, PPS} CONF ;
/* CONF is the configuration of the network.
   S:    1 single R/C
   SS:   2 serial R/C
   PP:   2 parallel R/C
   PPP:  3 parallel R/C
   SPP:  1 serial R/C, 2 parallel R/C
   SSS:  3 serial R/C
   SSP:  2 serial R/C, 1 parallel R/C

the following definitions are for resistor networks only
   SSS1: 3 serial R, tap between first two resistors
   SSS2: 3 serial R, tap between last two resistors
   PPS:  2 parallel R, 1 serial R               */

typedef enum {R, C, RN, COMPSIZE, NONE} COMP ;

typedef struct {double target ;
                double res ;
                double val1 ;
                double val2 ;
                double val3 ;
                double err ;
                CONF conf ;
                bool valid ;} SOLUTION ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       global variables                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
*/
HINSTANCE       MyInstance ;
HWND            MainWindow ;
char static ini_filename[MAX_PATH+1] ;
char static ps_buffer[STRINGSIZE+1] ;

/*
 Registry definitions
*/
int32_t static registry_x = 0 ;                     /* left edge of dialog */
int32_t static registry_y = 0 ;                     /* top edge of dialog */
int32_t static registry_width = 0 ;                 /* width of dialog */
int32_t static registry_height = 0 ;                /* height of dialog */
int32_t static registry_version = PROGRAMVERSION ;  /* version of program which wrote the registry */
int32_t static registry_flags[COMPSIZE] = {FLAG_DLG_E24 + FLAG_DLG_TOPOS + FLAG_DLG_TOPOPP + FLAG_DLG_TOPOSS + FLAG_DLG_IEC,
                                           FLAG_DLG_E24 + FLAG_DLG_TOPOS + FLAG_DLG_TOPOPP + FLAG_DLG_TOPOSS + FLAG_DLG_IEC,
                                           FLAG_DLG_E24 + FLAG_DLG_TOPOS + FLAG_DLG_IEC} ;     /* flags */
int32_t static registry_component = R ;             /* selected component */
char static registry_target[COMPSIZE][VALSIZE+1] ;  /* target value */
char static registry_maxerr[COMPSIZE][VALSIZE+1] ;  /* maximum error */
char static registry_maxdev[COMPSIZE][VALSIZE+1] ;  /* maximum deviation */
char static registry_u2[VALSIZE+1] ;                /* U2 */
char static registry_imin[VALSIZE+1] ;              /* Imin */
char static registry_imax[VALSIZE+1] ;              /* Imax */
char static registry_iload[VALSIZE+1] ;             /* Iload */
char static registry_rlist_filename[MAX_PATH+1] = "" ;   /* filename for private resistor list */
char static registry_clist_filename[MAX_PATH+1] = "" ;   /* filename for private capacitor list */
INIVALUE static inivalue[] = {{"Window", "PosX", REG_DWORD, REGFLAG_WRITEBACK, &registry_x, 1},
                              {"Window", "PosY", REG_DWORD, REGFLAG_WRITEBACK, &registry_y, 1},
                              {"Window", "Width", REG_DWORD, REGFLAG_WRITEBACK, &registry_width, 1},
                              {"Window", "Height", REG_DWORD, REGFLAG_WRITEBACK, &registry_height, 1},
                              {"Program", "Version", REG_DWORD, REGFLAG_WRITEBACK, &registry_version, 1},
                              {"Settings", "component", REG_DWORD, REGFLAG_WRITEBACK, &registry_component, 1},
                              {"Settings", "target_r", REG_SZ, REGFLAG_WRITEBACK, &registry_target[R], VALSIZE},
                              {"Settings", "target_c", REG_SZ, REGFLAG_WRITEBACK, &registry_target[C], VALSIZE},
                              {"Settings", "target_rn", REG_SZ, REGFLAG_WRITEBACK, &registry_target[RN], VALSIZE},
                              {"Settings", "maxerr_r", REG_SZ, REGFLAG_WRITEBACK, &registry_maxerr[R], VALSIZE},
                              {"Settings", "maxerr_c", REG_SZ, REGFLAG_WRITEBACK, &registry_maxerr[C], VALSIZE},
                              {"Settings", "maxerr_rn", REG_SZ, REGFLAG_WRITEBACK, &registry_maxerr[RN], VALSIZE},
                              {"Settings", "maxdev_r", REG_SZ, REGFLAG_WRITEBACK, &registry_maxdev[R], VALSIZE},
                              {"Settings", "maxdev_c", REG_SZ, REGFLAG_WRITEBACK, &registry_maxdev[C], VALSIZE},
                              {"Settings", "maxdev_rn", REG_SZ, REGFLAG_WRITEBACK, &registry_maxdev[RN], VALSIZE},
                              {"Settings", "u2", REG_SZ, REGFLAG_WRITEBACK, &registry_u2, VALSIZE},
                              {"Settings", "imin", REG_SZ, REGFLAG_WRITEBACK, &registry_imin, VALSIZE},
                              {"Settings", "imax", REG_SZ, REGFLAG_WRITEBACK, &registry_imax, VALSIZE},
                              {"Settings", "iload", REG_SZ, REGFLAG_WRITEBACK, &registry_iload, VALSIZE},
                              {"Settings", "C_List", REG_SZ, REGFLAG_WRITEBACK, &registry_clist_filename, MAX_PATH},
                              {"Settings", "R_List", REG_SZ, REGFLAG_WRITEBACK, &registry_rlist_filename, MAX_PATH},
                              {"Settings", "flags_r", REG_DWORD, REGFLAG_WRITEBACK+REGFLAG_HEX, &registry_flags[R], 1},
                              {"Settings", "flags_c", REG_DWORD, REGFLAG_WRITEBACK+REGFLAG_HEX, &registry_flags[C], 1},
                              {"Settings", "flags_rn", REG_DWORD, REGFLAG_WRITEBACK+REGFLAG_HEX, &registry_flags[RN], 1}} ;
MYINI static myini = {MY_HKEY, MY_REG_KEY, MY_INI_SECT ".ini", &inivalue[0], INI_NO, sizeof(inivalue)/sizeof(inivalue[0])} ;

/*
 Command line parameter definitions
*/
/* These are indexes into the CLO table below. Keep them in sync */
typedef enum {PARM_RC=0,
              PARM_RD,
              PARM_RR,
              PARM_RW,
              PARM_IF,
              PARM_ID,
              PARM_IR,
              PARM_IW,
              PARM_PP,
              PARM_PT,
              PARM_LAST} parm_enum ;

CLO clo[] = {{"rc", PARM_SIMPLE, NULL, 0, false, "clear registry entries for this program"},
             {"rd", PARM_SIMPLE, NULL, 0, false, "disable use of the registry for this program\r\r\n"
                                            "     (will use .ini file instead unless -id specified"},
             {"rr", PARM_SIMPLE, NULL, 0, false, "read initialization values from registry"},
             {"rw", PARM_SIMPLE, NULL, 0, false, "write initialization values back to registry"},
             {"if", PARM_STRING, ini_filename, MAX_PATH, false, "specify ini filename"},
             {"id", PARM_SIMPLE, NULL, 0, false, "disable use of ini file for this program"},
             {"ir", PARM_SIMPLE, NULL, 0, false, "read initialization values from ini file"},
             {"iw", PARM_SIMPLE, NULL, 0, false, "write initialization values back to ini file\r\r\n"
                                            "     NOTE: avoid using registry and ini-file at the same time\r\r\n"
                                            "     Exception: -rr -iw to export registry to ini-file and\r\r\n"
                                            "                -ir -rw to export ini-file to registry"},
             {"pp", PARM_INT, NULL, 0, false, "set process priority for dialog\r\r\n"
                                       "       (0:idle, 1:normal, 2:high, 3:realtime)"},
             {"pt", PARM_INT, NULL, 0, false, "set priority for processing thread\r\r\n"
                                       "       (0:idle, 1:lowest, 2:below normal, 3:normal,\r\r\n"
                                "        4:above normal, 5:highest, 6:time critical)"} } ;
uint32_t clo_size = PARM_LAST ;

char errorparm[MAXPARMLENGTH] ;

/*
 Dialog coordinates definitions
*/
COORDS coordinates[] = {{0, 0, 0, 0, 0, 0},  /* first entry is reserved for main dialog */
                        {DLG_RESULT, 0, 0, 0, 0, RESIZE_X|RESIZE_Y},
                        {DLG_STATUS, 0, 0, 0, 0, RESIZE_X|RESIZE_Y}
                       } ;

COORDS *sorted_coord[sizeof(coordinates)/sizeof(coordinates[0])-1] ;
uint32_t coord_size = sizeof(coordinates)/sizeof(coordinates[0]) ;
bool static validreg = false ;        /* valid data from registry retrieved */

char static buffer[256] ; /* general purpose buffer */
char static strng1[STRINGSIZE] ;
char static strng2[STRINGSIZE] ;
bool static TARGET_error = false ;
bool static MAXERR_error = false ;
bool static MAXDEVIATION_error = false ;
bool static valid_imin, valid_imax ;
double static maxerror = 0.1 ;
double static maxdeviation = 10 ;
double static res, imin, imax, iload ;
COMP static component ;

LPTSTR static icon_array[3][7] = {{MAKEINTRESOURCE(IC_RS),  MAKEINTRESOURCE(IC_RSS),  MAKEINTRESOURCE(IC_RSSS), MAKEINTRESOURCE(IC_RPP),   MAKEINTRESOURCE(IC_RSPP), MAKEINTRESOURCE(IC_RPPP),  MAKEINTRESOURCE(IC_RSSP)},
                                  {MAKEINTRESOURCE(IC_CS),  MAKEINTRESOURCE(IC_CSS),  MAKEINTRESOURCE(IC_CSSS), MAKEINTRESOURCE(IC_CPP),   MAKEINTRESOURCE(IC_CSPP), MAKEINTRESOURCE(IC_CPPP),  MAKEINTRESOURCE(IC_CSSP)},
                                  {MAKEINTRESOURCE(IC_RNSS),MAKEINTRESOURCE(IC_RNSPP),MAKEINTRESOURCE(IC_RNPPS),MAKEINTRESOURCE(IC_RNSSS1),MAKEINTRESOURCE(IC_EMPTY),MAKEINTRESOURCE(IC_RNSSS2),MAKEINTRESOURCE(IC_EMPTY)}} ;

double static e6[] =   {100, 150, 220, 330, 470, 680} ;

/* e12 is a superset of e6 */
double static e12[] =  {100, 120, 150, 180, 220, 270, 330, 390, 470, 560, 680, 820} ;

/* e24 is a superset of e12 */
double static e24[] =  {100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300,
                        330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910} ;

/* Note that e48 is NOT a superset of e24 */
double static e48[] =  {100, 105, 110, 115, 121, 127, 133, 140, 147, 154, 162, 169,
                        178, 187, 196, 205, 215, 226, 237, 249, 261, 274, 287, 301,
                        316, 332, 348, 365, 383, 402, 422, 442, 464, 487, 511, 536,
                        562, 590, 619, 649, 681, 715, 750, 787, 825, 866, 909, 953} ;

/* e96 is a superset of e48 */
double static e96[] =  {100, 102, 105, 107, 110, 113, 115, 118, 121, 124, 127, 130,
                        133, 137, 140, 143, 147, 150, 154, 158, 162, 165, 169, 174,
                        178, 182, 187, 191, 196, 200, 205, 210, 215, 221, 226, 232,
                        237, 243, 249, 255, 261, 267, 274, 280, 287, 294, 301, 309,
                        316, 324, 332, 340, 348, 357, 365, 374, 383, 392, 402, 412,
                        422, 432, 442, 453, 464, 475, 487, 499, 511, 523, 536, 549,
                        562, 576, 590, 604, 619, 634, 649, 665, 681, 698, 715, 732,
                        750, 768, 787, 806, 825, 845, 866, 887, 909, 931, 953, 976 } ;

/* e192 is a superset of e96 */
double static e192[] = {100, 101, 102, 104, 105, 106, 107, 109, 110, 111, 113, 114,
                        115, 117, 118, 120, 121, 123, 124, 126, 127, 129, 130, 132,
                        133, 135, 137, 138, 140, 142, 143, 145, 147, 149, 150, 152,
                        154, 156, 158, 160, 162, 164, 165, 167, 169, 172, 174, 176,
                        178, 180, 182, 184, 187, 189, 191, 193, 196, 198, 200, 203,
                        205, 208, 210, 213, 215, 218, 221, 223, 226, 229, 232, 234,
                        237, 240, 243, 246, 249, 252, 255, 258, 261, 264, 267, 271,
                        274, 277, 280, 284, 287, 291, 294, 298, 301, 305, 309, 312,
                        316, 320, 324, 328, 332, 336, 340, 344, 348, 352, 357, 361,
                        365, 370, 374, 379, 383, 388, 392, 397, 402, 407, 412, 417,
                        422, 427, 432, 437, 442, 448, 453, 459, 464, 470, 475, 481,
                        487, 493, 499, 505, 511, 517, 523, 530, 536, 542, 549, 556,
                        562, 569, 576, 583, 590, 597, 604, 612, 619, 626, 634, 642,
                        649, 657, 665, 673, 681, 690, 698, 706, 715, 723, 732, 741,
                        750, 759, 768, 777, 787, 796, 806, 816, 825, 835, 845, 856,
                        866, 876, 887, 898, 909, 920, 931, 942, 953, 965, 976, 988 } ;

// reserve space for decades
double static table[MAXDECADES * (sizeof(e192)+sizeof(e24))/sizeof(e192[0])] ; /* e24 contains some unique values */
uint32_t static tablesize ; /* number of elements in the table */
SOLUTION static sol[NOS] ;
double static target, target2 ;
bool static topos, topopp, toposs, topoppp, toposss, topospp, topossp ;
bool static volatile abort_thread ;
uint32_t static volatile counter = 0 ;
bool static status[40] ; /* stores enabled/disabled status of controls (make sufficiently large...) */
char purpose[] = PURPOSE ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Function prototypes                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------+
*/
static INT_PTR CALLBACK ParSerProc(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam ) ;
static bool get_double_value(char *buffer, double *value) ;
static void fill_table(void) ;
static uint32_t mix_table(double *target, double *source1, uint32_t size1,
                                   double *source2, uint32_t size2) ;
static void enqueue(double target, double res, CONF conf,
             double val1, double val2, double val3) ;
static void print_results(void) ;
static char *pval(char *strng, uint32_t size, double val) ;
static DWORD WINAPI FindResults(LPVOID lpParam) ;
static void EnableControls(bool enable) ;
static void SaveControls(bool *status) ;
static void RestoreControls(bool *status) ;
static void SwitchComponent(COMP newcomponent) ;
static void GetAttributes(COMP component) ;
static void SetAttributes(COMP component) ;
static int32_t compare_table(const void *op1, const void *op2) ;
void ReadTable(void) ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       WinMain                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| MAIN program.                                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int WINAPI WinMain(HINSTANCE this_inst, HINSTANCE prev_inst, LPSTR cmdline, int cmdshow)
{
uint32_t static volatile rv ;
bool startup ;
   startup = true ;
   errorparm[0] = '\0' ;
   getparms(cmdline, errorparm) ;
   if (clo[PARM_RC].found) clear_registry(&myini) ;
   myini.initype = INI_NO ;
   if (!clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found)
   { /* enable registry read/write if none of the previous parameters was found (as default and for compatibility) */
      myini.initype |= (INI_REGREAD + INI_REGWRITE) ;
   }
   if (clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found && !clo[PARM_ID].found)
   { /* enable ini file read/write if registry is disabled and none of the previous parameters was found
        use "-rd -id" to use neither registry nor ini file */
      myini.initype |= (INI_FILEREAD + INI_FILEWRITE) ;
   }
   if (clo[PARM_RR].found) myini.initype |= INI_REGREAD ;
   if (clo[PARM_RW].found) myini.initype |= INI_REGWRITE ;
   if (clo[PARM_IR].found) myini.initype |= INI_FILEREAD ;
   if (clo[PARM_IW].found) myini.initype |= INI_FILEWRITE ;
   if (clo[PARM_RD].found) myini.initype &= ~(INI_REGREAD + INI_REGWRITE) ;
   if (clo[PARM_ID].found) myini.initype &= ~(INI_FILEREAD + INI_FILEWRITE) ;
   if (clo[PARM_IF].found) myini.inifilename = ini_filename ;
   if (startup)
   { /* start windowed version */
      if (clo[PARM_PP].found) set_process_prio(clo[PARM_PP].value) ;
      MyInstance = this_inst ;
      InitCommonControls() ;
      /* set default startup values */
      component = NONE ;
// set default values
//      registry_flags[R]  = FLAG_DLG_E24 + FLAG_DLG_TOPOS + FLAG_DLG_TOPOPP + FLAG_DLG_TOPOSS ;
//      registry_flags[C]  = FLAG_DLG_E24 + FLAG_DLG_TOPOS + FLAG_DLG_TOPOPP + FLAG_DLG_TOPOSS ;
//      registry_flags[RN] = FLAG_DLG_E24 + FLAG_DLG_TOPOS ;
//      registry_component = R ;
      strcpy_s(registry_maxerr[R],  VALSIZE+1, "1 %") ;
      strcpy_s(registry_maxerr[C],  VALSIZE+1, "1 %") ;
      strcpy_s(registry_maxerr[RN], VALSIZE+1, "1 %") ;
      strcpy_s(registry_maxdev[R],  VALSIZE+1, "10") ;
      strcpy_s(registry_maxdev[C],  VALSIZE+1, "10") ;
      strcpy_s(registry_maxdev[RN], VALSIZE+1, "10") ;
      rv = DialogBox(this_inst, "DLG_PARSER", 0, (DLGPROC)ParSerProc );
   }
   return(0);
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       ParSerProc                                                                                                      |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Processes messages for the DLG_PARSER dialog                                                                          |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
INT_PTR CALLBACK ParSerProc(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam)
{
uint32_t i ;
int32_t si ;
double input_value ;
//uint32_t static volatile retcd ;
DWORD static dwThreadId, dwThrdParam = 1;
HANDLE static hThread;
HICON static hIconS ;
HICON static hIconB ;
HWND msg_hwnd ;
bool static suspended ;
bool retcd ;
RECT static rect ;
clock_t static start_time, elapsed_time ;
HFONT hFont ;
FILE *outfile ;
OPENFILENAME filestruct ;
static char def_file_ext[] = "Text files\0*.csv;*.ini;*.txt\0All files\0*.*\0" ;
char *filename ; /* filename including path */

   retcd = false ; /* return false if we don't process the message */
   validreg = false ;

   switch(msg)
   {
   case WM_INITDIALOG:
      retcd = true ;
      MainWindow = window_handle ;
      tablesize = 0 ; /* start with a clean table */

// save original coordinates of dialog
      SaveCoordinates() ;

// Select font for controls
      hFont = CreateFont(
          15,                  // logical height of font
          0,                   // logical average character width
          0,                   // angle of escapement
          0,                   // base-line orientation angle
          FW_NORMAL,           // font weight
          false,               // italic attribute flag
          false,               // underline attribute flag
          false,               // strikeout attribute flag
          ANSI_CHARSET,        // character set identifier
          OUT_DEFAULT_PRECIS,  // output precision
          CLIP_DEFAULT_PRECIS, // clipping precision
          DEFAULT_QUALITY,     // output quality
          DEFAULT_PITCH,       // pitch and family
          "Courier New"        // pointer to typeface name string
      ) ;

      SendMessage(GetDlgItem(MainWindow, DLG_RESULT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_IMINTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_IMAXTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_ILOADTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_TARGETTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_TARGETTEXT2), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_MAXDEVIATIONTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));
      SendMessage(GetDlgItem(MainWindow, DLG_MAXERRORTEXT), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));

      validreg = get_registry(&myini) ; // get registry content

      if (validreg)
      {
         si = coordinates[0].right - coordinates[0].left ;
         if (registry_width  < si) registry_width = si ;
         si = coordinates[0].bottom - coordinates[0].top ;
         if (registry_height < si) registry_height = si ;
         MoveWindow(
         MainWindow,            // handle of window
         registry_x,            // horizontal position
         registry_y,            // vertical position
         registry_width,        // width
         registry_height,       // height
         true);                 // repaint flag
      }
//      else
//      { // set default values if registry is unused
//         registry_component = R ;
//         strcpy_s(registry_maxerr[R],  VALSIZE+1, "1 %") ;
//         strcpy_s(registry_maxerr[C],  VALSIZE+1, "1 %") ;
//         strcpy_s(registry_maxerr[RN], VALSIZE+1, "1 %") ;
//         strcpy_s(registry_maxdev[R],  VALSIZE+1, "10") ;
//         strcpy_s(registry_maxdev[C],  VALSIZE+1, "10") ;
//         strcpy_s(registry_maxdev[RN], VALSIZE+1, "10") ;
//      }

      switch (registry_component)
      {
         case C :  SendMessage(MainWindow, WM_COMMAND, RAD_COMPC, 0) ; break ;
         case RN : SendMessage(MainWindow, WM_COMMAND, RAD_COMPRN, 0) ; break ;
         default:  registry_component = R ; SendMessage(MainWindow, WM_COMMAND, RAD_COMPR, 0) ; break ;
      }

//      if ((registry_flags[component] & FLAG_DLG_IEC) != 0)
//      {
//         CheckRadioButton(MainWindow, RAD_IEC, RAD_PRIVATE, RAD_IEC) ;
//         SendMessage(MainWindow, WM_COMMAND, RAD_IEC, 0) ;
//      {
//      else
//      {
//         CheckRadioButton(MainWindow, RAD_IEC, RAD_PRIVATE, RAD_PRIVATE) ;
//         SendMessage(MainWindow, WM_COMMAND, RAD_PRIVATE, 0) ;
//      }

      /* display any illegal parameter */
      if (strlen(errorparm) != 0)
      {
         LoadString(MyInstance, STR_INVALIDPARM, strng1, STRINGSIZE);
         sprintf_s(strng2, STRINGSIZE, strng1, errorparm) ;
         SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
      }

      hIconS = (HICON) LoadImage(MyInstance, MAKEINTRESOURCE(IC_01S), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
      hIconB = (HICON) LoadImage(MyInstance, MAKEINTRESOURCE(IC_01B), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
      if (hIconS != NULL) SendMessage(MainWindow, WM_SETICON, ICON_SMALL, (LPARAM) hIconS) ;
      if (hIconB != NULL) SendMessage(MainWindow, WM_SETICON, ICON_BIG  , (LPARAM) hIconB) ;
//      ShowError(DLG_STATUS) ; /* display last error message */
      sprintf_s(strng2, STRINGSIZE, "%s - Version %u.%2.2u", PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
      SendMessage(MainWindow, WM_SETTEXT, 0, (LPARAM) strng2);

   break ;

#if CLOCKS_PER_SEC != 1000
#error CLOCKS_PER_SEC should be 1000. Please adapt the source code accordingly.
#endif
   case WM_USER:
      retcd = true ;
      switch(LOWORD(wparam))
      {
      case USER_PRINTRESULTS:
      // output the results
         elapsed_time = clock() - start_time ;
         print_results() ;
         if (abort_thread)
            LoadString(MyInstance, STR_ABORTED, strng1, STRINGSIZE);
         else
         {
            LoadString(MyInstance, STR_SOLUTIONS, strng1, STRINGSIZE);
            sprintf_s(strng2, STRINGSIZE, strng1, counter, (elapsed_time/CLOCKS_PER_SEC)/60,
                     (elapsed_time/CLOCKS_PER_SEC)%60, elapsed_time%CLOCKS_PER_SEC) ;
         }
         SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
         CloseHandle(hThread);
         hThread = NULL ;
         SetDlgItemText(MainWindow, DLG_START, "Start") ;
         RestoreControls(status) ; /* restore controls */
         EnableWindow(GetDlgItem(MainWindow, DLG_SUSPEND), false) ;
      break ;

      case USER_PRINTCOUNT:
      // display number of solutions
         LoadString(MyInstance, STR_SOLUTIONSSOFAR, strng2, STRINGSIZE);
         sprintf_s(strng1, STRINGSIZE, "%u %s", counter, strng2) ;
         SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
      break ;
      }
   break ;

   case WM_COMMAND:
      retcd = true ;
      switch( LOWORD( wparam ) )
      {
      case DLG_B_INFO:
         sprintf_s(strng1, STRINGSIZE, "%s - Version %u.%2.2u", PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
         PrintProgramInfo(DLG_HELPLIST, strng1, __DATE__, __TIME__) ;
      break ;

      case RAD_COMPR:
         SwitchComponent(R) ;
//         EnableControls(true) ; /* enable controls */
         EnableWindow(GetDlgItem(MainWindow, DLG_TARGET2), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMIN), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_ILOAD), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMAX), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSPP), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSP), true) ;
         SetDlgItemText(MainWindow, DLG_TARGETTEXT, "Target") ;
      break ;

      case RAD_COMPC:
         SwitchComponent(C) ;
//         EnableControls(true) ; /* enable controls */
         EnableWindow(GetDlgItem(MainWindow, DLG_TARGET2), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMIN), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_ILOAD), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMAX), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSPP), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSP), true) ;
         SetDlgItemText(MainWindow, DLG_TARGETTEXT, "Target") ;
      break ;

      case RAD_COMPRN:
         SwitchComponent(RN) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_MAXDEVIATION), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOS), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPP), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSS), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPPP), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSS), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSPP), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSP), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_TARGET2), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMIN), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_ILOAD), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_IMAX), true) ;
         SetDlgItemText(MainWindow, DLG_TARGETTEXT, "U1") ;
      break ;

      case RAD_IEC:
         SetDlgItemText(MainWindow, BUT_FILEIO, "Export") ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E6), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E12), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E24), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E48), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E96), true) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E192), true) ;
         LoadString(MyInstance, STR_EMPTY, strng1, STRINGSIZE) ;
         SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
      break ;

      case RAD_PRIVATE:
         SetDlgItemText(MainWindow, BUT_FILEIO, "Import") ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E6), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E12), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E24), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E48), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E96), false) ;
         EnableWindow(GetDlgItem(MainWindow, DLG_E192), false) ;
         ReadTable() ;
      break ;

      case DLG_SUSPEND:
         if (hThread != NULL)
         {
            if (!suspended)
            {
               SuspendThread(hThread) ;
               suspended = true ;
               SetDlgItemText(MainWindow, DLG_SUSPEND, "Resume") ;
               LoadString(MyInstance, STR_HALTED, strng1, STRINGSIZE);
               SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
            }
            else
            {
               ResumeThread(hThread) ;
               suspended = false ;
               SetDlgItemText(MainWindow, DLG_SUSPEND, "Suspend") ;
               LoadString(MyInstance, STR_WORKING, strng1, STRINGSIZE);
               SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
            }
         }
      break ;

      case BUT_FILEIO:

         filename = NULL ;
         if ((component == R) || (component == RN)) filename = registry_rlist_filename ;
         if (component == C) filename = registry_clist_filename ;
         if (IsDlgButtonChecked(MainWindow, RAD_IEC) == BST_CHECKED)
         { /* Use IEC table, export data */
            fill_table() ; /* generate table with all component values */
            filestruct.lStructSize = sizeof(OPENFILENAME) ;
            filestruct.hwndOwner = MainWindow ;
            filestruct.hInstance = 0 ;
            filestruct.lpstrFilter = def_file_ext ;
            filestruct.lpstrCustomFilter = NULL ;
            filestruct.nMaxCustFilter = 0 ;
            filestruct.nFilterIndex = 1 ;
            filestruct.lpstrFile = filename ;
            filestruct.nMaxFile = MAX_PATH ;
            filestruct.lpstrFileTitle = NULL ;
            filestruct.nMaxFileTitle = 0 ;
            filestruct.lpstrInitialDir = NULL ;
            filestruct.lpstrTitle = "Output File" ;
            filestruct.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT ;
            filestruct.nFileOffset = 0 ;
            filestruct.nFileExtension = 0 ;
            filestruct.lpstrDefExt = NULL ;
            filestruct.lCustData = 0 ;
            filestruct.lpfnHook = NULL ;
            filestruct.lpTemplateName = NULL ;
            if (GetSaveFileName(&filestruct) != 0)
            {
               if (fopen_s(&outfile, filename, "wt") == 0)
               {
                  for (i=0; i<tablesize; i++)
                  {
                     fprintf(outfile, "%2.2e", table[i]) ;
                     if (((i+1)%15) == 0)
                        fprintf(outfile, "\n") ;
                     else
                        if (i != tablesize-1) fprintf(outfile, ",") ;
                  }
                  fclose(outfile) ;
                  LoadString(MyInstance, STR_TABLEWRITTEN, strng1, STRINGSIZE) ;
                  sprintf_s(strng2, STRINGSIZE, strng1, tablesize, filename) ;
                  SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
               }
            }
         }
         else
         { /* import table from file */
            filestruct.lStructSize = sizeof(OPENFILENAME) ;
            filestruct.hwndOwner = MainWindow ;
            filestruct.hInstance = 0 ;
            filestruct.lpstrFilter = def_file_ext ;
            filestruct.lpstrCustomFilter = NULL ;
            filestruct.nMaxCustFilter = 0 ;
            filestruct.nFilterIndex = 1 ;
            filestruct.lpstrFile = filename ;
            filestruct.nMaxFile = MAX_PATH ;
            filestruct.lpstrFileTitle = NULL ;
            filestruct.nMaxFileTitle = 0 ;
            filestruct.lpstrInitialDir = NULL ;
            filestruct.lpstrTitle = "Input File" ;
            filestruct.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_FILEMUSTEXIST ;
            filestruct.nFileOffset = 0 ;
            filestruct.nFileExtension = 0 ;
            filestruct.lpstrDefExt = NULL ;
            filestruct.lCustData = 0 ;
            filestruct.lpfnHook = NULL ;
            filestruct.lpTemplateName = NULL ;
            if (GetOpenFileName(&filestruct) != 0)
            {
               ReadTable() ;
            }
         }
      break ;

      case DLG_START:
/* Start */
         if (hThread != NULL)
         { /* Thread already started. Abort */
            if (suspended) SendMessage(MainWindow, WM_COMMAND, DLG_SUSPEND, 0) ;
            abort_thread = true ;
            LoadString(MyInstance, STR_ABORTING, strng1, STRINGSIZE);
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
         }
         else
         {
            if (IsDlgButtonChecked(MainWindow, RAD_IEC) == BST_CHECKED)
            {
               fill_table() ; /* generate table with all component values */
            }
/* invalidate solutions */
            for (i=0; i<NOS; i++) sol[i].valid = false ;
            counter = 0 ;
/* clear output window */
            msg_hwnd = GetDlgItem(MainWindow, DLG_RESULT) ;
            SendMessage(msg_hwnd, EM_SETSEL, 0, -1); /* clear window */
            SendMessage(msg_hwnd, EM_REPLACESEL, (bool) false, (WPARAM) "") ;
            abort_thread = false ;

/* get maximum error */
            GetWindowText(GetDlgItem(MainWindow, DLG_MAXERROR), buffer, sizeof(buffer)) ;
            MAXERR_error = true ;
            if (get_double_value(buffer, &input_value) && (input_value > 0))
            {
               maxerror = input_value ;
               MAXERR_error = false ;
            }

/* get maximum deviation */
            GetWindowText(GetDlgItem(MainWindow, DLG_MAXDEVIATION), buffer, sizeof(buffer)) ;
            MAXDEVIATION_error = true ;
            if (get_double_value(buffer, &input_value) && (input_value > 0))
            {
               maxdeviation = input_value ;
               MAXDEVIATION_error = false ;
            }

/* get target value */
            GetWindowText(GetDlgItem(MainWindow, DLG_TARGET), buffer, sizeof(buffer)) ;
            TARGET_error = true ;
            if (get_double_value(buffer, &input_value)
               && (input_value > 0)
               && (input_value < MAX_TARGET))
            {
               target = input_value ;
               TARGET_error = false ;
            }

            if (component == RN)
            {
/* get target2 value */
               GetWindowText(GetDlgItem(MainWindow, DLG_TARGET2), buffer, sizeof(buffer)) ;
               if (get_double_value(buffer, &input_value)
                  && (input_value > 0))
               {
                  target2 = input_value ;
               }
               else TARGET_error = true ;

/* get Imin value */
               GetWindowText(GetDlgItem(MainWindow, DLG_IMIN), buffer, sizeof(buffer)) ;
               if (strlen(buffer) > 0)
               {
                  if (get_double_value(buffer, &input_value)
                     && (input_value >= 0))
                  {
                     imin = input_value ;
                     valid_imin = true ;
                  }
                  else TARGET_error = true ;
               }
               else valid_imin = false ;

/* get Imax value */
               GetWindowText(GetDlgItem(MainWindow, DLG_IMAX), buffer, sizeof(buffer)) ;
               if (strlen(buffer) > 0)
               {
                  if (get_double_value(buffer, &input_value)
                     && (input_value >= 0))
                  {
                     imax = input_value ;
                     valid_imax = true ;
                  }
                  else TARGET_error = true ;
               }
               else valid_imax = false ;

/* get Iload value */
               GetWindowText(GetDlgItem(MainWindow, DLG_ILOAD), buffer, sizeof(buffer)) ;
               if (strlen(buffer) > 0)
               {
                  if (get_double_value(buffer, &input_value))
                     iload = input_value ;
                  else TARGET_error = true ;
               }
               else iload = 0 ;
            }

            LoadString(MyInstance, STR_EMPTY, strng1, STRINGSIZE);
            if (TARGET_error)
            {
               LoadString(MyInstance, STR_INVALIDTARGET, strng1, STRINGSIZE);
            }

            if ((!TARGET_error) && (target2 > target) && (component == RN))
            {
               LoadString(MyInstance, STR_TARGETVAL, strng1, STRINGSIZE);
               TARGET_error = true ;
            }
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;

            if (tablesize == 0)
            {
               LoadString(MyInstance, STR_TABLEISINVALID, strng1, STRINGSIZE) ;
               TARGET_error = true ;
            }
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;

            if (!TARGET_error)
            {
               topos   = IsDlgButtonChecked(MainWindow, DLG_TOPOS) ;
               topopp  = IsDlgButtonChecked(MainWindow, DLG_TOPOPP) ;
               toposs  = IsDlgButtonChecked(MainWindow, DLG_TOPOSS) ;
               topoppp = IsDlgButtonChecked(MainWindow, DLG_TOPOPPP) ;
               toposss = IsDlgButtonChecked(MainWindow, DLG_TOPOSSS) ;
               topospp = IsDlgButtonChecked(MainWindow, DLG_TOPOSPP) ;
               topossp = IsDlgButtonChecked(MainWindow, DLG_TOPOSSP) ;

               SaveControls(status) ;  /* save status of controls */
               EnableControls(false) ; /* disable some critical controls during execution */

               hThread = CreateThread(
                   NULL,                        // no security attributes
                   0,                           // use default stack size
                   FindResults,                 // thread function
                   &dwThrdParam,                // argument to thread function
                   0,                           // use default creation flags
                   &dwThreadId);                // returns the thread identifier

              // Check the return value for success.
               if (hThread == NULL)
                  LoadString(MyInstance, STR_THREADSTARTERROR, strng1, STRINGSIZE);
               else
               {
                  LoadString(MyInstance, STR_WORKING, strng1, STRINGSIZE);
                  suspended = false ;
                  start_time = clock() ;
                  if (clo[PARM_PT].found) set_thread_prio(hThread, clo[PARM_PT].value) ;
               }
               SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
               SetDlgItemText(MainWindow, DLG_START, "Abort") ;
            }
         }
      break ;

      case IDABORT:
      case IDCANCEL:
      case BUT_EXIT:
         GetWindowRect(MainWindow, &rect) ; /* get current dialog coordinates */
         registry_x = rect.left ;
         registry_y = rect.top ;
         registry_width = rect.right - rect.left ;
         registry_height = rect.bottom - rect.top ;
         if (registry_x < 0) registry_x = 0 ;
         if (registry_y < 0) registry_y = 0 ;
         if (IsDlgButtonChecked(MainWindow, RAD_COMPR)) registry_component = R ;
         if (IsDlgButtonChecked(MainWindow, RAD_COMPC)) registry_component = C ;
         if (IsDlgButtonChecked(MainWindow, RAD_COMPRN)) registry_component = RN ;
         GetAttributes(registry_component) ;
         registry_version = PROGRAMVERSION ;    /* version of program which wrote the registry */
         put_registry(&myini) ;                 /* store the dialog properties in registry */
         DestroyIcon(hIconS) ;                  /* Free icon memory */
         DestroyIcon(hIconB) ;                  /* Free icon memory */
         EndDialog(MainWindow, true );          /* Exits the dialog box */
      break ;

      default:
         retcd = false ;
      break ;
      }
   break ;

   default:
      retcd = WinLibMessage(msg, wparam, lparam) ; /* WinLib processes some messages */
   break ;
   }
   return(retcd);
} /* ParSerProc */


/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void ReadTable(void)                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Read a user defined table. The pathname is stored in registry_rlist_filename or registry_clist_filename, but it may   |
| be invalid.                                                                                                           |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void ReadTable(void)
{
char *stp ;
char *stptr ;
uint32_t count, line_no ;
bool scanerror ;
double tmp_double ;
FILE *infile ;
char *filename ; /* filename including path */
uint32_t i ;

   filename = NULL ;
   if ((component == R) || (component == RN)) filename = registry_rlist_filename ;
   if (component == C) filename = registry_clist_filename ;
   if (fopen_s(&infile, filename, "rt") == 0)
   {
      for (i=0; i<(sizeof(table)/sizeof(table[0])); i++) table[i] = 0.0 ;
      i = 0 ;
      line_no = 0 ;
      scanerror = false ;
      while (!feof(infile) && (!scanerror))
      {
         if (fgets(ps_buffer, STRINGSIZE, infile) != NULL)
         {
            line_no++ ;
            stp = strtok_s(ps_buffer, " ,;", &stptr ) ;
            while ((stp != NULL) && (!scanerror))
            {
               count = sscanf_s(stp, "%le", &tmp_double) ;
               if (count != 1) scanerror = true ;
               else table[i] = tmp_double ;
               i++ ;
               if (!scanerror) stp = strtok_s(NULL, " ,;", &stptr );
            }
         }
      }
      tablesize = i ;
      fclose(infile) ;
      if (scanerror)
      {
         LoadString(MyInstance, STR_SCANERROR, strng1, STRINGSIZE) ;
         sprintf_s(strng2, STRINGSIZE, strng1, line_no, stp) ;
      }
      else
      {/* sort the table and display success message */
         qsort(&table, tablesize, sizeof(table[0]), compare_table) ;
         LoadString(MyInstance, STR_TABLELOADED, strng1, STRINGSIZE) ;
         sprintf_s(strng2, STRINGSIZE, strng1, tablesize, filename) ;
      }
   }
   else
   {
      tablesize = 0 ;
      if (strlen(filename) > 0)
      {
         LoadString(MyInstance, STR_TABLENOTLOADED, strng1, STRINGSIZE) ;
         sprintf_s(strng2, STRINGSIZE, strng1, filename) ;
      }
      else
      {
         LoadString(MyInstance, STR_TABLEISINVALID, strng2, STRINGSIZE) ;
      }
   }
   SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void SwitchComponent(COMP newcomponent)                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| The user has selected a new component. We safe the old flags for the old component and select the new flags           |
| according to the settings in the flags array. Also the new icons will be displayed.                                   |
| This function is typically called, when a component is selected by clicking a radio button. The function may also     |
| be called, when no radio button was cliched, e.g. during initialization. Therefore we explicitely select the          |
| desired radio button in this function.                                                                                |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void SwitchComponent(COMP newcomponent)
{
HICON static icons = NULL ;
HICON static iconss = NULL ;
HICON static iconsss = NULL ;
HICON static iconpp = NULL ;
HICON static iconppp = NULL ;
HICON static iconspp = NULL ;
HICON static iconssp = NULL ;

   GetAttributes(component) ;
   SetAttributes(newcomponent) ;
/* Destroy any previous icons */
   if (icons != NULL) {DestroyIcon(icons) ; icons = NULL ;}
   if (iconss != NULL) {DestroyIcon(iconss) ; iconss = NULL ;}
   if (iconsss != NULL) {DestroyIcon(iconsss) ; iconsss = NULL ;}
   if (iconpp != NULL) {DestroyIcon(iconpp) ; iconpp = NULL ;}
   if (iconppp != NULL) {DestroyIcon(iconppp) ; iconppp = NULL ;}
   if (iconspp != NULL) {DestroyIcon(iconspp) ; iconspp = NULL ;}
   if (iconssp != NULL) {DestroyIcon(iconssp) ; iconssp = NULL ;}

/* set icons */
   icons   = (HICON) LoadImage(MyInstance, icon_array[newcomponent][0], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconss  = (HICON) LoadImage(MyInstance, icon_array[newcomponent][1], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconsss = (HICON) LoadImage(MyInstance, icon_array[newcomponent][2], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconpp  = (HICON) LoadImage(MyInstance, icon_array[newcomponent][3], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconspp = (HICON) LoadImage(MyInstance, icon_array[newcomponent][4], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconppp = (HICON) LoadImage(MyInstance, icon_array[newcomponent][5], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   iconssp = (HICON) LoadImage(MyInstance, icon_array[newcomponent][6], IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) ;
   if (icons   != NULL) SendMessage(GetDlgItem(MainWindow, ICON_S),   STM_SETIMAGE, IMAGE_ICON, (LPARAM) icons  );
   if (iconss  != NULL) SendMessage(GetDlgItem(MainWindow, ICON_SS),  STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconss );
   if (iconsss != NULL) SendMessage(GetDlgItem(MainWindow, ICON_SSS), STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconsss);
   if (iconpp  != NULL) SendMessage(GetDlgItem(MainWindow, ICON_PP),  STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconpp );
   if (iconspp != NULL) SendMessage(GetDlgItem(MainWindow, ICON_SPP), STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconspp);
   if (iconppp != NULL) SendMessage(GetDlgItem(MainWindow, ICON_PPP), STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconppp);
   if (iconssp != NULL) SendMessage(GetDlgItem(MainWindow, ICON_SSP), STM_SETIMAGE, IMAGE_ICON, (LPARAM) iconssp);

   switch (newcomponent)
   {
   case C :  CheckRadioButton(MainWindow, RAD_COMPR, RAD_COMPRN, RAD_COMPC) ; break ;
   case RN : CheckRadioButton(MainWindow, RAD_COMPR, RAD_COMPRN, RAD_COMPRN) ; break ;
   default:  CheckRadioButton(MainWindow, RAD_COMPR, RAD_COMPRN, RAD_COMPR) ; break ;
   }
   component = newcomponent ;

   if ((registry_flags[newcomponent] & FLAG_DLG_IEC) != 0)
   { /* IEC table */
      CheckRadioButton(MainWindow, RAD_IEC, RAD_PRIVATE, RAD_IEC) ;
      SendMessage(MainWindow, WM_COMMAND, RAD_IEC, 0) ;
      LoadString(MyInstance, STR_EMPTY, strng1, STRINGSIZE) ;
      SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
   }
   else
   { /* private table */
      CheckRadioButton(MainWindow, RAD_IEC, RAD_PRIVATE, RAD_PRIVATE) ;
      SendMessage(MainWindow, WM_COMMAND, RAD_PRIVATE, 0) ;
      ReadTable() ; /* try to read the table, use previous filename, this may fail */
   }

}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void GetAttributes(COMP comp)                                                                                   |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get the attributes of the current dialog. Attributes include all flags and dialog values.                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void GetAttributes(COMP comp)
{
   if (comp < COMPSIZE) /* check if in range */
   {
      registry_flags[comp] = ((IsDlgButtonChecked(MainWindow, DLG_E6) == BST_UNCHECKED)      ? 0 : FLAG_DLG_E6)
                           + ((IsDlgButtonChecked(MainWindow, DLG_E12) == BST_UNCHECKED)     ? 0 : FLAG_DLG_E12)
                           + ((IsDlgButtonChecked(MainWindow, DLG_E24) == BST_UNCHECKED)     ? 0 : FLAG_DLG_E24)
                           + ((IsDlgButtonChecked(MainWindow, DLG_E48) == BST_UNCHECKED)     ? 0 : FLAG_DLG_E48)
                           + ((IsDlgButtonChecked(MainWindow, DLG_E96) == BST_UNCHECKED)     ? 0 : FLAG_DLG_E96)
                           + ((IsDlgButtonChecked(MainWindow, DLG_E192) == BST_UNCHECKED)    ? 0 : FLAG_DLG_E192)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOS) == BST_UNCHECKED)   ? 0 : FLAG_DLG_TOPOS)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOPP) == BST_UNCHECKED)  ? 0 : FLAG_DLG_TOPOPP)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOSS) == BST_UNCHECKED)  ? 0 : FLAG_DLG_TOPOSS)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOPPP) == BST_UNCHECKED) ? 0 : FLAG_DLG_TOPOPPP)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOSSS) == BST_UNCHECKED) ? 0 : FLAG_DLG_TOPOSSS)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOSPP) == BST_UNCHECKED) ? 0 : FLAG_DLG_TOPOSPP)
                           + ((IsDlgButtonChecked(MainWindow, DLG_TOPOSSP) == BST_UNCHECKED) ? 0 : FLAG_DLG_TOPOSSP)
                           + ((IsDlgButtonChecked(MainWindow, RAD_IEC) == BST_UNCHECKED)     ? 0 : FLAG_DLG_IEC) ;
      GetDlgItemText(MainWindow, DLG_TARGET, registry_target[comp], VALSIZE) ;
      GetDlgItemText(MainWindow, DLG_MAXERROR, registry_maxerr[comp], VALSIZE) ;
      GetDlgItemText(MainWindow, DLG_MAXDEVIATION, registry_maxdev[comp], VALSIZE) ;
      if (comp == RN)
      {
         GetDlgItemText(MainWindow, DLG_TARGET2, registry_u2, VALSIZE) ;
         GetDlgItemText(MainWindow, DLG_IMIN, registry_imin, VALSIZE) ;
         GetDlgItemText(MainWindow, DLG_IMAX, registry_imax, VALSIZE) ;
         GetDlgItemText(MainWindow, DLG_ILOAD, registry_iload, VALSIZE) ;
      }
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void SetAttributes(COMP comp)                                                                                   |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Set the attributes of the current dialog. Attributes include all flags and dialog values.                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void SetAttributes(COMP comp)
{
   if (comp < COMPSIZE) /* check if in range */
   {
      CheckDlgButton(MainWindow, DLG_E6     , ((registry_flags[comp] & FLAG_DLG_E6     ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_E12    , ((registry_flags[comp] & FLAG_DLG_E12    ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_E24    , ((registry_flags[comp] & FLAG_DLG_E24    ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_E48    , ((registry_flags[comp] & FLAG_DLG_E48    ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_E96    , ((registry_flags[comp] & FLAG_DLG_E96    ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_E192   , ((registry_flags[comp] & FLAG_DLG_E192   ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOS  , ((registry_flags[comp] & FLAG_DLG_TOPOS  ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOPP , ((registry_flags[comp] & FLAG_DLG_TOPOPP ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOSS , ((registry_flags[comp] & FLAG_DLG_TOPOSS ) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOPPP, ((registry_flags[comp] & FLAG_DLG_TOPOPPP) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOSSS, ((registry_flags[comp] & FLAG_DLG_TOPOSSS) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOSPP, ((registry_flags[comp] & FLAG_DLG_TOPOSPP) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton(MainWindow, DLG_TOPOSSP, ((registry_flags[comp] & FLAG_DLG_TOPOSSP) != 0) ? BST_CHECKED : BST_UNCHECKED) ;
      SetDlgItemText(MainWindow, DLG_TARGET, registry_target[comp]) ;
      SetDlgItemText(MainWindow, DLG_MAXERROR, registry_maxerr[comp]) ;
      SetDlgItemText(MainWindow, DLG_MAXDEVIATION, registry_maxdev[comp]) ;
      if (comp == RN)
      {
         SetDlgItemText(MainWindow, DLG_TARGET2, registry_u2) ;
         SetDlgItemText(MainWindow, DLG_IMIN, registry_imin) ;
         SetDlgItemText(MainWindow, DLG_IMAX, registry_imax) ;
         SetDlgItemText(MainWindow, DLG_ILOAD, registry_iload) ;
      }
      else
      {
         SetDlgItemText(MainWindow, DLG_TARGET2, "") ;
         SetDlgItemText(MainWindow, DLG_IMIN, "") ;
         SetDlgItemText(MainWindow, DLG_IMAX, "") ;
         SetDlgItemText(MainWindow, DLG_ILOAD, "") ;
      }
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void EnableControls(bool enable)                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Enable or disable some critical controls during execution of thread.                                                  |
| Control gets enabled if enable is true, otherwise it gets disabled.                                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void EnableControls(bool enable)
{
   EnableWindow(GetDlgItem(MainWindow, DLG_E6), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E12), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E24), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E48), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E96), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E192), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPR), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPC), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPRN), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TARGET), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TARGET2), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_IMIN), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_IMAX), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_ILOAD), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_MAXERROR), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_MAXDEVIATION), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOS), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPP), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSS), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPPP), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSS), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSPP), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSP), enable) ;
   EnableWindow(GetDlgItem(MainWindow, BUT_FILEIO), enable) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_SUSPEND), !enable) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void SaveControls(bool *status)                                                                                 |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Save enabled/disabled status of some controls.                                                                        |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void SaveControls(bool *status)
{
   status[ 0] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E6)) ;
   status[ 1] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E12)) ;
   status[ 2] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E24)) ;
   status[ 3] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E48)) ;
   status[ 4] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E96)) ;
   status[ 5] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_E192)) ;
   status[ 6] = IsWindowEnabled(GetDlgItem(MainWindow, RAD_COMPR)) ;
   status[ 7] = IsWindowEnabled(GetDlgItem(MainWindow, RAD_COMPC)) ;
   status[ 8] = IsWindowEnabled(GetDlgItem(MainWindow, RAD_COMPRN)) ;
   status[ 9] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TARGET)) ;
   status[10] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TARGET2)) ;
   status[11] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_IMIN)) ;
   status[12] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_IMAX)) ;
   status[13] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_ILOAD)) ;
   status[14] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_MAXERROR)) ;
   status[15] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_MAXDEVIATION)) ;
   status[16] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOS)) ;
   status[17] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOPP)) ;
   status[18] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOSS)) ;
   status[19] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOPPP)) ;
   status[20] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOSSS)) ;
   status[21] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOSPP)) ;
   status[22] = IsWindowEnabled(GetDlgItem(MainWindow, DLG_TOPOSSP)) ;
   status[23] = IsWindowEnabled(GetDlgItem(MainWindow, BUT_FILEIO)) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void RestoreControls(bool *status)                                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Restore enabled/disabled status of some controls.                                                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void RestoreControls(bool *status)
{
   EnableWindow(GetDlgItem(MainWindow, DLG_E6),           status[ 0]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E12),          status[ 1]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E24),          status[ 2]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E48),          status[ 3]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E96),          status[ 4]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_E192),         status[ 5]) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPR),        status[ 6]) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPC),        status[ 7]) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_COMPRN),       status[ 8]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TARGET),       status[ 9]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TARGET2),      status[10]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_IMIN),         status[11]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_IMAX),         status[12]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_ILOAD),        status[13]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_MAXERROR),     status[14]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_MAXDEVIATION), status[15]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOS),        status[16]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPP),       status[17]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSS),       status[18]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOPPP),      status[19]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSS),      status[20]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSPP),      status[21]) ;
   EnableWindow(GetDlgItem(MainWindow, DLG_TOPOSSP),      status[22]) ;
   EnableWindow(GetDlgItem(MainWindow, BUT_FILEIO),       status[23]) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void print_results(void)                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Display the results.                                                                                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void print_results(void)
{
HWND msg_hwnd ;
uint32_t iLen, i, j ;
bool found = false ;
char str1[10], str2[10], str3[10], res[10] ;
   msg_hwnd = GetDlgItem(MainWindow, DLG_RESULT) ;
   iLen = GetWindowTextLength(msg_hwnd) ;
   SendMessage(msg_hwnd, EM_SETSEL, 0, -1); /* clear window */
   SendMessage(msg_hwnd, EM_REPLACESEL, (bool) false, (WPARAM) "") ;
   do
   {
      j = 0 ;
      found = false ;
      for (i=0; i<NOS; i++)
      {
         if ((sol[i].valid) && ((!sol[j].valid) || ((sol[j].err) > sol[i].err)))
         {
            j = i ;
            found = true ;
         }
      }
      found = found || (sol[j].valid) ;
      if (found)
      {
         sol[j].valid = false ;
         pval(str1, 10, sol[j].val1) ;
         pval(str2, 10, sol[j].val2) ;
         pval(str3, 10, sol[j].val3) ;
         pval(res, 10, sol[j].res) ;
         switch (sol[j].conf) {
         case S:
            sprintf_s(strng1, STRINGSIZE, "%s (=%1.2lf%% error)\r\r\n",
                           str1, 100*sol[j].err/sol[j].target) ;
            break ;

         case SS:
            sprintf_s(strng1, STRINGSIZE, "%s - %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case PP:
            sprintf_s(strng1, STRINGSIZE, "%s || %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case PPP:
            sprintf_s(strng1, STRINGSIZE, "%s || %s || %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case SPP:
            sprintf_s(strng1, STRINGSIZE, "%s - %s || %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case SSS:
            sprintf_s(strng1, STRINGSIZE, "%s - %s - %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case SSP:
            sprintf_s(strng1, STRINGSIZE, "(%s - %s) || %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case SSS1:
            sprintf_s(strng1, STRINGSIZE, "%s - (%s - %s) = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case SSS2:
            sprintf_s(strng1, STRINGSIZE, "(%s - %s) - %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         case PPS:
            sprintf_s(strng1, STRINGSIZE, "%s || %s - %s = %s (=%1.2lf%% error)\r\r\n",
                           str1, str2, str3, res,
                           100*sol[j].err/sol[j].target) ;
            break ;

         }
         SendMessage(msg_hwnd, EM_REPLACESEL, (bool) false, (WPARAM) strng1) ;
      }
   } while (found) ;
   SendMessage(msg_hwnd, EM_SETSEL, 0, 0); /* position to top left */
   SendMessage(msg_hwnd, EM_SCROLLCARET, 0, 0); /* scroll caret into view */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       char *pval(char *strng, uint32_t size, double val)                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Format an output value. Use suffixes 'k' or 'M' where appropriate.                                                    |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
char *pval(char *strng, uint32_t size, double val)
{
char *suffix ;
char *form ;
   suffix = "" ;

   if (val < 1)
   {
      val *= 1000 ;
      suffix = "m" ;
   }

   if (val < 1)
   {
      val *= 1000 ;
      suffix = "µ" ;
   }

   if (val < 1)
   {
      val *= 1000 ;
      suffix = "n" ;
   }

   if (val < 1)
   {
      val *= 1000 ;
      suffix = "p" ;
   }

   if (val >= 1000)
   {
      val /= 1000 ;
      suffix = "k" ;
   }

   if (val >= 1000)
   {
      val /= 1000 ;
      suffix = "M" ;
   }

   if (val - (double)((uint32_t) val) > 0.01)
      form = "%1.2lf%s" ;
   else
      form = "%1.0lf%s" ;

   sprintf_s(strng, size, form, val, suffix) ;

   return(strng) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       bool get_double_value(char *buffer, double *value)                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Decode an integer value with optional suffix (k,M,G...).                                                              |
| Decimal points are supported, but result is always truncated to signed long long.                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
bool get_double_value(char *buffer, double *value)
{
double mydouble ;
double mult = 1.0 ;
char mystrng[16] ;
uint32_t size ;
    size = sscanf_s(buffer, "%lf%s", &mydouble, &mystrng, 16) ;
    if (size == 2)
    {
        switch (mystrng[0]) {
        case 'T': mult = 1e12 ; break ;
        case 'G': mult = 1e9 ; break ;
        case 'M': mult = 1e6 ; break ;
        case 'k': mult = 1e3 ; break ;
        case 'm': mult = 1e-3 ; break ;
        case 'u': mult = 1e-6 ; break ;
        case 'µ': mult = 1e-6 ; break ;
        case 'n': mult = 1e-9 ; break ;
        case 'p': mult = 1e-12 ; break ;
        case '%': mult = 1e-2 ; break ;
        default : mult = 1.0 ; }
    }
    *value = mydouble * mult ;
    return((size == 1) || (size == 2)) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       uint32_t mix_table(double *target, double *source1, uint32_t size1, double *source2, uint32_t size2)            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Mix two tables and return the size of the new table.                                                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
uint32_t mix_table(double *target, double *source1, uint32_t size1, double *source2, uint32_t size2)
{
uint32_t size, i, j, k ;
double v1, last ;
   size = 0 ;
   i = 0 ;
   j = 0 ;
   k = 0 ;
   last = 0 ;
   while ((i<size1) || (j<size2))
   {
      if (i<size1)
      {
         v1 = source1[i] ;
         i++ ;
      }
      else
         v1 = -1 ;
      while ((j<size2) && (((v1 >=0) && (source2[j] <= v1)) || (v1 < 0)))
      {
         target[size] = source2[j] ;
         last = source2[j] ;
         size++ ;
         j++ ;
      }
      if ((v1 > 0) && (v1 != last))
      {
         target[size] = v1 ;
         size++ ;
      }
   }
   return(size) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void fill_table(void)                                                                                           |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Fill the table with all decades of currently selected tables.                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void fill_table(void)
{
uint32_t i, j, dec ;
double mul ;
uint32_t size ;
double table1[(sizeof(e192)+sizeof(e24))/sizeof(e192[0])] ; /* e24 contains some unique values */
double table2[(sizeof(e192)+sizeof(e24))/sizeof(e192[0])] ; /* e24 contains some unique values */
double *source, *target, *temp ;
   size = 0 ;
   target = table1 ;
   source = table2 ;
   if (IsDlgButtonChecked(MainWindow, DLG_E6))
   {
      size = mix_table(target, e6, sizeof(e6)/sizeof(e6[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   if (IsDlgButtonChecked(MainWindow, DLG_E12))
   {
      size = mix_table(target, e12, sizeof(e12)/sizeof(e12[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   if (IsDlgButtonChecked(MainWindow, DLG_E24))
   {
      size = mix_table(target, e24, sizeof(e24)/sizeof(e24[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   if (IsDlgButtonChecked(MainWindow, DLG_E48))
   {
      size = mix_table(target, e48, sizeof(e48)/sizeof(e48[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   if (IsDlgButtonChecked(MainWindow, DLG_E96))
   {
      size = mix_table(target, e96, sizeof(e96)/sizeof(e96[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   if (IsDlgButtonChecked(MainWindow, DLG_E192))
   {
      size = mix_table(target, e192, sizeof(e192)/sizeof(e192[0]), source, size) ;
      temp = target ;
      target = source ;
      source = temp ;
   }
   mul = ((component == R) || (component == RN)) ? 1e-3 : 1e-14 ;
   dec = ((component == R) || (component == RN)) ? RDECADES : CDECADES ;
   tablesize = dec * size ;
   for (j=0; j<dec; j++)
   {
       for (i=0; i<size; i++)
       {
          table[i+size*j] = source[i] * mul ;
       }
       mul *= 10.0 ;
  }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int compare_table(const void *op1, const void *op2 )                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Compare for qsort                                                                                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int32_t compare_table(const void *op1, const void *op2)
{
int32_t retval ;
double diff ;
   diff = * (double *) op1 - * (double *) op2 ;
   retval = 1 ;
   if (diff == 0.0) retval = 0 ;
   else if (diff < 0.0) retval = -1 ;
   return (retval) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void enqueue(double target, double res, CONF conf, double val1, double val2, double val3)                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Enqueue values if error is below maximum error of current entries and lower than the maximum permitted error.         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void enqueue(double target, double res, CONF conf, double val1, double val2, double val3)
{
uint32_t i, j ;
bool found ;
double error ; /* absolute error */
double deviation ; /* deviation between the individual components */
double maxf, minf ;
   found = false ;
   error = fabs(res-target) ;
   if (conf == S) deviation = 0 ;
   if ((conf == SS) || (conf == PP))
      deviation = (val1>val2) ? val1/val2 : val2/val1 ;
   if ((conf == SSS) || (conf == SPP) || (conf == PPP) || (conf == SSP) || (conf == PPS) || (conf == SSS1) || (conf == SSS2))
   {
      maxf = (val1 > val2) ? val1 : val2 ;
      maxf = (maxf > val3) ? maxf : val3 ;
      minf = (val1 < val2) ? val1 : val2 ;
      minf = (minf < val3) ? minf : val3 ;
      deviation = maxf / minf ;
   }
   if (error/target <= maxerror)
   {
      if ((MAXDEVIATION_error) || (deviation <= maxdeviation))
      {
         j = 0 ;
         for (i=0; (i<NOS) && (!found); i++) found = (!sol[i].valid) ;
         j = i - 1 ;
         if (!found)
         /* table is full. Seek entry with max error */
            for (i=0; i<NOS; i++) if (sol[i].err > sol[j].err) j = i ;
         if ((sol[j].err > error) || found)
         { /* enque values */
            sol[j].target = target ;
            sol[j].val1 = val1 ;
            sol[j].val2 = val2 ;
            sol[j].val3 = val3 ;
            sol[j].conf = conf ;
            sol[j].res = res ;
            sol[j].err  = error ;
            sol[j].valid = true ;
         }
         counter++ ;
         if ( (counter < 100)
          || ((counter < 1000) && ((counter%10) == 0))
          || ((counter < 10000) && ((counter%100) == 0))
          || ((counter < 100000) && ((counter%1000) == 0))
          || ((counter < 1000000) && ((counter%10000) == 0)))
            SendMessage(MainWindow, WM_USER, USER_PRINTCOUNT, 0) ;
      }
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       FindResults(LPVOID lpParam)                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| This thread searches for the possible results. The parameter is unused; we use only global variables.                 |
|                                                                                                                       |
| The user can abort this thread by setting the global variable abort_thread to true.                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
DWORD WINAPI FindResults(LPVOID lpParam)
{
double limit1, limit2, llimit, ulimit, llimitres, ulimitres, itotal, rges, minres ;
uint32_t i, j, k ;
CONF cfg ;
   ulimit = target * (1 + maxerror) ; /* upper limit */
   llimit = target / (1 + maxerror) ; /* lower limit */
// check S configuration
   if ((topos) && ((component == R) || (component == C)))
   {
      limit1 = target * (1 + maxerror) ;
      for (i = 0; (i < tablesize) && (table[i] < llimit); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (table[i] <= limit1); i++)
         enqueue(target, table[i], S, table[i], 0, 0) ;
   }

// check PP configuration
   if ( (((component == R) && (topopp))
      || ((component == C) && (toposs))) && (!abort_thread))
   {
      if (component == R) cfg = PP ;
      if (component == C) cfg = SS ;
      for (i = 0; (i < tablesize) && (table[i] < llimit); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (table[i] <= (2 * ulimit)) && (!abort_thread); i++)
      {
         limit1 = table[i] * maxdeviation ;
         res = 0 ;
         for (j = i; (j < tablesize) && (table[j] <= limit1) && (res < ulimit); j++)
         {
            res = 1/((1/table[i])+(1/table[j])) ;
            enqueue(target, res, cfg, table[i], table[j], 0) ;
         }
      }
   }

// check SS configuration
   if ( (((component == R) && (toposs))
      || ((component == C) && (topopp))) && (!abort_thread))
   {
      if (component == R) cfg = SS ;
      if (component == C) cfg = PP ;
      for (i = 0; (i < tablesize) && (table[i] < (llimit/(maxdeviation+1))); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (table[i] <= ulimit) && (!abort_thread); i++)
      {
         limit1 = min(ulimit, table[i] * maxdeviation) ;
         for (j = i; (j < tablesize) && (table[j] <= limit1); j++)
         {
            res = table[i] + table[j] ;
            enqueue(target, res, cfg, table[i], table[j], 0) ;
         }
      }
   }

// check PPP configuration
   if ( (((component == R) && (topoppp))
      || ((component == C) && (toposss))) && (!abort_thread))
   {
      if (component == R) cfg = PPP ;
      if (component == C) cfg = SSS ;
      for (i = 0; (i < tablesize) && (table[i] < llimit); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (table[i] <= (3 * ulimit)) && (!abort_thread); i++)
      {
         limit1 = table[i] * maxdeviation ;
         minres = 0 ;
         for (j = i; (j < tablesize) && (table[j] <= limit1) && (minres < ulimit); j++)
         {
            minres = 1/((1/table[i])+(1/table[j])+(1/table[j])) ;
            limit2 = min(table[i],table[j]) * maxdeviation ;
            res = 0 ;
            for (k = j; (k < tablesize) && (table[k] <= limit2) && (res < ulimit); k++)
            {
               res = 1/((1/table[i])+(1/table[j])+(1/table[k])) ;
               enqueue(target, res, cfg, table[i], table[j], table[k]) ;
            }
         }
      }
   }

// check SPP configuration for resistors
   if ((component == R) && (topospp) && (!abort_thread))
   {
      cfg = SPP ;
      for (i = 0; (i < tablesize) && (table[i] < (llimit/(maxdeviation+1))); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (table[i] <= ulimit) && (!abort_thread); i++)
      {
         limit1 = table[i] * maxdeviation ;
         for (j = 0; (j < tablesize) && (table[j] < (llimit-table[i])); j++) ;
         if (j > 0) j-- ;
         for ( ; (j < tablesize) && (table[j] <= limit1); j++)
         {
            limit2 = min(table[i],table[j]) * maxdeviation ;
            res = 0 ;
            for (k = j; (k < tablesize) && (table[k] <= limit2) && (res < ulimit); k++)
            {
               res = table[i] + 1/((1/table[j])+(1/table[k])) ;
               enqueue(target, res, cfg, table[i], table[j], table[k]) ;
            }
         }
      }
   }

// check SPP configuration for capacitors
   if ((component == C) && (topospp) && (!abort_thread))
   {
      cfg = SPP ;
      for (i = 0; (i < tablesize) && (table[i] < llimit); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (!abort_thread); i++)
      {
         limit1 = table[i] * maxdeviation ;
         for (j = 0; (j < tablesize) && (table[j] <= limit1); j++)
         {
            limit2 = min(table[i],table[j]) * maxdeviation ;
            for (k = j; (k < tablesize) && (table[k] <= limit2); k++)
            {
               res = 1/(1/table[i] + 1/(table[j]+table[k])) ;
               enqueue(target, res, cfg, table[i], table[j], table[k]) ;
            }
         }
      }
   }

// check SSP configuration for resistors
   if ((component == R) && (topossp) && (!abort_thread))
   {
      cfg = SSP ;
      for (i = 0; (i < tablesize) && (table[i] < llimit); i++) ;
      if (i > 0) i-- ;
      for ( ; (i < tablesize) && (!abort_thread); i++)
      {
         ulimitres = 1/((1/ulimit) - (1/table[i])) ;
         llimitres = 1/((1/llimit) - (1/table[i])) ;
         limit1 = min(llimitres, table[i] * maxdeviation) ;
         for (j = 0; (j < tablesize) && (table[j] <= limit1); j++)
         {
            limit2 = min(llimitres, min(table[i],table[j]) * maxdeviation) ;
            for (k = j; (k < tablesize) && (table[k] <= limit2); k++)
            {
               res = 1/((1/table[i])+1/(table[j] + table[k])) ;
               enqueue(target, res, cfg, table[j], table[k], table[i]) ;
            }
         }
      }
   }

// check SSP configuration for capacitors
   if ((component == C) && (topossp) && (!abort_thread))
   {
      cfg = SSP ;
      for (i = 0; (i < tablesize) && (table[i] < ulimit) && (!abort_thread); i++)
      {
         limit1 = table[i] * maxdeviation ;
         for (j = 0; (j < tablesize) && (table[j] < (llimit-table[i])); j++) ;
         if (j > 0) j-- ;
         for ( ; (j < tablesize) && (table[j] <= limit1); j++)
         {
            limit2 = min(table[i],table[j]) * maxdeviation ;
            for (k = j; (k < tablesize) && (table[k] <= limit2); k++)
            {
               res = table[i] + 1/(1/table[j] + 1/table[k]) ;
               enqueue(target, res, cfg, table[j], table[k], table[i]) ;
            }
         }
      }
   }

// check SSS configuration
   if ( (((component == R) && (toposss))
      || ((component == C) && (topoppp))) && (!abort_thread))
   {
      if (component == R) cfg = SSS ;
      if (component == C) cfg = PPP ;
      for (i = 0; (i < tablesize) && (table[i] <= ulimit) && (!abort_thread); i++)
      {
         limit1 = min(ulimit, table[i] * maxdeviation) ;
         for (j = i; (j < tablesize) && (table[j] <= limit1); j++)
         {
            limit2 = min(ulimit, min(table[i],table[j]) * maxdeviation) ;
            for (k = j; (k < tablesize) && (table[k] <= limit2); k++)
            {
               res = table[i]+table[j]+table[k] ;
               enqueue(target, res, cfg, table[i], table[j], table[k]) ;
            }
         }
     }
   }

// check RN configuration
   if (component == RN)
   {
      if (topos)
      {
         for (i = 0; (i < tablesize) && (!abort_thread); i++)
         {
            for (j = 0; j < tablesize; j++)
            {
               itotal = target / (table[i] + table[j]) ;
               if ((!valid_imin || (itotal >= imin)) && (!valid_imax || (itotal <= imax)))
               {
                  res = (target - iload * table[i]) * table[j] / (table[i] + table[j]) ;
                  enqueue(target2, res, SS, table[i], table[j], 0) ;
               }
            }
         }
      }


      if (toposs)
      {
         for (i = 0; (i < tablesize) && (!abort_thread); i++)
         {
            for (j = 0; j < tablesize; j++)
            {
               for (k = j; k < tablesize; k++)
               {
                  rges = (1 / ((1 / table[j]) + (1 / table[k]))) ; /* R2 || R3 */
                  itotal = target / (table[i] + rges) ;
                  if ((!valid_imin || (itotal >= imin)) && (!valid_imax || (itotal <= imax)))
                  {
                     res = (target - iload * table[i]) * rges / (table[i] + rges) ;
                     enqueue(target2, res, SPP, table[i], table[j], table[k]) ;
                  }
               }
            }
         }
      }

      if (topopp)
      {
         for (i = 0; (i < tablesize) && (!abort_thread); i++)
         {
            for (j = 0; j < tablesize; j++)
            {
               for (k = j; k < tablesize; k++)
               {
                  rges = table[j] + table[k] ; /* R2 - R3 */
                  itotal = target / (table[i] + rges) ;
                  if ((!valid_imin || (itotal >= imin)) && (!valid_imax || (itotal <= imax)))
                  {
                     res = (target - iload * table[i]) * rges / (table[i] + rges) ;
                     enqueue(target2, res, SSS1, table[i], table[j], table[k]) ;
                  }
               }
            }
         }
      }

      if (topoppp)
      {
         for (i = 0; (i < tablesize) && (!abort_thread); i++)
         {
            for (j = i; j < tablesize; j++)
            {
               for (k = 0; k < tablesize; k++)
               {
                  rges = table[i] + table[j] ; /* R1 - R2 */
                  itotal = target / (table[k] + rges) ;
                  if ((!valid_imin || (itotal >= imin)) && (!valid_imax || (itotal <= imax)))
                  {
                     res = (target - iload * rges) * table[k] / (table[k] + rges) ;
                     enqueue(target2, res, SSS2, table[i], table[j], table[k]) ;
                  }
               }
            }
         }
      }

      if (toposss)
      {
         for (i = 0; (i < tablesize) && (!abort_thread); i++)
         {
            for (j = i; j < tablesize; j++)
            {
               for (k = 0; k < tablesize; k++)
               {
                  rges = (1 / ((1 / table[i]) + (1 / table[j]))) ; /* R1 || R2 */
                  itotal = target / (table[k] + rges) ;
                  if ((!valid_imin || (itotal >= imin)) && (!valid_imax || (itotal <= imax)))
                  {
                     res = (target - iload * rges) * table[k] / (table[k] + rges) ;
                     enqueue(target2, res, PPS, table[i], table[j], table[k]) ;
                  }
               }
            }
         }
      }

   }

// Notify main window that the thread is finished
    SendMessage(MainWindow, WM_USER, USER_PRINTRESULTS, 0) ;

    return 0;
}


