/****************************************************************************
** Leistungsorientierte Krankenanstaltenfinanzierung (LKF)                 **
** Scoring-C-Sourcen V2026.1                                               **
** (C) 2026 BMASGPK                                                        **
****************************************************************************
** utils5.c - Basisfunktionen                                              **
**                                                                         **
**		 Alex.Holzer@chello.at                                             **
**       Bernhard.Pesec@dothealth.at                                       **
***************************************************************************/


#include "lkfint5.h"
#include <ctype.h>
#include <math.h>

static long str2int (const char *str, unsigned int len);


/* function IsEmpty **************************************************
**    Liefert 0, wenn text (Maximallänge len) nicht leer ist, sonst **
**    1.                                                            **
*********************************************************************/

int IsEmpty (const char *text, int len)
{
	while (len && *text == ' ')
		{ ++text; --len; }
	if (!len || !*text) return 1;
	else return 0;
}

/* function IsNull ***************************************************
**    Überprüft, ob in einem String der Wert 0 steht.               **
*********************************************************************/

static int IsNull (const char *text, int len)
{
	int i;
	for (i = 0; i < len && text[i] == ' '; i++);
	for (; i < len && text[i] == '0'; i++);
	for (; i < len && text[i] == ' '; i++);
	if (i == len) return 1;
	else return 0;
}

/* function UCase ****************************************************
**     Konvertiert einen String auf Grossbuchstaben                 **
*********************************************************************/

void UCase(char *ps, int len)
{
	for (; len > 0; len--) ps[len-1] = (char) toupper(ps[len-1]);
}


int CompareUCase (const char *cand, const char *patt, int len)
{
	int i;
	for (i = 0; i < len; i++) {
		if (toupper(cand[i]) != patt[i]) return 1;
	}
	return 0;
}

/* function Util_Datum_Nullen *****************************************
**   Diese Funktion füllt das Datum mit Nullen                       **
**   an den leeren Stellen auf, und setzt Null-stellenDatum auf leer **
**********************************************************************/

void Util_Datum_Nullen(char *pDest, const char *pSrc)
{
	memmove (pDest, pSrc, 8);
	if (pDest[4] == ' ' && pDest[5] >= '0' && pDest[5] <= '9') pDest[4] = '0';
	if (pDest[6] == ' ' && pDest[7] >= '0' && pDest[7] <= '9') pDest[6] = '0';

	if (IsNull (pDest, 8)) memset (pDest, ' ', 8);
}

/** function Util_FC_Nullen *******************************
**     Diese Funktion füllt den FC mit Nullen            **
**     an der 7. und 8. Stelle auf                       **
***********************************************************/

void Util_FC_Nullen(char *pDest, const char *pSrc)
{
	int i, j;
	char tmp_buf[9];

	for (i = 0; i < 8 && pSrc[i] == ' '; i++) ;
	for (j = i; j < 8 && pSrc[j] != ' '; j++) pDest [j-i] = pSrc[j];
	j -= i; 
	for (i = j; i < 8; i++) pDest[i] = ' ';

/*	if (j == 6) memcpy (pDest + 6, "00", 2); */

	memmove (tmp_buf, pDest, 8);
	tmp_buf[8] = 0;
	if (IsNull (tmp_buf, 8)) memset (pDest, ' ', 8);
}

/* function GetInt ***************************************************
**    Überprüft, ob in text in der Länge len ein Integer-Wert steht	**
**    (return 1) oder nicht (return 0). Ist resultat kein NULL-Ptr, **
**    so wird der Integer-Wert darin zurückgeliefert.               **
*********************************************************************/

_SCORING_EXPORT int lkf_GetInt (const char *text, int len, int *resultat)
{
	int digit = 0, minus = 0, erg;
	while (len && *text == ' ') { --len; ++text; }

	if (len	&& (*text == '-' || *text == '+'))
	{ 
		if (*text == '-') minus = 1;
		--len; ++text; 
		while (len && *text == ' ') { --len; ++text; }
	}

	erg = 0;

	while (len && isdigit (*text)) 
	{ 
		erg = erg * 10 + (*text - '0');
		--len; 
		++text; 
		digit = 1; 
	}

	while (len && *text == ' ') { --len; ++text; }

	if ((!len || !*text) && digit) {
	
		if (resultat) {
			*resultat = minus ? -erg : erg;
			return 1;
		}
		else return minus ? -erg : erg;
	
	}
	else {
		if (resultat) *resultat = 0;
		return 0;
	}
}

_SCORING_EXPORT long lkf_DatToLong (const char *dat) {
    return lkf_dat2int(dat);
}

_SCORING_EXPORT char *lkf_LongToDat (long lDat, char *sDatBuf) {
    return lkf_int2dat(lDat, sDatBuf);
}

_SCORING_EXPORT char *lkf_LongToDat8 (long lDat, char *sDatBuf) {
    return lkf_int2dat8(lDat, sDatBuf);
}

double simVal(const char *str, int strlen)
{
/* der Versuch die Basic Val Funktion nachzuempfinden*/
/* Basic Val skipt alle Char zwischen 9-13*/
/* akzeptiert leading Vorzeichen + -*/
/* kennt . als kommaseperator*/
/* beendet scan bei jedem nicht nummerischen Zeichen*/
/* und liefert alles bis dahin gefundene*/
char Buffer[100]; int BufZeiger=0;
int Vorzeichen=0/*FALSE*/; int Number=0/*FALSE*/;
int i;

	if (strlen > 99) return 0.0; /* Zahl zu groß (atof cant handle more)*/

	for (i = 0; i < strlen; i++)
	{
		if (*(str+i) == ' ') continue; /* skip blank*/
		if ((*(str+i) >= 9) && (*(str+i) <= 13)) continue; /* skip it*/
		if ((*(str+i) >= '0') && (*(str+i) <= '9')) {Buffer[BufZeiger++] = *(str+i);Number=(-1)/*TRUE*/;continue;}
		if (*(str+i) == '.')                        {Buffer[BufZeiger++] = *(str+i);Number=(-1)/*TRUE*/;continue;}
		if ((*(str+i) == '-') || (*(str+i) == '+'))
		{
			if (Vorzeichen == (-1)/*TRUE*/) break; /* kein zweites vorzeichen*/
			if (Number == (-1)/*TRUE*/) break;     /* kein vorzeichen nach num.Werten*/
			Buffer[BufZeiger++] = *(str+i);
			Vorzeichen = (-1)/*TRUE*/;
			continue;
		}
		break;   /* falsches Zeichen */
	}
	Buffer[BufZeiger] = 0; /* append ascii Null*/

	return (atof(Buffer));
}

long Util_Alter_Abteilung (LKF_AUFENTHALT *pAufenthalt, const char *abtdat)
{ /* V8.1: Berechnung des Abteilungsalters */
	int y_auf, y_geb, m_auf, m_geb ,d_auf, d_geb;

	GetDate8 (abtdat, &y_auf, &m_auf, &d_auf);
	GetDate8 (pAufenthalt->GEBDAT, &y_geb, &m_geb, &d_geb);
	
	if (m_auf < m_geb || (m_auf == m_geb && d_auf < d_geb)) return y_auf - y_geb - 1;
	else return y_auf - y_geb;
	
}

long Util_Alter (LKF_AUFENTHALT *pAufenthalt)
{
	int y_ent, y_geb, m_ent, m_geb ,d_ent, d_geb;

	GetDate8 (pAufenthalt->GEBDAT, &y_geb, &m_geb, &d_geb);
    if (pAufenthalt->isAmb)
    {
        GetDate8 (pAufenthalt->AUFDAT, &y_ent, &m_ent, &d_ent);
    }
	else if (pAufenthalt->pScoringContext && (pAufenthalt->ENTART == 'V' || !memcmp (pAufenthalt->ENTDAT, "        ", 8))) /* V12.0 */
	{ /* V11.0 - Altersberechnung bei ENTART = V */
		return pAufenthalt->pScoringContext->cmb_jahr - y_geb;
	}
    else
	{
		GetDate8 (pAufenthalt->ENTDAT, &y_ent, &m_ent, &d_ent);
	}
	
	if (m_ent < m_geb || (m_ent == m_geb && d_ent < d_geb)) return y_ent - y_geb - 1;
	else return y_ent - y_geb;
}

/* function DayOfYear ************************************************
**    Liefert die Information, der wievielte Tag im Jahr der angeg. **
**    Tag ist.                                                      **
*********************************************************************/

int DayOfYear (int year, int month, int day)
{
	static int month_len[12] = {  0,  31,  59,  90, 120, 151, 
		                        181, 212, 243, 273, 304, 334};
	int erg;
	if (month > 0 && month < 13) {
		erg = month_len[month - 1] + day;
		if (month > 2 && IS_SCHALTJAHR (year)) erg++;
		return erg;
	}
	else return 0;

}


/* function GetYearDiffDate8 *****************************************
**    Liefert die Anzahl ganzer Jahre zwischen date1 und date2. Ist **
**    date1 das Geburtsdatum und date2 das aktuelle Datum, so lie-  **
**    fert die Funktion das Alter.                                  **
*********************************************************************/

int GetYearDiffDate8 (const char *date1, const char *date2)
{
	int jahr1, jahr2, monat1, monat2, tag1, tag2;

	GetDate8 (date1, &jahr1, &monat1, &tag1);
	GetDate8 (date2, &jahr2, &monat2, &tag2);

	if (monat2 > monat1 || (monat2 == monat1 && tag2 >= tag1))
		return jahr2 - jahr1;
	else return jahr2 - jahr1 - 1;
}

/** function Util_Diff_tage *******************************
**     Berechnet die Verweildauer des Patienten in einer **
**     bestimmten Abteilung.                             **
**********************************************************/ 

long Util_Diff_Tage(LKF_AUFENTHALT *pAufenthalt, short abtidx)
{
	int y, m, d;
	long date1, date2;

	GetDate8 (pAufenthalt->abt[abtidx].ABTDAT, &y, &m, &d);
	date1 = DatJul (y, m, d);

	if (abtidx == (short) (pAufenthalt->abt_anz - 1))
	{
		if (pAufenthalt->ENTART == 'V')
		{
			char buf[10];
			sprintf (buf, "%04d0101", pAufenthalt->pScoringContext->cmb_jahr + 1);
			date2 = lkf_dat2int (buf);
		}
		else date2 = lkf_dat2int (pAufenthalt->ENTDAT);
	}
	else date2 = lkf_dat2int (pAufenthalt->abt[abtidx + 1].ABTDAT);

	if (date1 && date2 > date1) return date2 - date1;
	else return 0;
}


/* function CopyUCase **************************************************
**     Kopiert den Source- in den Destination-Buffer, wobei Klein-    **
**     buchstaben auf Großbuchstaben konvertiert werden               **
***********************************************************************/

void CopyUCase (char *dest, const char *src, unsigned int len)
{
	unsigned int i;
	for (i = 0; src[i] && i < len; i++) dest[i] = (char) toupper (src[i]);
	while (i < len) dest[i++] = ' ';
}

/* function CopyUCaseLTrim *********************************************
**     analoc CopyUCase, leading blanks werden eliminiert             **
***********************************************************************/

void CopyUCaseLTrim (char *dest, const char *src, unsigned int len)
{
	unsigned int i, j;
	for (j = 0; j < len && src[j] == ' '; j++) ;
	len -= j;
	for (i = 0; src[i+j] && i < len; i++) dest[i] = (char) toupper (src[i+j]);
	if (j) memset (dest + len, ' ', j);
}

void CopyLTrim (char *dest, const char *src, unsigned int len)
{
	unsigned int i, j;
	for (j = 0; j < len && src[j] == ' '; j++) ;
	len -= j;
	for (i = 0; i < len && src[i+j]; i++) dest[i] = src[i+j];
	if (j || i < len) memset (dest + i, ' ', j + len - i);
}

unsigned int Copy (char *dest, const char *src, unsigned int len)
{ /* V15.0 */
	unsigned int i;
	for (i = 0; i < len && src[i]; i++)
		dest[i] = src[i];
	if (i < len)
		memset (dest + i, ' ', len - i);
	return i;
}

void CopyTrimNull (char *dest, const char *src, unsigned int len)
{
	unsigned int i, j;
	for (j = 0; j < len && src[j] == ' '; j++) ;
	len -= j;
	for (i = 0; src[i+j] && i < len; i++) dest[i] = src[i+j];
	for (; i > 0 && dest[i-1] == ' '; i--) ;
	dest[i] = 0;

}


int simLTrim(char *pDes, const char *pSrc, int srclen)
{
	int blanklen = 0;

	while (blanklen < srclen && pSrc[blanklen] == ' ') ++blanklen;

	if (pDes) {
		if (blanklen < srclen) 
			memmove(pDes, pSrc + blanklen, srclen - blanklen);

		if (blanklen) 
			memset(pDes + srclen - blanklen, ' ', blanklen);	/* den rest auf blank setzen*/
	}
	return srclen - blanklen;
}

int simTrim (char *pDes, const char *pSrc, int srclen)
{
	int i, leading_blanks, trailing_blanks, nNull, orig_srclen;

	/* V9.1 */
	nNull = srclen;
    
    for (i = 0; i < srclen; i++) {
        if (!pSrc[i]) {
            nNull = i;
            break;
        }
    }
        
	orig_srclen = srclen; /* V9.1a */
	srclen = nNull;

	leading_blanks = 0; 
	while (leading_blanks < srclen && pSrc[leading_blanks] == ' ') leading_blanks++;

	if (leading_blanks == srclen) {
		if (pDes) memset (pDes, ' ', orig_srclen);
		return 0;
	}

	trailing_blanks = 0;
	while (pSrc[srclen - trailing_blanks - 1] == ' ') trailing_blanks++;

	if (pDes) {
		memmove (pDes, pSrc + leading_blanks, srclen - leading_blanks - trailing_blanks);
	
		if (leading_blanks + trailing_blanks)
			memset (pDes + srclen - leading_blanks - trailing_blanks, ' ', leading_blanks + trailing_blanks);
	}

	return srclen - leading_blanks - trailing_blanks;
}

int GetLength (const char *src, int len)
{
	while (len && src[len-1] == ' ') --len;
	return len;
}

int HasBlanks (const char *src, unsigned int len)
{
	unsigned int i;
	for (i = 0; i < len; i++) if (src[i] == ' ') return 1;
	return 0;
}

/* function TrimEqual *************************************************
**     Liefert 1, wenn str in der Länge len abgesehen von leading    **
**     oder trailing blanks dem nullterminierten String cmp ent-     **
**     spricht - sonst 0.                                            **
**********************************************************************/

int TrimEqual (const char *str, int len, const char *cmp)
{
	int i;
	int cmplen = (int) strlen (cmp);

	/* Ermittlung der tatsächlichen Stringlänge (maximal len) */	
	for (i = 0; i < len && str[i] != 0; i++);
	len = i;

	/* Leading Blanks eliminieren */
	while (len && *str == ' ')
	{ --len; ++str; }

	/* Trailing Blanks eliminieren */
	while (len && str[len - 1] == ' ') --len;

	/* Vergleich */
	if (len != cmplen || memcmp (str, cmp, (size_t) cmplen)) return 0;
	else return 1;
}

/*
int UtilICDIndex (const char *icd_text)
{
	return (icd_text[0] - 64) * 100 + (icd_text[1] - '0') * 10 + (icd_text[2] - '0');
}*/

long simCLng(double dNumber)
{
	/* diese Routine soll das dokumentierte Basic verhalten simulieren*/
	/* Basic runded bei werten von genau x.5 zur näachsten GERADEN (?) Zahl*/
	/* zb.: CLng(1.5)=>2  // CLng(2.5)=>2 wird ebenfalls zwei*/
	/* leider verhält sich basic zur laufzeit nicht so; sodaß folgendes auftritt*/
	/* ???????????????????????????????????????????????????????????????????????*/
	/* CLng(1996.5) =>1996 jedoch CLng(11/14*2541)=>1997  ????????????????????*/
	/* ???????????????????????????????????????????????????????????????????????*/
double dVorkomma, dNachkomma;
long lVorkomma;
	dNachkomma = modf(dNumber, &dVorkomma);
	lVorkomma = (long) dVorkomma;
	if (fabs(dNachkomma) <= 0.499999) return lVorkomma;
	if (fabs(dNachkomma) >= 0.500001) return (dNumber >= 0.0 ? lVorkomma+1 : lVorkomma-1);
	/* equal 0.5*/
	if ((lVorkomma%2) == 0)/*even number*/ return lVorkomma;
	else                   /*odd  number*/ return (dNumber >= 0.0 ? lVorkomma+1 : lVorkomma-1);
}

int TrimCompare (const char *str, int len, const char *cmp, char krit)
{
	int i;
	for (i = 0; i < len && str[i]; i++) ;
	len = i;

	while (len && *str == ' ')
	{ --len; ++str; }

	while (len && str[len - 1] == ' ') --len;

	while (len && *cmp) {
	
		if (*str > *cmp) {
			if (krit & PAT_KRIT_GREATER) return 1;
			else if ((krit & PAT_KRIT_LESS) || (krit & PAT_KRIT_EQUAL)) return 0;
		}
		else if (*str < *cmp) {
			if ((krit & PAT_KRIT_GREATER) || (krit & PAT_KRIT_EQUAL)) return 0;
			else if (krit & PAT_KRIT_LESS) return 1;
		}
	
		++str; ++cmp; --len;
	}

	while (*cmp == ' ') ++cmp;

	if (len) {
		if (krit & PAT_KRIT_GREATER) return 1;
		return 0;
	}
	else if (*cmp) {
		if (krit & PAT_KRIT_LESS) return 1;
		else return 0;
	}
	else if (krit & PAT_KRIT_EQUAL) return 1;
	else return 0;
}

/* function TrimWildcardEqual *****************************************
**     Wie TrimEqual, nur darf der Compare-String cmp die Wildcard-  **
**     symbole '*' für beliebig viele (auch 0) Zeichen und '%' für   **
**     genau ein beliebiges Zeichen enthalten.                       **
**********************************************************************/

static int WildcardEqual (const char *str, int len, const char *cmp)
{
	while (*cmp && len) {
	
		switch (*cmp) {
		
			case PAT_KRIT_WILDCARD_ONE_CHARACTER:

				break;

			case PAT_KRIT_WILDCARD_SOME_CHARACTERS:

				while (*cmp == '*') ++cmp;
				if (!*cmp) return 1;

				while (len) {
					if (TrimWildcardEqual (str, len, cmp)) return 1;
					++str; --len;
				}
				return 0;
			
			default: 
				if (*str != *cmp) return 0;
		
		}
	
		++cmp; ++str; --len;
	}
	
	if (len) return 0;
	
	while (*cmp == '*') ++cmp;
	if (*cmp) return 0;

	return 1;
}

int TrimWildcardEqual (const char *str, int len, const char *cmp)
{
	while (len && *str == ' ')
	{ --len; ++str; }

	while (len && str[len - 1] == ' ') --len;
	return WildcardEqual (str, len, cmp);
}

const char *get_string_ext (const char *src, unsigned int offset)
{ /* V11.1 */
	unsigned int l = (unsigned int) strlen (src);
	if (l <= offset) return 0;
	else return src + offset;
}



int expand_string (const char *src, int len, char *buf)
{ /* V5.1 */
	int l;
	strncpy (buf, src, len);
	buf[len - 1] = 0;
	
	l = (int) strlen (buf);
	while (l > 0 && (buf[l-1]=='\n' || buf[l-1]=='\r')) --l; /* V11.1: \r fuer Unix-kompatible Files */
	
	if (len - l - 1 > 0) memset (buf + l, ' ', len - l - 1);
	return l;
}

/************************************************************/
/* Nachbau der Basicfunktionen								*/
/************************************************************/

static const int daysinmonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const int daysaccu[12] =    {  0, 31, 59, 90,120,151,181,212,243,273,304,334};

void DatLongToYMD (long lDate, int *pYear, int *pMonth, int *pDay)
{ /* V11.0 - stimmt bis inkl. Jahr 2099 */
	int nYear, nMonth, nDay, n, nSchaltJahr;

	nYear = (int) (lDate / (366*97+365*303));
	lDate -= nYear * (366*97+365*303);
	nYear *= 400;

	nDay = (int) (lDate / (366*24+365*76));
	lDate -= nDay * (366*24+365*76);
	nYear += (nDay*100);

	nDay = (int) (lDate / (365*4+1));
	lDate -= nDay * (365*4+1);
	nYear += 4 * nDay;
	nYear++;

	nSchaltJahr = 0;
	if (lDate >= 3*365)
	{
		nYear += 3;
		lDate -= 3*365;
        nSchaltJahr = 1;
	}
	else if (lDate >= 2*365)
	{
		nYear += 2;
		lDate -= 2 * 365;
	}
	else if (lDate >= 365)
	{
		nYear++;
		lDate -= 365;
	}
		

	nMonth = 0;

	for (n = 0; !nMonth && n < 12; n++)
	{
		if (n == 1 && nSchaltJahr)
		{
			if (29 > lDate)
			{
				nMonth = 2;
				nDay = (int)(lDate + 1);
			}
			else lDate -= 29;
		}
		else if (daysinmonth[n] > lDate)
		{
			nMonth = n + 1;
			nDay = (int)(lDate + 1);
		}
		else lDate -= daysinmonth[n];
	}

	*pYear = nYear;
	*pMonth = nMonth;
	*pDay = nDay;
}


long DatJul (int year, int month, int day)
{
	
	long value;

	if (year < 0 || month < 1 || month > 12 || day < 1 || day > 31) return -1;

	if (month == 2) {
		if (!(year%4) && (year%100 || !(year%400))) {
			if (day > 29) return -1;
		}
		else if (day > 28) return -1;
	}
	else if (day > daysinmonth[month-1]) return -1;


	value = (year-1) * 365;
	if (month < 3) --year;
	value += (year / 4) - (year/100) + (year/400);
	value += daysaccu[month-1] + day - 1;
	return value;
}



/************************************************************************
** Funktion lkf_dat2int: Konvertiert ein Datum der Form yyyymmdd in    **
**     eine Zahl, wobei aufeinanderfolgende Zahlen aufeinanderfolgende **
**     Tage darstellen.                                                **
************************************************************************/

static long str2int (const char *str, unsigned int len)
{
	long value;
	unsigned int i;
	i = 0;
	value = 0;

	/* Allow leading blanks */
	while (i < len && str[i] == ' ') ++i;
	if (i == len) return -1;

	/* Compute Integer Value */
	while (i < len) {
		if (str[i] < '0' || str[i] > '9') return -1;
		value = value * 10 + (str[i++] - '0');
	}
	return value;
}

static long str2intStrict (const char *str, unsigned int len)
{ /* V2025.0 */
    long value;
    unsigned int i;
    i = 0;
    value = 0;

    /* Compute Integer Value */
    while (i < len) {
        if (str[i] < '0' || str[i] > '9') return -1;
        value = value * 10 + (str[i++] - '0');
    }
    return value;
}



long lkf_str2int (const char *str, unsigned int len) {
    return str2int(str, len);
}



long lkf_time2int (const char *tim)
{ /* V8.1 - konvertiert eine Zeitangabe in einen Ganzzahlwert */
	int stunden, minuten;
	stunden = (int) str2int (tim, 2);
	minuten = (int) str2int (tim + 2, 2);

	if (stunden < 0 || stunden > 23 || minuten < 0 || minuten > 59) return 0;
	else return minuten + stunden * 60 + 1;
}

long lkf_time2intStrict (const char *tim)
{ /* V8.1 - konvertiert eine Zeitangabe in einen Ganzzahlwert */
    int stunden, minuten;
    
    stunden = (int) str2intStrict (tim, 2);
    minuten = (int) str2intStrict (tim + 2, 2);

    if (stunden < 0 || stunden > 23 || minuten < 0 || minuten > 59) return 0;
    else return minuten + stunden * 60 + 1;
}



char *lkf_int2dat (long lDat, char *sDatBuf)
{ /* V11.0 */
	int nYear, nMonth, nDay;
	DatLongToYMD (lDat, &nYear, &nMonth, &nDay);
	sprintf (sDatBuf, "%02d.%02d.%04d", nDay, nMonth, nYear);
	return sDatBuf;
}

char *lkf_int2dat8 (long lDat, char *sDatBuf)
{ /* V11.0 */
    int nYear, nMonth, nDay;
    char buf[10];
    
    DatLongToYMD (lDat, &nYear, &nMonth, &nDay);
    sprintf (buf, "%04d%02d%02d", nYear, nMonth, nDay);
    memcpy (sDatBuf, buf, 8);
    return sDatBuf;
}


long lkf_dat2int (const char *dat)
{	
	int year, month, day;

	/* V7.0 - BUGFIX: 0 statt -1 wird im DatJul-Fehlerfall geliefert */
	if (GetDate8 (dat, &year, &month, &day)) {
		long d = DatJul (year, month, day);
		if (d > 0) return d;
		else return 0;
	}
	else return 0;
}

long lkf_dat2intStrict (const char *dat)
{
    int year, month, day;

    /* V7.0 - BUGFIX: 0 statt -1 wird im DatJul-Fehlerfall geliefert */
    if (GetDate8Strict (dat, &year, &month, &day)) {
        long d = DatJul (year, month, day);
        if (d > 0) return d;
        else return 0;
    }
    else return 0;
}

/*
static void test_int2dat () {
    char datbuf[12];
    printf ("%10ld/%s\n", lkf_dat2int("19981231"), lkf_int2dat(lkf_dat2int("19981231"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("19990228"), lkf_int2dat(lkf_dat2int("19990228"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("19990301"), lkf_int2dat(lkf_dat2int("19990301"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("19991231"), lkf_int2dat(lkf_dat2int("19991231"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20000101"), lkf_int2dat(lkf_dat2int("20000101"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20000228"), lkf_int2dat(lkf_dat2int("20000228"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20000229"), lkf_int2dat(lkf_dat2int("20000229"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20000301"), lkf_int2dat(lkf_dat2int("20000301"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20000401"), lkf_int2dat(lkf_dat2int("20000401"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20001201"), lkf_int2dat(lkf_dat2int("20001201"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20010101"), lkf_int2dat(lkf_dat2int("20010101"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20010110"), lkf_int2dat(lkf_dat2int("20010110"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20010228"), lkf_int2dat(lkf_dat2int("20010228"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20010301"), lkf_int2dat(lkf_dat2int("20010301"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20020210"), lkf_int2dat(lkf_dat2int("20020210"), datbuf));
    printf ("%10ld/%s\n", lkf_dat2int("20020310"), lkf_int2dat(lkf_dat2int("20020310"), datbuf));
}
*/


/* function GetDate8 ************************************************
**    Überprüft das Format des Datums in text (YYYYMMDD) und lie-  **
**    Jahr, Monat und Tag zurück.                                  **
********************************************************************/

int GetDate8 (const char *text, int *year, int *month, int *day)
{
	*year = (int) str2int (text, 4);
	*month = (int) str2int (text + 4, 2);
	*day = (int) str2int (text + 6, 2);

	if (*year < 0 || *month < 0 || *day < 0) return 0;
	else return 1;
}

int GetDate8Strict (const char *text, int *year, int *month, int *day)
{
    *year = (int) str2intStrict (text, 4);
    *month = (int) str2intStrict (text + 4, 2);
    *day = (int) str2intStrict (text + 6, 2);

    if (*year < 0 || *month < 0 || *day < 0) return 0;
    else return 1;
}

/* function Util_Datum_Format ********************************************
**    Überprüft, ob das Datumsformat paßt und dieses Datum auch gültig  **
**    ist (und zwischen 1850 und 2200 liegt).                           **
*************************************************************************/

int Util_Datum_Format (const char *datum)
{
	if (lkf_dat2int (datum) > 0) return 0;
	else return 1;
}

/* function SmallerDate8 ********************************************
**    Liefert 1, wenn date1 vor date2 liegt, sonst 0.              **
********************************************************************/

int SmallerDate8 (const char *date1, const char *date2)
{
	long d1, d2;

	d1 = lkf_dat2int (date1);
	d2 = lkf_dat2int (date2);

	if (d1 < d2) return 1;
	else return 0;
}

/********************************************************************************************
** Dynamische Zeilenverwaltung                                                             **
********************************************************************************************/

LKF_LINE_BUFFER *lkf_create_linebuffer (LKF_CONTEXT *pContext)
{
	LKF_LINE_BUFFER *pBuffer = (LKF_LINE_BUFFER *) LKF_MALLOC (pContext, "lkf_create_linebuffer", sizeof(LKF_LINE_BUFFER));
	if (!pBuffer) return 0;
	pBuffer->lBufAlloc = LKF_LINE_BUFFER_BUFALLOC;
	pBuffer->lLineAlloc = LKF_LINE_BUFFER_LINEALLOC;
	pBuffer->pBuf = (char *) LKF_MALLOC (pContext, "lkf_create_linebuffer.2", pBuffer->lBufAlloc * sizeof (char));
	if (!pBuffer->pBuf) {
		LKF_FREE(pContext, "lkf_create_linebuffer", pBuffer);
		return 0;
	}
	pBuffer->plOffsets = (unsigned long *) LKF_MALLOC (pContext, "lkf_create_linebuffer.3", pBuffer->lLineAlloc * sizeof(unsigned long));
	if (!pBuffer->plOffsets) {
		LKF_FREE(pContext, "lkf_create_linebuffer.2", pBuffer->pBuf);
		LKF_FREE(pContext, "lkf_create_linebuffer", pBuffer);
		return 0;
	}
	pBuffer->lLineCount = 0;
	pBuffer->lBufLen = 0; 
	pBuffer->lBufExtend = LKF_LINE_BUFFER_BUFEXTEND;
	pBuffer->lLineExtend = LKF_LINE_BUFFER_LINEEXTEND;
	return pBuffer;
}

void lkf_drop_linebuffer (LKF_CONTEXT *pContext, LKF_LINE_BUFFER *pBuffer)
{
	if (pBuffer) {
		LKF_FREE (pContext, "lkf_create_linebuffer.3", pBuffer->plOffsets);
		LKF_FREE (pContext, "lkf_create_linebuffer.2", pBuffer->pBuf);
		LKF_FREE (pContext, "lkf_create_linebuffer", pBuffer);
	}
}

unsigned int lkf_add_dynamic_line (LKF_CONTEXT *pContext, LKF_LINE_BUFFER *pLineBuffer, const char *sBuf)
{
	const char *sStoredBuffer;
	size_t len = strlen (sBuf);
	while (len > 0 && (sBuf[len-1] == 10 || sBuf[len-1] == 13)) --len;

	if (pLineBuffer->lLineCount == pLineBuffer->lLineAlloc) {
		unsigned long* ppOldBuf = pLineBuffer->plOffsets;

		if (!pLineBuffer->lLineExtend) pLineBuffer->lLineAlloc++;
		else pLineBuffer->lLineAlloc += pLineBuffer->lLineExtend;

		pLineBuffer->plOffsets = (unsigned long*)LKF_MALLOC(pContext, "lkf_add_dynamic_line", pLineBuffer->lLineAlloc * sizeof(unsigned long));

		if (pLineBuffer->plOffsets == 0) {
            if (!pLineBuffer->lLineExtend) pLineBuffer->lLineAlloc--;
            else pLineBuffer->lLineAlloc -= pLineBuffer->lLineExtend;
            pLineBuffer->plOffsets = ppOldBuf;
			return LKF_OUTOFMEMORY_UINTERROR;
		}
		
		memcpy (pLineBuffer->plOffsets, ppOldBuf, sizeof (unsigned long) * pLineBuffer->lLineCount);
		LKF_FREE (pContext, "lkf_add_dynamic_line", ppOldBuf);
	}

	if (pLineBuffer->lBufLen + len + 1 >= pLineBuffer->lBufAlloc) {
		
        unsigned long extendBytes;
        unsigned int needed_bytes = (unsigned int) (pLineBuffer->lBufLen + len + 1 - pLineBuffer->lBufAlloc);
		char *pOldBuf = pLineBuffer->pBuf;
		if (needed_bytes < pLineBuffer->lBufExtend) needed_bytes = (unsigned int)(pLineBuffer->lBufExtend);
		
        extendBytes = pLineBuffer->lBufAlloc / 2;
        
        if (needed_bytes < extendBytes) {
            pLineBuffer->lBufAlloc += extendBytes;
        }
        else {
            pLineBuffer->lBufAlloc += (unsigned int) needed_bytes;
        }

		pLineBuffer->pBuf = (char *) LKF_MALLOC (pContext, "lkf_add_dynamic_line.buffer", pLineBuffer->lBufAlloc * sizeof (char));
	
		if (pLineBuffer->pBuf == 0) {
			LKF_FREE(pContext, "lkf_add_dynamic_line.buffer", pOldBuf);
			return LKF_OUTOFMEMORY_UINTERROR;
		}
		
		memcpy (pLineBuffer->pBuf, pOldBuf, sizeof (char) * pLineBuffer->lBufLen);
		LKF_FREE (pContext, "lkf_add_dynamic_line.buffer", pOldBuf);
	}

	pLineBuffer->plOffsets[pLineBuffer->lLineCount++] = pLineBuffer->lBufLen;
	sStoredBuffer = pLineBuffer->pBuf + pLineBuffer->lBufLen;
	memcpy (pLineBuffer->pBuf + pLineBuffer->lBufLen, sBuf, len);
	pLineBuffer->pBuf[pLineBuffer->lBufLen + len] = 0;
	pLineBuffer->lBufLen += (unsigned long) len + 1;

	return (unsigned int) pLineBuffer->lLineCount - 1;
}

const char *lkf_get_line (LKF_LINE_BUFFER *pLineBuffer, unsigned int index)
{
	if (index < pLineBuffer->lLineCount) return pLineBuffer->pBuf + pLineBuffer->plOffsets[index];
	else return 0;
}

int lkf_get_linelen (LKF_LINE_BUFFER *pLineBuffer, unsigned int index)
{
	if (index < pLineBuffer->lLineCount - 1) return (int) (pLineBuffer->plOffsets[index + 1] - pLineBuffer->plOffsets[index] - 1);
	else if (index == pLineBuffer->lLineCount - 1) return (int) (pLineBuffer->lBufLen - pLineBuffer->plOffsets[index] - 1);
	else return -1;
}

/* V2024.1: String command handling */

static int lkfcommand_sort_function (const void *c1, const void *c2) {
    const LKF_COMMAND *comm1, *comm2;
    comm1 = (const LKF_COMMAND *) c1;
    comm2 = (const LKF_COMMAND *) c2;
    
    return strcmp (comm1->command, comm2->command);
}

LKF_COMMANDS* lkfcommand_alloc (LKF_CONTEXT *pContext, const char *s) {
    const char *strpos;
    const char *nextpos, *ce;
    unsigned int commandlen, valuelen;
    LKF_COMMANDS *pCommands;
    strpos = s;
    
    pCommands = LKF_MALLOC(pContext, "lkfcommand_alloc", sizeof(LKF_COMMANDS));
    if (pCommands == 0) {
        return 0;
    }
    pCommands->command_alloc = 20;
    pCommands->pContext = pContext;
    pCommands->command_count = 0;
    pCommands->commands = LKF_CALLOC(pContext, "lkfcommand_alloc", sizeof(LKF_COMMAND), pCommands->command_alloc);
    
    do {
        nextpos = strchr(strpos, '|');
        ce = strchr (strpos, '=');
        if (ce && (!nextpos || ce < nextpos)) {
            commandlen = (unsigned int) (ce - strpos);
            if (nextpos) {
                valuelen = (unsigned int) (nextpos - ce) - 1;
            }
            else {
                valuelen = (unsigned int) strlen(ce + 1);
            }
        }
        else if (nextpos) {
            commandlen = (unsigned int) (nextpos - strpos);
            valuelen = 0;
        }
        else {
            commandlen = (unsigned int) strlen(strpos);
            valuelen = 0;
        }
        if (commandlen == 0) {
            
            strpos = nextpos ? nextpos + 1 : 0;
            continue;
        }
        
        if (commandlen > 24) {
            commandlen = 24;
        }
        if (valuelen > 64) {
            valuelen = 64;
        }
        
        if (pCommands->command_count == pCommands->command_alloc) {
            LKF_COMMAND *pOld = pCommands->commands;
            pCommands->command_alloc += 20;
            pCommands->commands = LKF_CALLOC(pContext, "lkfcommand_alloc", sizeof(LKF_COMMAND), pCommands->command_alloc);
            if (pCommands->commands == 0) {
                LKF_FREE(pContext, "lkfcommand_alloc", pOld);
                return 0;
            }
            memcpy(pCommands->commands, pOld, sizeof(LKF_COMMAND) * pCommands->command_count);
            LKF_FREE(pContext, "lkfcommand_alloc", pOld);
        }
        if (valuelen > 0) {
            memcpy (pCommands->commands[pCommands->command_count].value, ce + 1, valuelen);
            pCommands->commands[pCommands->command_count].value[valuelen] = 0;
        }
        memcpy (pCommands->commands[pCommands->command_count].command, strpos, commandlen);
        pCommands->commands[pCommands->command_count++].command[commandlen] = 0;
        strpos = nextpos ? nextpos + 1 : 0;
    } while (strpos);
    qsort(pCommands->commands, pCommands->command_count, sizeof(LKF_COMMAND), lkfcommand_sort_function);
    return pCommands;
}

void lkfcommand_free (LKF_COMMANDS *commands) {
    LKF_FREE(commands->pContext, "lkfcommand_free", commands->commands);
    LKF_FREE (commands->pContext, "lkfcommand_free", commands);
}

LKF_COMMAND *lkfcommand_get (const LKF_COMMANDS *commands, const char *name) {
    
    LKF_COMMAND key;
    strncpy (key.command, name, 24);
    
    return (LKF_COMMAND *) bsearch(&key, commands->commands, commands->command_count, sizeof (LKF_COMMAND), lkfcommand_sort_function);
}


/********************************************************************************************
** Memory Check Routinen                                                                   **
********************************************************************************************/

static LKF_MEMORYSETTINGS *pMemorySettings = 0;
static long long memoryBlockCount = 0;

#define MEMORY_CHECK_BOUNDS_END 1
#ifdef MEMORY_CHECK_BOUNDS_END
#define MEMORY_SIZE_ADD (sizeof(long long) * 3)
#define AFTER_MARKER 54234567345233511
#else
#define MEMORY_SIZE_ADD (sizeof(long long) * 2)
#endif
#define MEMORY_SIZE_ADJUST_PTR (sizeof(long long) * 2)
#define BEFORE_MARKER 1238456980234945

_SCORING_EXPORT long long lkf_debugmemory_bytes(void) {
    if (pMemorySettings) return pMemorySettings->open_memory;
    return 0;
}

_SCORING_EXPORT long long lkf_debugmemory_blocks(void) {
	return memoryBlockCount;
}


_SCORING_EXPORT void lkf_setMemoryTraceDelegate (LKFMEMORY_DELEGATE del, int delegateFlags) {
    if (!pMemorySettings) {
		pMemorySettings = (LKF_MEMORYSETTINGS*)calloc(1, sizeof(LKF_MEMORYSETTINGS));
	}
    pMemorySettings->memory_delegate = del;
    pMemorySettings->delegateFlags = delegateFlags;
}

_SCORING_EXPORT int lkf_enableMemoryTrace (void) {

	if (memoryBlockCount) {
		if (pMemorySettings && (pMemorySettings->flags & LKF_MEMORYSETTINGS_DEBUG_MEMORY)) return 0;
		return -1;
	}
    if (!pMemorySettings) {
        pMemorySettings = (LKF_MEMORYSETTINGS *) calloc (1, sizeof(LKF_MEMORYSETTINGS));
    }
    pMemorySettings->flags = LKF_MEMORYSETTINGS_DEBUG_MEMORY;
	return 0;
}

_SCORING_EXPORT void lkf_set_memoryDebugLimit (long long available, long long used, const char *breaking, int breakingCount) {
    if (!pMemorySettings) {
		pMemorySettings = (LKF_MEMORYSETTINGS*) calloc(1, sizeof(LKF_MEMORYSETTINGS));
	}
    pMemorySettings->availableMemory = (size_t) available;
    pMemorySettings->usedMemory = (size_t) used;
    if (breaking) {
        strncpy (pMemorySettings->breakingFunction, breaking, 255);
        pMemorySettings->breakingFunction[255] = 0;
        pMemorySettings->breakingCount = breakingCount;
        pMemorySettings->breakingHitCount = 0;
    }
    else {
        pMemorySettings->breakingFunction[0] = 0;
    }
    pMemorySettings->flags |= LKF_MEMORYSETTINGS_LIMIT;
}

_SCORING_EXPORT unsigned int lkf_get_debugmemoryStatus (char *bufptr, unsigned int len) {
    char buf[1024];
    unsigned int bl;
    if (pMemorySettings == 0) {
        if (bufptr) *bufptr = 0;
        return 0;
    }
    
    sprintf (buf, "used=%ld, breakingCount=%d", (long) pMemorySettings->usedMemory, pMemorySettings->breakingHitCount);
    
    bl = (unsigned int) strlen(buf);
    
    if (bufptr) {
        strncpy (bufptr, buf, len);
        if (len >= bl) bufptr[len - 1] = 0;
    }
    return bl;
}

_SCORING_EXPORT void lkf_clear_debug_memory_limit (void) {
    if (!pMemorySettings) return;
    pMemorySettings->availableMemory = 0;
    pMemorySettings->usedMemory = 0;
    pMemorySettings->breakingFunction[0] = 0;
    pMemorySettings->breakingCount = 0;
    pMemorySettings->flags &= ~LKF_MEMORYSETTINGS_LIMIT;
}


static int lkf_memory_limit_reached (LKF_MEMORYSETTINGS *pMemorySettings, LKF_CONTEXT *pContext, const char *op, const char *key, size_t elem, size_t siz, int freeMemory) {
    
    size_t memorySize = siz * elem;
    
    if (freeMemory) {
        pMemorySettings->usedMemory -= memorySize;
    }
    else {
        if (pMemorySettings->availableMemory > 0 && pMemorySettings->availableMemory - pMemorySettings->usedMemory < memorySize) {
            if (!strcmp (pMemorySettings->breakingFunction, key)) {
                pMemorySettings->breakingHitCount += 1;
                if (pMemorySettings->breakingCount - pMemorySettings->breakingHitCount < 0) {
                    
                    if (!strcmp (key, "...")) {
                        return 1;
                    }
                    
                    return 1;
                }
            }
        }
        else if (pMemorySettings->availableMemory == 0 && !strcmp (pMemorySettings->breakingFunction, key)) {
            pMemorySettings->breakingHitCount += 1;
            if (pMemorySettings->breakingCount - pMemorySettings->breakingHitCount < 0) {
                
/*
                if (!strcmp (key, "lkf_optimize_amb_melmel")) {
                    return 1;
                }
*/
                return 1;
            }
        }
        pMemorySettings->usedMemory += memorySize;
    }
    
    return 0;
}

#define LKF_MEMORYSETTINGS_TRACK_SIZE (LKF_MEMORYSETTINGS_LIMIT|LKF_MEMORYSETTINGS_DEBUG_MEMORY)

void *lkf_malloc (LKF_CONTEXT *pContext, const char *key, size_t siz)
{
    void *ptr;
    if (!pMemorySettings) {
        ptr = malloc (siz);
        if (!ptr) lkf_set_outOfMemory(pContext, key, siz);
		else memoryBlockCount++;
        return ptr;
    }
    
    if (pMemorySettings->flags & LKF_MEMORYSETTINGS_TRACK_SIZE) {
        if ((pMemorySettings->flags & LKF_MEMORYSETTINGS_LIMIT) && lkf_memory_limit_reached(pMemorySettings, pContext, "malloc", key, 1, siz, 0)) {
            ptr = 0;
        }
        else {
            char *cp = (char *) malloc(siz + MEMORY_SIZE_ADD);
            if (cp) {
                pMemorySettings->open_ptr += 1;
                pMemorySettings->open_memory += siz;
                ((long long*) cp)[0] = siz;
                ((long long*) cp)[1] = BEFORE_MARKER;
                ptr = (cp + MEMORY_SIZE_ADJUST_PTR);
#ifdef MEMORY_CHECK_BOUNDS_END
                ((long long *) (cp + siz))[2] = AFTER_MARKER;
#endif
            }
            else {
                ptr = 0;
            }
        }
    }
    else {
        ptr = malloc(siz);
    }
    
    if (!ptr) {
        lkf_set_outOfMemory(pContext, key, siz);
        if (pMemorySettings->memory_delegate) pMemorySettings->memory_delegate (pContext, "malloc", key, (long long) ptr, 1, (long) siz);
    }
    else {
		if (pMemorySettings->memory_delegate && (pMemorySettings->delegateFlags & LKF_MEMORYSETTINGS_DELEGATE_ALL)) {
			pMemorySettings->memory_delegate(pContext, "malloc", key, (long long)ptr, 1, (long)siz);
		}
		memoryBlockCount++;
	}

    return ptr;
}

void *lkf_calloc (LKF_CONTEXT *pContext, const char *key, size_t nr, size_t siz)
{
    void *ptr;
    if (!pMemorySettings) {
        ptr = calloc(nr, siz);
        if (!ptr) lkf_set_outOfMemory(pContext, key, siz);
		else memoryBlockCount++;
		return ptr;
    }
    
    if (pMemorySettings->flags & LKF_MEMORYSETTINGS_TRACK_SIZE) {
        if ((pMemorySettings->flags & LKF_MEMORYSETTINGS_LIMIT) && lkf_memory_limit_reached(pMemorySettings, pContext, "calloc", key, nr, siz, 0)) {
            ptr = 0;
        }
        else {
            size_t callocSize;
            char *cp;
            callocSize = nr * siz;
            cp = (char *) malloc(callocSize + MEMORY_SIZE_ADD);
            if (cp) {
                pMemorySettings->open_ptr += 1;
                pMemorySettings->open_memory += callocSize;
                ((long long*) cp)[0] = callocSize;
                ((long long*) cp)[1] = BEFORE_MARKER;
                ptr = (cp + MEMORY_SIZE_ADJUST_PTR);
#ifdef MEMORY_CHECK_BOUNDS_END
                ((long long *) (cp + callocSize))[2] = AFTER_MARKER;
#endif
                memset (ptr, 0, callocSize);
            }
            else {
                ptr = 0;
            }
        }
    }
    else {
        ptr = calloc (nr, siz);
    }
    
    if (!ptr) {
        lkf_set_outOfMemory(pContext, key, siz);
        if (pMemorySettings->memory_delegate) pMemorySettings->memory_delegate (pContext, "calloc", key, (long long) ptr, (long) nr, (long) siz);
    }
    else {
		if (pMemorySettings->memory_delegate && (pMemorySettings->delegateFlags & LKF_MEMORYSETTINGS_DELEGATE_ALL)) {
			pMemorySettings->memory_delegate(pContext, "calloc", key, (long long)ptr, (long)nr, (long)siz);
		}
		memoryBlockCount++;
    }
    return ptr;
}

void *lkf_realloc (LKF_CONTEXT *pContext, const char *key, void *p, size_t siz)
{
    void *ptr;
    char* np;
    unsigned long long freed_size = 0;
    if (!pMemorySettings) {
        ptr = realloc(p, siz);
		if (!ptr) {
			lkf_set_outOfMemory(pContext, key, siz);
			memoryBlockCount--;
		}
        return ptr;
    }
    
    if (pMemorySettings->flags & LKF_MEMORYSETTINGS_TRACK_SIZE) {
        
        if (p) {
            np = ((char*)p) - MEMORY_SIZE_ADJUST_PTR;
            if (((long long*)np)[1] != BEFORE_MARKER) {
                if (pMemorySettings->memory_delegate) {
                    pMemorySettings->memory_delegate(pContext, "bounds:free", key, (long long) p, 0, 0);
                }
                else {
                    printf("realloc - memory overflow detected\n");
                }
            }
            freed_size = ((long long*)np)[0];
#ifdef MEMORY_CHECK_BOUNDS_END
            if (((long long*)(np+freed_size))[2] != AFTER_MARKER) {
                if (pMemorySettings->memory_delegate) {
                    pMemorySettings->memory_delegate(pContext, "bounds:free:end", key, (long long) p, 0, 0);
                }
                else {
                    printf("realloc - end memory overflow detected\n");
                }
            }
#endif
            pMemorySettings->open_ptr -= 1;
            pMemorySettings->open_memory -= freed_size;

        }
        else {
            np = 0;
            freed_size = 0;
        }
        
        if ((pMemorySettings->flags & LKF_MEMORYSETTINGS_LIMIT)
            && (!p || lkf_memory_limit_reached(pMemorySettings, pContext, "free-realloc", key, 1, (size_t) freed_size, 1) || 1)
            && lkf_memory_limit_reached(pMemorySettings, pContext, "realloc", key, 1, siz, 0)) {
            ptr = 0;
        }
        else {
            char *cp = (char *) malloc(siz + MEMORY_SIZE_ADD);
            if (cp) {
                pMemorySettings->open_ptr += 1;
                pMemorySettings->open_memory += siz;
                ((long long*) cp)[0] = siz;
                ((long long*) cp)[1] = BEFORE_MARKER;
                ptr = (cp + MEMORY_SIZE_ADJUST_PTR);
#ifdef MEMORY_CHECK_BOUNDS_END
                ((long long *) (cp + siz))[2] = AFTER_MARKER;
#endif

                if (siz < freed_size) {
                    memcpy (ptr, p, siz);
                }
                else {
                    memcpy (ptr, p, (size_t) freed_size);
                }
            }
            else {
                ptr = 0;
            }
        }
        if (np) free (np);
    }
    else {
        ptr = realloc(p, siz);
    }
    
    if (!ptr) {
		memoryBlockCount--;
        lkf_set_outOfMemory(pContext, key, siz);
        
        if (pMemorySettings->memory_delegate) {
            if (p && (pMemorySettings->delegateFlags & LKF_MEMORYSETTINGS_DELEGATE_ALL)) {
                pMemorySettings->memory_delegate(pContext, "free-realloc", key, (long long) p, (long) 1, (long) freed_size);
            }
            pMemorySettings->memory_delegate (pContext, "realloc", key, (long long) ptr, 1, (long)siz);
        }
    }
    else if (pMemorySettings->memory_delegate && (pMemorySettings->delegateFlags & LKF_MEMORYSETTINGS_DELEGATE_ALL)) {
        if (p) pMemorySettings->memory_delegate(pContext, "free-realloc", key, (long long) p, (long) 0, (long) freed_size);
        pMemorySettings->memory_delegate (pContext, "realloc", key, (long long) ptr, 1, (long)siz);
    }
    return ptr;
}

void lkf_free (LKF_CONTEXT *pContext, const char *key, void *p)
{
    long long freed_size = 0;
    
    if (!p) return;
    
    if (!pMemorySettings) {
        free (p);
		memoryBlockCount--;
        return;
    }
    
    if (pMemorySettings->flags & LKF_MEMORYSETTINGS_TRACK_SIZE) {
        char* np = ((char*)p) - MEMORY_SIZE_ADJUST_PTR;
        if (((long long*)np)[1] != BEFORE_MARKER) {
            if (pMemorySettings->memory_delegate) {
                pMemorySettings->memory_delegate(pContext, "bounds:free", key, (long long) p, 0, 0);
            }
            else {
                printf("free - memory overflow detected\n");
            }
        }
        pMemorySettings->open_ptr -= 1;
        freed_size = ((long long*)np)[0];
#ifdef MEMORY_CHECK_BOUNDS_END
        if (((long long*)(np+freed_size))[2] != AFTER_MARKER) {
            if (pMemorySettings->memory_delegate) {
                pMemorySettings->memory_delegate(pContext, "bounds:free:end", key, (long long) p, 0, 0);
            }
            else {
                printf("free - end memory overflow detected\n");
            }
        }
#endif
        if (pMemorySettings->flags & LKF_MEMORYSETTINGS_LIMIT) lkf_memory_limit_reached(pMemorySettings, pContext, "free", key, 1, (size_t) freed_size, 1);

        pMemorySettings->open_ptr -= 1;
        pMemorySettings->open_memory -= freed_size;
        free (np);
    }
    else {
        free (p);
    }
    
    if (pMemorySettings->memory_delegate && (pMemorySettings->delegateFlags & LKF_MEMORYSETTINGS_DELEGATE_ALL)) {
        pMemorySettings->memory_delegate (pContext, "free", key, (long long) p, 0, (long) freed_size);
    }
	memoryBlockCount--;
}
