/****************************************************************************
** Leistungsorientierte Krankenanstaltenfinanzierung (LKF)                 **
** Scoring-C-Sourcen V2026.1                                               **
** (C) 2026 BMASGPK                                                        **
*****************************************************************************
** stamm5.c - Einlesen und Abfragen von Stammdaten                         **
**                                                                         **
**		 Alex.Holzer@chello.at                                             **
**       Bernhard.Pesec@dothealth.at                                       **
***************************************************************************/


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

static DATAPROVIDER_MODEL_FILE_FUNC	_modelFileFunc = 0;

/* Data Provider */
_SCORING_EXPORT void lkf_set_dataprovider(DATAPROVIDER_MODEL_FILE_FUNC fileFunc) {
	_modelFileFunc = fileFunc;
}

static MEL_ARRAY_STRUC *FindStammMel (const STAMM2001 *pStamm, const char *sMel); /* V19.0 */

/* compare- & match-functions *******************************************************/

static int plz_compare (const void *p1, const void *p2)
{ /* Vergleichsfunktion für Postleitzahlenarray für qsort und bsearch
     ebenso für Krankenanstalten (ka_compare) */ 
	return memcmp (p1, p2, 4); 
}

#define ka_compare plz_compare
#define ktr_compare plz_compare /* V14.2 */

static int staat_compare (const void *s1, const void *s2)
{ /* Vergleichsfunktion für Staatenkennzeichen */
	return memcmp (s1, s2, 3); 
}

int spez_compare (const void *s1, const void *s2)
{ /* Vergleichsfunktion fuer Spezialitaeten */
    /* V6.0: Jede Abteilung kann nur einmal vorkommen; Sortierung nur noch nach Abteilung */
	return memcmp (((char *)s1) + 5, ((char *) s2) + 5, 12);
}

static int funk_compare (const void *f1, const void *f2)
{ /* Vergleichsfunktion fuer Funktionscodes */
	int i;
	i = memcmp (f1, f2, 6); 
	return i;
}

int kost_compare (const void *k1, const void *k2)
{ /* Vergleichsfunktion fuer Kostenstellen */
	return memcmp (((char *)k1) + 6, ((char *) k2) + 6, 2);
}

int kost2_compare (const void *k1, const void *k2)
{
	return memcmp ((* (char **) k1), (* (char **) k2), 3);
}

int mel_ka_compare_old (const void *m1, const void *m2)
{
    return memcmp (m1, m2, 9);
}

int mel_ka_compare2009 (const void *m1, const void *m2)
{
	return memcmp (m1, m2, 5 + LKF_AUFENTHALT_NEWMEL_LEN);
}

static int diag_compare6 (const void *d1, const void *d2)
{
	return memcmp (d1, d2, 6);
}

static int diag_compare5 (const void *d1, const void *d2)
{
	return memcmp (d1, d2, 5);
}

static int diag_compare3 (const void *d1, const void *d2)
{
	return memcmp (d1, d2, 3);
}

static int mel_mel_compare2009 (const void *m1, const void *m2)
{
	return memcmp (m1, m2, 10);
}

static int knoten_compare (const void *gruppe, const void *k1)
{
	const LKF_KNOTEN *pKnoten = * ((const LKF_KNOTEN**) k1);	
	return memcmp (gruppe, pKnoten->knoten, 8);
}

static int ggart_compare (const void *ga1, const void *ga2)
{
	return memcmp (ga1, ga2, sizeof (GGART_STRING));
}

static int ggleist_compare (const void *gl1, const void *gl2)
{
	return memcmp (gl1, gl2, sizeof (GGLEIST_STRING));
}

int oemelka_compare_2009 (const void *om1, const void *om2)
{
	return memcmp (om1, om2, 11);
}

static int oemelka_compare_ka (const void *om1, const void *om2)
{ /* V2020.2 */
    return memcmp (om1, om2, 6);
}

static int oekaso_compare (const void *ok1, const void *ok2)
{
	return memcmp (ok1, ok2, 6);
}

static int oekaso_compare4 (const void *ok1, const void *ok2)
{
	return memcmp (ok1, ok2, 4);
}

static int diag_diag_compare (const void *dd1, const void *dd2)
{
	return memcmp (dd1, dd2, 12);
}

static int diag_diag_compare2018 (const void *dd1, const void *dd2)
{ /* V22.2 */
    return memcmp (dd1, dd2, 14);
}

static int diag_mel_compare (const void *dm1, const void *dm2) 
{
	return memcmp (dm1, dm2, 11);
}

static int diag12_find (const void *cand, const void *patt)
{
	return memcmp (cand, patt, 12);
}

static int diag12_find_2017 (const void *cand, const void *patt)
{
    int res = memcmp (cand, patt, 6);
    if (!res)
        return memcmp ((const char *) cand + 7, (const char *) patt + 7, 6);
    return res;
}



static int diag6_find (const void *cand, const void *patt)
{
	return memcmp (cand, patt, 6);
}

static int gkz_compare (const void *g1, const void *g2)
{ /* V21.0 */
    return memcmp(g1, g2, 5);
}

static int plz_gemco_compare (const void *pg1, const void *pg2) {
    return memcmp (pg1, pg2, 9);
}

static int atc_compare (const void *atc1, const void *atc2)
{ /* V21.0 */
    return memcmp(atc1, atc2, 7);
}


GKZ_LIST_STRING *FindGKZ (const LKF_CONTEXT *pContext, const char *gemeindeziffer) {
    return (GKZ_LIST_STRING *) bsearch (gemeindeziffer, pContext->pStamm2001->gkz_list, pContext->pStamm2001->gkz_list_gesamt, sizeof (GKZ_LIST_STRING), gkz_compare);
}

int has_diagdiag (const LKF_CONTEXT *pContext, const char *diag)
{
	const DIAG_DIAG_STRING *base = (const DIAG_DIAG_STRING *) pContext->pStamm2001->diag_diag;
    if (base) {
        return bsearch (diag, base, pContext->pStamm2001->diag_diag_gesamt, sizeof (DIAG_DIAG_STRING), diag6_find) ? 1 : 0;
    }
    return 0;
}

const DIAG_DIAG_STRING *diag_diag_find (const LKF_CONTEXT *pContext, const char *diag, const char *otherDiag) 
{
	const DIAG_DIAG_STRING *base = (const DIAG_DIAG_STRING *) pContext->pStamm2001->diag_diag;
	if (base)
	{
		char diagbuf[14];
        if (pContext->modelljahr >= 2017) {
            sprintf (diagbuf, "%-6.6s %-6.6s", diag, otherDiag);
            return (const DIAG_DIAG_STRING *) bsearch (diagbuf, base, pContext->pStamm2001->diag_diag_gesamt, sizeof (DIAG_DIAG_STRING), diag12_find_2017);
        }
		sprintf (diagbuf, "%-6.6s%-6.6s", diag, otherDiag);
		return (const DIAG_DIAG_STRING *) bsearch (diagbuf, base, pContext->pStamm2001->diag_diag_gesamt, sizeof (DIAG_DIAG_STRING), diag12_find);
	}
	return 0;
}

const DIAG_MEL_STRING *diag_mel_next (const LKF_CONTEXT *pContext, const DIAG_MEL_STRING *diagMel)
{
	diagMel++;
	if (diagMel >= (const DIAG_MEL_STRING *) (pContext->pStamm2001->diag_mel + pContext->pStamm2001->diag_mel_gesamt))
		return 0;
	return (memcmp (diagMel, diagMel-1, 6) ? 0 : diagMel);
}


const DIAG_MEL_STRING *diag_mel_find (const LKF_CONTEXT *pContext, const char *diag)
{
	const DIAG_MEL_STRING *base = (const DIAG_MEL_STRING *) (pContext->pStamm2001->diag_mel);
	if (base) 
	{
		char diagbuf[8];
		const DIAG_MEL_STRING *pStr;
		sprintf (diagbuf, "%-6.6s", diag); /* V18.1 */
		pStr = (const DIAG_MEL_STRING *) bsearch (diagbuf, base, pContext->pStamm2001->diag_mel_gesamt, sizeof (DIAG_MEL_STRING), diag6_find);
	
		while (pStr && pStr >= base)
		{
			if (pStr == base || memcmp (pStr - 1, diagbuf, 6)) return pStr; /* V22.5 - Memory overflow */
			--pStr;
		}
		return pStr;
	
	}
	return 0;
}

ICD_LIST_STRING *FindDiag6 (const STAMM2001 *pStamm, const char *diag)
{
	return (ICD_LIST_STRING *) bsearch (diag, pStamm->icd_list, pStamm->icd_gesamt, sizeof (ICD_LIST_STRING), diag_compare6); 
}

ICD_LIST_STRING *FindDiag (const STAMM2001 *pStamm, const char *diag)
{ /* V13.3: Sucht die exakte Diagnose, wenn nicht vorhanden, den 5Steller, sonst den 3Steller */
	ICD_LIST_STRING *pICD = FindDiag6 (pStamm, diag);
	if (!pICD && diag[5] != ' ')
	{
		pICD = FindDiag5 (pStamm, diag);
		if (!pICD && diag[4] == 'x')
			pICD = FindDiag3 (pStamm, diag);
	}
	return pICD;
}

int HasOeMelka (LKF_CONTEXT *pContext, const char *sKANR6)
{ /* V2020.2 */
	const OEMELKA_STRING *found;
    
    if (pContext->oemelka && pContext->oemelka_gesamt > 0) {
        found= (const OEMELKA_STRING *) bsearch (sKANR6, pContext->oemelka, pContext->oemelka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_ka);
    }
    else {
        const STAMM2001 *pStamm = pContext->pStamm2001;
        if (!pStamm->oe_mel_ka || !pStamm->oe_mel_ka_gesamt) return 0;
        found= (const OEMELKA_STRING *) bsearch (sKANR6, pStamm->oe_mel_ka, pStamm->oe_mel_ka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_ka);
    }
    
    return found ? 1 : 0;
}

const OEMELKA_STRING *FindOeMelka (LKF_CONTEXT *pContext, const char *sKANR6, const char *sMel, unsigned int check6_flagpos)
{
	char sPattern[12];
    const OEMELKA_STRING *found;
    OEMELKA_STRING *oe_mel_ka;
    unsigned int oe_mel_ka_gesamt;
    
    sprintf (sPattern, "%-6.6s%-5.5s", sKANR6, sMel);

    if (pContext->oemelka && pContext->oemelka_gesamt > 0) {
        oe_mel_ka = pContext->oemelka;
        oe_mel_ka_gesamt = pContext->oemelka_gesamt;
    }
    else {
        const STAMM2001 *pStamm = pContext->pStamm2001;
        if (!pStamm->oe_mel_ka || !pStamm->oe_mel_ka_gesamt) return 0;
        oe_mel_ka = pStamm->oe_mel_ka;
        oe_mel_ka_gesamt = pStamm->oe_mel_ka_gesamt;
    }
        
    found = (const OEMELKA_STRING *) bsearch (sPattern, oe_mel_ka, oe_mel_ka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_2009);

    if (!found && check6_flagpos && sKANR6[4] != '.') {
        int standort = 1;
        if (pContext->modelljahr >= 2021) {
            for (; standort < 10; standort++) {
                const OEMELKA_STRING *cand;
                sprintf (sPattern, "%-4.4s.%1d%-5.5s", sKANR6, standort, sMel);
                cand = (const OEMELKA_STRING *) bsearch (sPattern,  oe_mel_ka,  oe_mel_ka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_2009);
                if (!cand) {
                    continue;
                }
                if ((*cand)[check6_flagpos] == 'J')
                    return cand;
                if (!found) {
                    found = cand;
                    continue;
                }
                if ((*found)[check6_flagpos] == 'N') {
                    found = cand;
                }
            }
        }
        else {
            while (1) {
                const OEMELKA_STRING *cand;
                sprintf (sPattern, "%-4.4s.%1d%-5.5s", sKANR6, standort, sMel);
                cand = (const OEMELKA_STRING *) bsearch (sPattern,  oe_mel_ka,  oe_mel_ka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_2009);
                if (!cand) {
                    return found;
                }
                ++standort;
                if ((*cand)[check6_flagpos] == 'J')
                    return cand;
                if (!found) {
                    found = cand;
                    continue;
                }
                if ((*found)[check6_flagpos] == 'N') {
                    found = cand;
                }
            }
        }
    }
    
    return found;
}

const OEKASO_STRING *FindOEKASO4 (const STAMM2001 *pStamm, const char *ka)
{
	return (const OEKASO_STRING *) bsearch (ka, pStamm->oekaso, pStamm->oekaso_gesamt, sizeof (OEKASO_STRING), oekaso_compare4);
}

const OEKASO_STRING *FindOEKASO6 (const STAMM2001 *pStamm, const char *ka)
{
	return (const OEKASO_STRING *) bsearch (ka, pStamm->oekaso, pStamm->oekaso_gesamt, sizeof (OEKASO_STRING), oekaso_compare);
}

ICD_LIST_STRING *FindDiag5 (const STAMM2001 *pStamm, const char *diag)
{
	ICD_LIST_STRING *found = (ICD_LIST_STRING *) bsearch (diag, pStamm->icd_list, pStamm->icd_gesamt, sizeof (ICD_LIST_STRING), diag_compare5); 
	while (pStamm->icd_list < found && !memcmp (found - 1, found, 5)) {
		--found;
	}
	return found;
}

ICD_LIST_STRING *FindDiag3 (const STAMM2001 *pStamm, const char *diag)
{
	ICD_LIST_STRING *found = (ICD_LIST_STRING *) bsearch (diag, pStamm->icd_list, pStamm->icd_gesamt, sizeof (ICD_LIST_STRING), diag_compare3); 
	while (pStamm->icd_list < found && !memcmp (found - 1, found, 3)) {
		--found;
	}
	return found;

}

static STERNKR_LIST_STRING *FindFirstSternKr (STERNKR_LIST_STRING *pFirst, STERNKR_LIST_STRING *pAct)
{
	STERNKR_LIST_STRING *pCur;
	
	if (!pAct || !pFirst) return 0;
	pCur = pAct - 1;

	while (pCur >= pFirst && !memcmp (*pCur, *pAct, 5)) --pCur;
	return pCur + 1;
}

STERNKR_LIST_STRING *FindSternKr3 (const STAMM2001 *pStamm, const char *diag)
{
	STERNKR_LIST_STRING *pSternKr;
	pSternKr = bsearch (diag, pStamm->sternkr_list, pStamm->sternkr_gesamt, sizeof (STERNKR_LIST_STRING), diag_compare3);

	if (pSternKr) return FindFirstSternKr (pStamm->sternkr_list, pSternKr);
	else return 0;
}

STERNKR_LIST_STRING *FindSternKr5 (const STAMM2001 *pStamm, const char *diag)
{
	STERNKR_LIST_STRING *pSternKr;
	pSternKr = (STERNKR_LIST_STRING *) bsearch (diag, pStamm->sternkr_list, pStamm->sternkr_gesamt, sizeof (STERNKR_LIST_STRING), diag_compare5);
	
	if (pSternKr) return FindFirstSternKr (pStamm->sternkr_list, pSternKr);
	else return 0;
}

/************************************************************************************************************
** Array-Abfrage-Funktionen                                                                                **
************************************************************************************************************/

const LKF_KNOTEN *LKFKnotenInArray (STAMM2001 *pStamm, const char *gruppe)
{

	const LKF_KNOTEN **ppKnoten;

	ppKnoten = (const LKF_KNOTEN **) bsearch (gruppe, pStamm->ppKnoten, pStamm->knoten_gesamt, sizeof (LKF_KNOTEN *), knoten_compare);

	if (ppKnoten) return *ppKnoten;
	else return 0;

}

PLZ_LIST_STRING *PLZInArray (const LKF_CONTEXT *pContext, const char *plz)
{
	return (PLZ_LIST_STRING *) bsearch ((plz), pContext->pStamm2001->plz_list, pContext->pStamm2001->plz_gesamt, sizeof (PLZ_LIST_STRING), plz_compare);
}

KA_LIST_STRING *KAInArray (const STAMM2001 *pStamm, const char *ka)
{
	return KAInArray2(pStamm, ka, 0);
}

KA_LIST_STRING *KAInArray2 (const STAMM2001 *pStamm, const char *ka, int nQuelle)
{
	KA_LIST_STRING *pKAString;
	char local_ka[8];

	/* V18.0 */
	if (nQuelle == 4 && ka[0] != 'P')
		return 0;
	else if (nQuelle != 4 && ka[0] == 'P')
		return 0;

	sprintf (local_ka, "%-6.6s", ka);
	
	if (nQuelle != 2)
	{
		pKAString = (KA_LIST_STRING *) bsearch (local_ka, pStamm->ka_list, pStamm->ka_gesamt, sizeof (KA_LIST_STRING), ka_compare);
		if (pKAString) return pKAString;
	}

	if (nQuelle != 1 && nQuelle != 4)
	{
		pKAString = (KA_LIST_STRING *) bsearch (local_ka, pStamm->ka_list_alternate, pStamm->ka_alternate_gesamt, sizeof (KA_LIST_STRING), ka_compare);
		if (pKAString) return pKAString;
	}

	return 0;

}

KTR_LIST_STRING *KTRInArray (const STAMM2001 *pStamm, const char *ktr)
{
	if (!pStamm->ktr_list)
		return 0;
	return (KTR_LIST_STRING *) bsearch (ktr, pStamm->ktr_list, pStamm->ktr_gesamt, sizeof(KTR_LIST_STRING), ktr_compare);
}

FUNK_LIST_STRING *FunkInArray (const STAMM2001 *pStamm, const char *abtfc)
{
	return (FUNK_LIST_STRING *) bsearch (abtfc, pStamm->funk_list, pStamm->funk_gesamt, sizeof (FUNK_LIST_STRING), funk_compare);
}

ATCLIST_STRING *FindATC (const STAMM2001 *pStamm, const char *atc)
{ /* V2026.0 */
    if (pStamm->atc_list) {
        ATCLIST_STRING *pATC = (ATCLIST_STRING *) bsearch (atc, pStamm->atc_list, pStamm->atc_list_gesamt, sizeof (ATCLIST_STRING), atc_compare);
        return pATC;
    }
    return 0;
}

const SPEZ_LIST_STRING *SpezInArrayStamm (const STAMM2001 *pStamm, const char *spez)
{
	return (const SPEZ_LIST_STRING *) bsearch ((spez), pStamm->spez_list, pStamm->spez_gesamt, sizeof (SPEZ_LIST_STRING), spez_compare);
}

unsigned int SpezAnzStamm (const STAMM2001 *pStamm)
{
	return pStamm->spez_gesamt;
}

KOST_LIST_STRING *KostInArrayStamm (const STAMM2001 *pStamm, const char *kost)
{
	return (KOST_LIST_STRING *) bsearch (kost, pStamm->kost_list, pStamm->kost_gesamt, sizeof (KOST_LIST_STRING), kost_compare);
}

void *KostInArray2Stamm (const STAMM2001 *pStamm, const char *kost)
{
	return bsearch (&kost, pStamm->kost_list2, pStamm->kost_gesamt, sizeof (KOST_LIST_STRING *), kost2_compare);
}

MEL_ARRAY_STRUC *GetMELArrayString (const STAMM2001 *pStamm, unsigned int i)
{
	return pStamm->mel_array + i;
}

MEL_ARRAY_STRUC *GetMELArrayFromIndex (const LKF_CONTEXT *pContext, unsigned int nMelIndex)
{ /* V13.0 */
    return pContext->pStamm2001->mel_array + nMelIndex;
}

MEL_DIAG_STRING *GetMELDiag (const STAMM2001 *pStamm, unsigned int i)
{
	if (pStamm->mel_diag && i < pStamm->mel_diag_gesamt) return pStamm->mel_diag + i;
	else return 0;
}

MEL_DIAG_STRUC6 *GetMELDiag6 (const STAMM2001 *pStamm, unsigned int i)
{
	if (pStamm->mel_diag6 && i < pStamm->mel_diag_gesamt) return pStamm->mel_diag6 + i;
	else return 0;
}

MEL_KA_STRING *GetMELKAStamm2009 (const STAMM2001 *pStamm, const char *melka)
{
	return (MEL_KA_STRING *) bsearch (melka, pStamm->mel_ka, pStamm->mel_ka_gesamt, sizeof (MEL_KA_STRING), mel_ka_compare2009);
}

MEL_KA_STRING*GetMELKATKL2009 (const STAMM2001 *pStamm, const char *melka)
{
	return (MEL_KA_STRING *) bsearch (melka, pStamm->mel_tkl, pStamm->mel_tkl_gesamt, sizeof (MEL_KA_STRING), mel_ka_compare2009);

}

MEL_MEL_STRING *GetMEL_MEL2009 (const STAMM2001 *pStamm, const char *mel1, const char *mel2)
{
	char buf[12];
	sprintf (buf, "%5.5s%5.5s", mel1, mel2);
    if (pStamm->mel_mel) {
        return (MEL_MEL_STRING *) bsearch (buf, pStamm->mel_mel, pStamm->mel_mel_gesamt, sizeof (MEL_MEL_STRING), mel_mel_compare2009);
    }
    return 0;
}


GGART_STRING *FindGGArt (const LKF_CONTEXT *pContext, const char *sGGArt)
{
	if (!pContext->pStamm2001->lGGArtAnz) return 0;
	return bsearch (sGGArt, pContext->pStamm2001->sGGArt, pContext->pStamm2001->lGGArtAnz, sizeof (GGART_STRING), ggart_compare);
}

GGLEIST_STRING *FindGGLeist (const LKF_CONTEXT *pContext, const char *sGGLeist)
{
	if (!pContext->pStamm2001->lGGLeistAnz) return 0;
	return bsearch (sGGLeist, pContext->pStamm2001->sGGLeist, pContext->pStamm2001->lGGLeistAnz, sizeof (GGART_STRING), ggleist_compare);
}

static void combine_path(char *path, const char *srcpath, const char*sub)
{

	int len = (int)strlen(srcpath);
	strcpy(path, srcpath);

#if defined (UNIX)
	if (srcpath[len - 1] != '/') {
		strcat(path, "/");
		len++;
	}
	sprintf(path + len, "%s/", sub);
#elif defined (VMS)

	if (srcpath[len - 1] == ']') {
		if (srcpath[len - 2] == '.') --len;
		sprintf(path + len - 1, ".%s]", sub);
	}
	else if (srcpath[len - 1] == '>') {
		if (srcpath[len - 2] == '.') --len;
		sprintf(path + len - 1, ".%s>", sub);
	}
	else sprintf(path + len, "[.%s]", sub);
#elif defined (__APPLE__)
	if (srcpath[len - 1] != '/') {
		strcat(path, "/");
		len++;
	}
	sprintf(path + len, "%s/", sub);
#else /* WIN32 */
	/* #elif defined (WIN32) */
	if (srcpath[len - 1] != '\\' && srcpath[len - 1] != '/') {
		strcat(path, "\\");
		len++;
	}
	sprintf(path + len, "%s\\", sub);
#endif
}

#ifdef SUPPORT_PRE2017
static STAMM2001 *pStamm2009Global = 0;
static int stamm2009_status = 0;

static STAMM2001 *pStamm2010Global = 0;
static int stamm2010_status = 0;

static STAMM2001 *pStamm2011Global = 0;
static int stamm2011_status = 0;

static STAMM2001 *pStamm2012Global = 0;
static int stamm2012_status = 0;

static STAMM2001 *pStamm2013Global = 0;
static int stamm2013_status = 0;

static STAMM2001 *pStamm2014Global = 0; /* V18.0 */
static int stamm2014_status = 0;  /* V18.0 */

static STAMM2001 *pStamm2015Global = 0; /* V19.0 */
static int stamm2015_status = 0;  /* V19.0 */

static STAMM2001 *pStamm2016Global = 0; /* V20.0 */
static int stamm2016_status = 0;  /* V20.0 */
#endif

static STAMM2001 *pStamm2017Global = 0; /* V21.0 */
static int stamm2017_status = 0;  /* V21.0 */

static STAMM2001 *pStamm2018Global = 0; /* V22.0 */
static int stamm2018_status = 0;  /* V22.0 */

static STAMM2001 *pStamm2019Global = 0; /* V2019.0 */
static int stamm2019_status = 0;  /* V2019.0 */

static STAMM2001 *pStamm2020Global = 0; /* V2020.0 */
static int stamm2020_status = 0;  /* V2020.0 */

static STAMM2001 *pStamm2021Global = 0; /* V2021.0 */
static int stamm2021_status = 0;  /* V2021.0 */

static STAMM2001 *pStamm2022Global = 0; /* V2022.0 */
static int stamm2022_status = 0;  /* V2022.0 */

static STAMM2001 *pStamm2023Global = 0; /* V2023.0 */
static int stamm2023_status = 0;  /* V2023.0 */

static STAMM2001 *pStamm2024Global = 0; /* V2024.0 */
static int stamm2024_status = 0;  /* V2024.0 */

static STAMM2001 *pStamm2025Global = 0; /* V2025.0 */
static int stamm2025_status = 0;  /* V2025.0 */

static STAMM2001 *pStamm2026Global = 0; /* V2026.0 */
static int stamm2026_status = 0;  /* V2026.0 */

/** function lkf_read_stamm **************************************************
**    Liest die Stammdaten aus einer Datei ein.                             **
*****************************************************************************/

#define MIN_ALLOC_ANZ 100 /* V14.0: 1000 -> 100 */
#define ALLOC_ADD_ANZAHL 100

char *lkf_realloc_stamm_array (LKF_CONTEXT *pContext, unsigned int nSize, unsigned int nAllocCount, char *sOld, unsigned int nCurrentCount, const char *alloc_text)
{
	char *sAllocArray;
	
	if (nAllocCount < MIN_ALLOC_ANZ) return sOld;

	sAllocArray = LKF_CALLOC (pContext, alloc_text, (size_t) (nAllocCount+ALLOC_ADD_ANZAHL), (size_t) nSize);
    if (!sAllocArray) {
        if (sOld) LKF_FREE (pContext, alloc_text, sOld);
        return 0;
    }
	if (sOld)
	{
		if (nCurrentCount)
			memcpy (sAllocArray, sOld, nCurrentCount * nSize);
		LKF_FREE (pContext, alloc_text, sOld);
	}

	return sAllocArray;
}

char *lkf_alloc_stamm_array (LKF_CONTEXT *pContext, unsigned int nSize, unsigned int nAllocSize, const char *alloc_text)
{
	if (nAllocSize < MIN_ALLOC_ANZ) nAllocSize = MIN_ALLOC_ANZ;
	return LKF_CALLOC (pContext, "alloc_text", (size_t) (nAllocSize+ALLOC_ADD_ANZAHL), (size_t) nSize);
}

#define MAX_BUF_LEN 1024

STAMM_FILE *lkf_stamm_open (STAMM2001 *pStamm, const char *filename, const char *alternateName)
{
    const char *explicitPath;
    char filebuf[MAX_BUF_LEN];
    STAMM_FILE *pStammFile = (STAMM_FILE *) LKF_CALLOC(pStamm->pScoringContext, "lkf_stamm_open", 1, sizeof (STAMM_FILE));
    if (!pStammFile) return 0;
 
	if (_modelFileFunc) {
#define PROVIDER_MAX_BUF 1000000
        int res;
        pStammFile->fileContent = (char*)LKF_MALLOC(pStamm->pScoringContext, "lkf_stamm_open.fileContent", PROVIDER_MAX_BUF);
        if (!pStammFile->fileContent) {
            LKF_FREE(pStamm->pScoringContext, "lkf_stamm_open", pStammFile);
            return 0;
        }
		res = _modelFileFunc(pStamm->nModellJahr, filename, PROVIDER_MAX_BUF, pStammFile->fileContent);

		if (res > 0) {
            if (res == 1) {
                pStammFile->fileContentLen = 0;
                return pStammFile;
            }
            else if (res >= PROVIDER_MAX_BUF) {
                LKF_FREE(pStamm->pScoringContext, "lkf_stamm_open", pStammFile->fileContent);
                pStammFile->fileContent = (char*)LKF_MALLOC(pStamm->pScoringContext, "lkf_stamm_open.fileContent", res + 2);
                if (!pStammFile->fileContent) {
                    LKF_FREE(pStamm->pScoringContext, "lkf_stamm_open", pStammFile);
                    return 0;
                }
                res = _modelFileFunc(pStamm->nModellJahr, filename, res + 2, pStammFile->fileContent);
            }
            pStammFile->fileContentLen = res;
            return pStammFile;
        }
 		else if (res < 0) {
			int pos;
			char errbuf[2048];
			pos = sprintf(errbuf, "Provider-Fehler bei File '%s': ", filename);
			strncpy(errbuf + pos, pStammFile->fileContent, 1024 - pos);
			strncpy(pStammFile->error, errbuf, 1024);
			pStammFile->error[1023] = 0;
            LKF_FREE(pStamm->pScoringContext, "lkf_stamm_open", pStammFile->fileContent);
            pStammFile->fileContent = 0;
            return pStammFile;
        }
        LKF_FREE(pStamm->pScoringContext, "lkf_stamm_open", pStammFile->fileContent);
        pStammFile->fileContent = 0;
    }

    explicitPath = pStamm->stammFilePathFunc ? pStamm->stammFilePathFunc(pStamm->pScoringContext, filename) : 0;
    if (explicitPath) {
        strcpy (filebuf, explicitPath);
    }
    else {
        sprintf (filebuf, "%s%s", pStamm->input_path, filename);
    }
    
    pStammFile->fp = fopen (filebuf, "r");
    if (!pStammFile->fp && errno == 2) {
        if (alternateName) {
            sprintf (filebuf, "%s%s", pStamm->input_path, alternateName);
            pStammFile->fp = fopen (filebuf, "r");
        }
        else {
            int i = 0;
            char lower_filename[28];
            for (i = 0; filename[i]; i++)
                lower_filename[i] = (char) tolower(filename[i]);
            lower_filename[i] = 0;
            sprintf (filebuf, "%s%s", pStamm->input_path, lower_filename);
            pStammFile->fp = fopen (filebuf, "r");
        }

        if (!pStammFile->fp && errno == 2) {

            char alternateBuf[1024];

            *alternateBuf = 0;
            if (!memcmp(filename, "HDG", 3)) {
                if (pStamm->nModellJahr >= 2017)
                    combine_path(alternateBuf, pStamm->input_path, "HDG");
                else
                    combine_path(alternateBuf, pStamm->input_path, "HDG-Gruppen");
            }
            else if (!memcmp(filename, "MEL", 3)) {
                if (pStamm->nModellJahr >= 2017)
                    combine_path(alternateBuf, pStamm->input_path, "MEL");
                else
                    combine_path(alternateBuf, pStamm->input_path, "MEL-Gruppen");
            }
            else if (!memcmp(filename, "AMG", 3)) {
                combine_path(alternateBuf, pStamm->input_path, "AMG");
            }
            else if (!memcmp(filename, "APG", 3)) {
                combine_path(alternateBuf, pStamm->input_path, "APG");
            }
            if (*alternateBuf) {
                sprintf(filebuf, "%s%s", alternateBuf, filename);
                pStammFile->fp = fopen(filebuf, "r");
            }
        }
    }
    
    if (pStammFile->fp == 0) {
        char errbuf[2048];
        sprintf (errbuf, "Stammdaten-Datei '%s' konnte nicht geoeffnet werden: %s", filebuf, strerror (errno));
        strncpy(pStammFile->error, errbuf, 1024);
        pStammFile->error[1023] = 0;
    }
    else {
        pStammFile->error[0] = 0;
    }
    pStammFile->pStamm = pStamm;
    strcpy (pStammFile->fileName, filename);
    return pStammFile;
}

void lkf_stamm_close (STAMM_FILE *file) {
    
    LKF_CONTEXT *pContext;
    
    if (file->fp) {
        fclose (file->fp);
    }
    
    pContext = file->pStamm ? file->pStamm->pScoringContext : 0;
    
    LKF_FREE (pContext, "lkf_stamm_close", file);
}

const char *lkf_stamm_line (char *buf, int buflen, STAMM_FILE *file) {
    
    const char *l;

    if (!file)
        return 0;
    
    if (file->fileContent) {
        char* currentLine = file->fileContent + file->fileContentPos;
        int lineLen;
        while (file->fileContentPos < file->fileContentLen) {
            char* line = strchr(currentLine, 10);
            if (!line) {
                lineLen = (int) strlen(currentLine);
                if (buflen < lineLen + 1) lineLen = buflen - 1;
                memcpy(buf, currentLine, lineLen);
                buf[lineLen] = 0;
                file->fileContentPos = file->fileContentLen;
                return buf;
            }
            if (line == currentLine) {
                currentLine++;
                file->fileContentPos++;
                continue;
            }
            lineLen = (int) (line - currentLine);
            file->fileContentPos += lineLen + 1;

            while (lineLen > 0 && currentLine[lineLen - 1] == 13) --lineLen;
            if (lineLen == 0) {
                continue;
            }
            memcpy(buf, currentLine, lineLen);
            buf[lineLen] = 0;
            return buf;
        }
        return 0;
    }

    if (!file->add) {
        
        l = fgets (buf, buflen, file->fp);
        if (l || !file->pStamm->stammAddLineFunc)
            return l;
        file->add = 1;
        l = file->pStamm->stammAddLineFunc(file->pStamm->pScoringContext, file->fileName);
        if (l != 0) {
            strncpy (buf, l, buflen);
            return buf;
        }
        return 0;
    }
    l = file->pStamm->stammAddLineFunc(file->pStamm->pScoringContext, 0);
    if (l != 0) {
        strncpy (buf, l, buflen);
        return buf;
    }
    return 0;
}


static int lkf_read_stamm (STAMM2001 *pStamm, const char *filename, char **array, unsigned int single_len, unsigned int *count, unsigned int nExtend, char *errbuf)
{
	STAMM_FILE *fp;
	char buf[MAX_BUF_LEN];
	int len;
	unsigned int nAllocSize;

	fp = lkf_stamm_open (pStamm, filename, 0);
    
	if (!fp->fp && !fp->fileContent) {
		*array = 0;
		*count = 0;
        strcpy (errbuf, fp->error);
        lkf_stamm_close(fp);
		return 1;
	}

	*errbuf = 0;
	*count = 0;

	nAllocSize = 10000;

	*array = lkf_alloc_stamm_array (pStamm->pScoringContext, single_len, nAllocSize, "alloc: lkf_read_stamm");
    if (!*array) return LKF_OUTOFMEMORY_ERROR;
    
	while (lkf_stamm_line (buf, MAX_BUF_LEN-1, fp)) {
		buf[MAX_BUF_LEN-1] = 0;
        len = (int)strlen(buf);
        while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) buf[--len] = 0;
		
		if (len > 0) {
		
			memset (buf + len, ' ', MAX_BUF_LEN - len);

			if (*count == nAllocSize) {
				nAllocSize *= 2;
				*array = lkf_realloc_stamm_array (pStamm->pScoringContext, single_len, nAllocSize, *array, *count, "realloc: lkf_read_stamm");
                if (!*array) {
                    *count = 0;
                    return LKF_OUTOFMEMORY_ERROR;
                }
			}
			else memcpy ((*array) + (single_len * (*count)++), buf, single_len);
		}
	}
	
	lkf_stamm_close(fp);

	*array = lkf_realloc_stamm_array (pStamm->pScoringContext, single_len, *count + nExtend, *array, *count, "realloc: lkf_read_stamm");
    if (!*array) {
        *count = 0;
        return LKF_OUTOFMEMORY_ERROR;
    }
	return 0;
}


/********************************************************************************************************
** Funktion lkf_read_diaglist: Liest die Stammdaten-Datei diaglist.dat ein                             **
********************************************************************************************************/

static int lkf_read_diaglist (STAMM2001 *pStamm2001, char **array, unsigned int *count, char *errbuf)
{
#define MAX_BUF_LEN 1024
	STAMM_FILE *fp;
	char buf[MAX_BUF_LEN];
	int len;
	int startpos;
	unsigned int nAllocSize = 1000;

    fp = lkf_stamm_open (pStamm2001, "DIAGLIST.dat", "diaglist.dat");
    
	if (!fp->fp && !fp->fileContent) {
		strcpy (errbuf, fp->error);
        lkf_stamm_close(fp);
		return 1;
	}

	*errbuf = 0;
	*count = 0;

	*array = lkf_alloc_stamm_array (pStamm2001->pScoringContext, sizeof (ICD_LIST_STRING), nAllocSize, "alloc: lkf_read_diaglist");

	while (lkf_stamm_line(buf, MAX_BUF_LEN-1, fp)) {
		buf[MAX_BUF_LEN-1] = 0;
		len = (int) strlen (buf);
		if (buf[len-1] == '\n') buf[--len] = 0;
		
		if (len > 0) {
		
			memset (buf + len, ' ', MAX_BUF_LEN - len);

			if (*count == nAllocSize) {
				nAllocSize *= 2;
				*array = lkf_realloc_stamm_array (pStamm2001->pScoringContext,sizeof (ICD_LIST_STRING), nAllocSize, *array, *count, "realloc: lkf_read_diaglist");
                if (!*array) {
                    strcpy (errbuf, "lkf_read_diaglist: OUTOFMEMORY");
                    return LKF_OUTOFMEMORY_ERROR;
                }
			}
			startpos = (int) (sizeof(ICD_LIST_STRING) * (*count)++);
			memcpy ((*array) + startpos, buf, 46);
			(*array)[startpos + 46] = buf[247];
            (*array)[startpos + 47] = buf[350]; /* V21.0: stationaer erworben */
            memcpy ((*array) + startpos + 50, buf + 46, 80); /* Text */
		}
	}
	
	lkf_stamm_close(fp);

    
    fp = lkf_stamm_open (pStamm2001, "ORPHALIST.dat", "orphalist.dat");
    
    if (fp->fp || fp->fileContent) {
        while (lkf_stamm_line(buf, MAX_BUF_LEN-1, fp)) {
            buf[MAX_BUF_LEN-1] = 0;
            len = (int) strlen (buf);
            if (buf[len-1] == '\n') buf[--len] = 0;
            
            if (len > 0) {
                
                memset (buf + len, ' ', MAX_BUF_LEN - len);
                
                if (*count == nAllocSize) {
                    nAllocSize *= 2;
                    *array = lkf_realloc_stamm_array (pStamm2001->pScoringContext,sizeof (ICD_LIST_STRING), nAllocSize, *array, *count, "realloc: lkf_read_diaglist(orphalist)");
                    if (!*array) {
                        strcpy (errbuf, "lkf_read_diaglist: OUTOFMEMORY");
                        return LKF_OUTOFMEMORY_ERROR;
                    }
                }
                startpos = (int) (sizeof(ICD_LIST_STRING) * (*count)++);
                memcpy ((*array) + startpos, buf, 46);
                (*array)[startpos + 46] = buf[247];
                (*array)[startpos + 47] = buf[350]; /* V21.0: stationaer erworben */
                memcpy ((*array) + startpos + 50, buf + 46, 80); /* Text */
            }
        }
    }
    lkf_stamm_close(fp);
	*array = lkf_realloc_stamm_array (pStamm2001->pScoringContext,sizeof (ICD_LIST_STRING), *count, *array, *count, "realloc: lkf_read_diaglist");
	return 0;
}


/********************************************************************************************************
** Funktion lkf_read_meldiag6: Liest die meldiag-Einträge ein                                          **
********************************************************************************************************/

static int lkf_compare_meldiag6 (const void *m1, const void *m2)
{
	const MEL_DIAG_STRUC6 *s1 = (const MEL_DIAG_STRUC6 *) m1;
	const MEL_DIAG_STRUC6 *s2 = (const MEL_DIAG_STRUC6 *) m2;

	if (s1->mel_min != s2->mel_min) return s1->mel_min - s2->mel_min;
	if (s1->mel_max != s2->mel_max) return s1->mel_max - s2->mel_max;
	return memcmp (s1, s2, sizeof (MEL_DIAG_STRUC6));
}


static int lkf_read_meldiag6 (STAMM2001 *pStamm, char *errbuf, int nModellJahr)
{
#define MAX_BUF_LEN 1024
	STAMM_FILE *fp;
	char buf[MAX_BUF_LEN];
	int len;
	unsigned int nAllocSize = 1000;

	MEL_DIAG_STRUC6 *pMELDiagStruc;
	int startmel, endmel;
	unsigned int i;
	int j;

    fp = lkf_stamm_open(pStamm, "MELDIAG.dat", "meldiag.dat");
    
	if (!fp->fp && !fp->fileContent) {
        strcpy (errbuf, fp->error);
		return 1;
	}

	*errbuf = 0;
	pStamm->mel_diag_gesamt = 0;
	pStamm->mel_diag6 = (MEL_DIAG_STRUC6 *) lkf_alloc_stamm_array (pStamm->pScoringContext, sizeof (MEL_DIAG_STRUC6), nAllocSize, "alloc: lkf_read_meldiag6");
    if (!pStamm->mel_diag6) {
        return LKF_OUTOFMEMORY_ERROR;
    }
	while (lkf_stamm_line (buf, MAX_BUF_LEN-1, fp)) {
		buf[MAX_BUF_LEN-1] = 0;
		len = (int) strlen (buf);
		if (buf[len-1] == '\n') buf[--len] = 0;
		if (len < 24) memset (buf + len, ' ', 24 - len);

        startmel = FindMelIndexStamm (pStamm, buf);
        endmel = FindMelIndexStamm (pStamm, buf + 5);

        if (startmel < 0 && memcmp (buf, "ZN400", 5))
            startmel = FindMelIndexStammWeak (pStamm, buf, 0);

        if (startmel >= 0 && endmel < 0 && memcmp (buf + 5, "ZN400", 5))
            endmel = FindMelIndexStammWeak (pStamm, buf+5, 1);

		if (startmel < 0 || endmel < 0)
			continue;

		if (pStamm->mel_diag_gesamt == nAllocSize) 
		{ /* V11.0: dynamische Erweiterung */
			nAllocSize *= 2;
			pStamm->mel_diag6 = (MEL_DIAG_STRUC6 *) lkf_realloc_stamm_array (pStamm->pScoringContext, sizeof (MEL_DIAG_STRUC6), nAllocSize, (char *) pStamm->mel_diag6, pStamm->mel_diag_gesamt, "realloc: lkf_read_meldiag6");
            if (!pStamm->mel_diag6) {
                return LKF_OUTOFMEMORY_ERROR;
            }
		}
		pMELDiagStruc = pStamm->mel_diag6 + pStamm->mel_diag_gesamt++;
		
		pMELDiagStruc->mel_min = startmel;
		pMELDiagStruc->mel_max = endmel;
		memcpy (pMELDiagStruc->hdg_min, buf + 10, 3);
		memcpy (pMELDiagStruc->hdg_max, buf + 14, 3);
		pMELDiagStruc->hdg4_min = buf[18];
		pMELDiagStruc->hdg4_max = buf[20];
	}
	lkf_stamm_close (fp);

	pStamm->mel_diag6 = (MEL_DIAG_STRUC6 *) lkf_realloc_stamm_array (pStamm->pScoringContext, sizeof (MEL_DIAG_STRUC6), pStamm->mel_diag_gesamt, (char *) pStamm->mel_diag6, pStamm->mel_diag_gesamt, "realloc: lkf_read_meldiag6");

	qsort (pStamm->mel_diag6, pStamm->mel_diag_gesamt, sizeof (MEL_DIAG_STRUC6), lkf_compare_meldiag6);
	
    for (i = 0; i < pStamm->mel_list_gesamt; i++) pStamm->mel_array[i].mel_diag_ug = 0;
	
	for (i = 0; i < pStamm->mel_diag_gesamt; i++) 
	{
		pMELDiagStruc = pStamm->mel_diag6 + i;
		for (j = pMELDiagStruc->mel_min; j <= pMELDiagStruc->mel_max; j++) {

			if (!pStamm->mel_array[j].mel_diag_ug) pStamm->mel_array[j].mel_diag_ug = i + 1;
			pStamm->mel_array[j].mel_diag_og = i;
		}
	}

	return 0;
}

/********************************************************************************************************
** Funktion lkf_read_meldiag: List die meldiag-Einträge ein (nur für LKF2001)                          **
********************************************************************************************************/

/********************************************************************************************************
** Funktion lkf_read_mel: List die Einzelleistungs-Einträge ein.                                       **
********************************************************************************************************/
static int compare_melarray (const void *m1, const void *m2);

static int lkf_read_mel (STAMM2001 *pStamm, char *errbuf, int nModellJahr)
{
#define MAX_BUF_LEN 1024
	STAMM_FILE *fp;
	char buf[MAX_BUF_LEN];
	int len, mel, val;
	MEL_ARRAY_STRUC *pMel;

	unsigned int nMelAlloc = 2000;

    pStamm->mel_array = (MEL_ARRAY_STRUC *) lkf_alloc_stamm_array (pStamm->pScoringContext, sizeof(MEL_ARRAY_STRUC), nMelAlloc, "alloc: lkf_read_mel");

    fp = lkf_stamm_open(pStamm, "MELLIST.dat", "mellist.dat");
    
	if (!fp->fp && !fp->fileContent) {
        strcpy (errbuf, fp->error);
        lkf_stamm_close(fp);
		return 1;
	}

	*errbuf = 0;
	pStamm->mel_list_gesamt = 0;

	while (lkf_stamm_line(buf, MAX_BUF_LEN-1, fp)) {
		buf[MAX_BUF_LEN-1] = 0;
		len = (int) strlen (buf);
		if (buf[len-1] == '\n') buf[--len] = 0;
		memset (buf + len, ' ', MAX_BUF_LEN - len);

        if ((unsigned int)pStamm->mel_list_gesamt == nMelAlloc)
        {
            pStamm->mel_array = (MEL_ARRAY_STRUC *) lkf_realloc_stamm_array (pStamm->pScoringContext, sizeof(MEL_ARRAY_STRUC), nMelAlloc+1000, (char *) pStamm->mel_array, nMelAlloc, "realloc: lkf_read_mel");
            nMelAlloc += 1000;
            if (!pStamm->mel_array) return LKF_OUTOFMEMORY_ERROR;
        }
        mel = (int) pStamm->mel_list_gesamt;
        pMel = pStamm->mel_array + (pStamm->mel_list_gesamt++);
		
        memcpy(pMel->MEL, buf, 5);
        memcpy(pMel->sGruppe, buf + 10,8);
        memcpy(pMel->sLGR, buf + 18, 5);
        
        pMel->cGeschlecht = buf[23];
        GetInt (buf + 24, 3, &val);
        pMel->nAltermin = val;
		if (nModellJahr >= 2016) { /* V20.1 */
			if (IsEmpty(buf + 27, 3))
				pMel->nAltermax = 1000;
			else
			{
				GetInt(buf + 27, 3, &val);
				pMel->nAltermax = val;
			}
		}
		else {
			GetInt(buf + 27, 3, &val);
			pMel->nAltermax = val > 0 ? val : 1000;
		}
        GetInt (buf + 30, 3, &val);
        pMel->mel_warn = val;
        GetInt (buf + 33, 3, &val);
        pMel->mel_anz = val;
        pMel->cAmbulant = buf[42];
        pMel->cBdgr0 = buf[43];
        /*
        if (pMel->cBdgr0 == 'W') {
            printf ("W: %5.5s\n", pMel->MEL);
        }
        else if (pMel->cBdgr0 == 'E') {
            printf ("E: %5.5s\n", pMel->MEL);
        }
        */
        pMel->cTKL = buf[44];
        pMel->cAmbModus2 = pMel->cTKL;
        
        GetInt (buf + 36, 3, &val);
        pMel->tag_mel_warn = val;
        GetInt (buf + 39, 3, &val);
        pMel->tag_mel_anz = val;
        
        pMel->cOp = buf[45];
        
        pMel->cSeite = (buf[9] == 'C') ? 'J' : 'N';
        pMel->cLE = buf[9];
        
		pMel->melflags = 0;
		pMel->isValid = 1;
        
        if (nModellJahr >= 2017) {
            if (!memcmp(pMel->MEL, "ZX", 2) || (!memcmp(pMel->MEL, "ZV", 2) && memcmp(pMel->MEL, "ZV510", 5))) {
                pMel->melflags |= LKF_MEL_FLAG_NOAUFDATE2017;
            }
        }
        else if (nModellJahr >= 2014 && pMel->MEL[0] == 'Z' && pMel->MEL[1] == 'X') {
            pMel->melflags |= LKF_MEL_FLAG_NOAUFDATE;
        }
        
        if (nModellJahr >= 2017) {
            memcpy (pMel->sAmbGruppe, buf + 150, 8);
            memcpy (pMel->sAmbLeistGrp, buf + 158, 5);
            
            GetInt (buf + 163, 3, &val);
            pMel->amb_melanz_warn = val;
            
            GetInt (buf + 166, 3, &val);
            pMel->amb_melanz_error = val;

            pMel->cAmbulant = 0;
            
            pMel->cStatVerpflichtend = (buf[149] == 'J');
            pMel->cDiagVerpflichtend = (buf[169] == 'J');
			pMel->cDisableLimitCheck = !pMel->cStatVerpflichtend && !memcmp(buf + 146, "KAL", 3); /* V21.2 */
		}
        else {
            pMel->cStatVerpflichtend = 0;
            pMel->cDiagVerpflichtend = 0;
			pMel->cDisableLimitCheck = (nModellJahr < 2014 || len < 149 || memcmp(buf + 146, "KAL", 3)) ? 0 : 1; /* V18.2 */
		}
        
        if (nModellJahr >= 2019) {
            pMel->cBettFC = buf[171] == 'J' ? 1 : 0;
        }
        else {
            pMel->cBettFC = 0;
        }
        
        if (nModellJahr >= 2025 && !memcmp (pMel->sAmbGruppe, "MEL", 3)) {
            pMel->cAmbModus2 = 'J';
        }
        
        if (nModellJahr >= 2022) {
            pMel->cPhysAnw = buf[172];
        }
        else {
            pMel->cPhysAnw = 0;
        }
        
        if (nModellJahr >= 2019 && len >= 182) {
            GetInt (buf + 180, len >= 186 ? 6 : len - 180, &pMel->ambFixPunkte); /* V2019.1: Position */
        }
        else {
            pMel->ambFixPunkte = 0;
        }
        
        strncpy (pMel->sText, buf + 46, 100);
        pMel->sText[101] = 0;
    }
	
    pStamm->mel_array = (MEL_ARRAY_STRUC *) lkf_realloc_stamm_array (pStamm->pScoringContext, sizeof(MEL_ARRAY_STRUC), (unsigned int) pStamm->mel_list_gesamt, (char *) pStamm->mel_array, (unsigned int) pStamm->mel_list_gesamt, "realloc: lkf_read_mel");
    
    if (!pStamm->mel_array) {
        return LKF_OUTOFMEMORY_ERROR;
    }
    
    qsort (pStamm->mel_array, pStamm->mel_list_gesamt, sizeof (MEL_ARRAY_STRUC), compare_melarray);

	lkf_stamm_close(fp);
    
    if (nModellJahr >= 2026) {
        static const char *noDateMels[] = {"ZN800", "ZN810", "JS510", "ZZ630", "ZZ640", "ZN121", "ZN131", "ZN132", "ZN200", "ZN210", 0};
        const char **ppNoDateMels = noDateMels;
        int flagToSet = LKF_MEL_FLAG_NOAUFDATE2017;
        
        while (*ppNoDateMels) {
            MEL_ARRAY_STRUC *pM = FindStammMel(pStamm, *ppNoDateMels);
            if (pM) pM->melflags |= flagToSet;
            ++ppNoDateMels;
        }
        
    }
    else if (nModellJahr >= 2018) {
        static const char *noDateMels[] = {"ZN800", "ZN810", "JS510", "ZZ640", "ZN121", "ZN131", "ZN132", "ZN200", "ZN210", 0};
        const char **ppNoDateMels = noDateMels;
        int flagToSet = LKF_MEL_FLAG_NOAUFDATE2017;
        
        while (*ppNoDateMels) {
            MEL_ARRAY_STRUC *pM = FindStammMel(pStamm, *ppNoDateMels);
            if (pM) pM->melflags |= flagToSet;
            ++ppNoDateMels;
        }
        
    }
    else if (nModellJahr == 2017) {
        static const char *noDateMels[] = {"ZN800", "ZN810", "JS510", "ZZ640", 0};
        const char **ppNoDateMels = noDateMels;
        int flagToSet = LKF_MEL_FLAG_NOAUFDATE;

        while (*ppNoDateMels) {
            MEL_ARRAY_STRUC *pM = FindStammMel(pStamm, *ppNoDateMels);
            if (pM) pM->melflags |= flagToSet;
            ++ppNoDateMels;
        }
        
    }
    else if (nModellJahr >= 2014) {
        static const char *noDateMels[] = {"ZN800", "ZN810", "ZV610", "ZV620", "ZV630", "ZV635", "ZV640", "ZV644", "ZV648", "ZV660", "ZV670", "ZV675", "ZV677", "ZV680", "ZV690", "ZV698", "JS510", "ZZ640", "ZV520", 0};
        const char **ppNoDateMels = noDateMels;
        
        while (*ppNoDateMels) {
            MEL_ARRAY_STRUC *pM = FindStammMel(pStamm, *ppNoDateMels);
            if (pM) pM->melflags |= LKF_MEL_FLAG_NOAUFDATE;
            ++ppNoDateMels;
        }
    }
    if (nModellJahr >= 2015) {
        
        static const char *revisionsMels2017[] = {"NE090", "NE111", "NE112", "NE130", "NE131", "NE132", "NF190", "NF193", "NF220", "NF240", "NF241", "NF242", "NF243", 0}; /* V21.2 */
        static const char *revisionsMels[] = {"NE090", "NE111", "NE112", "NE130", "NE131", "NE132", 0};
        const char **ppRevisionsMels = nModellJahr >= 2017 ? revisionsMels2017 : revisionsMels;
        
        while (*ppRevisionsMels) {
            MEL_ARRAY_STRUC *pM = FindStammMel (pStamm, *ppRevisionsMels);
            if (pM) pM->melflags |= LKF_MEL_FLAG_REVISION;
            ++ppRevisionsMels;
        }
    }
    if (nModellJahr >= 2025) {
        apply_custom_melChecks(pStamm);
    }
	return 0;
}

#define LKF_MEL_LINE_LEN 110

static int compare_melarray (const void *m1, const void *m2)
{
	return memcmp (((const MEL_ARRAY_STRUC *) m1)->MEL, ((const MEL_ARRAY_STRUC *) m2)->MEL, LKF_AUFENTHALT_NEWMEL_LEN);
}

static int compare_melarray_bsearch (const void *mel, const void *m1)
{
    /* V22.5 - switched parameters (alignment issue) */
	return memcmp (mel, ((const MEL_ARRAY_STRUC *) m1)->MEL, LKF_AUFENTHALT_NEWMEL_LEN);
}

MEL_ARRAY_STRUC *GetStammMel (const STAMM2001 *pStamm, const char *sMel)
{ /* V2024.0 */
    return (MEL_ARRAY_STRUC *) bsearch (sMel, pStamm->mel_array, pStamm->mel_list_gesamt, sizeof(MEL_ARRAY_STRUC), compare_melarray_bsearch);
}

MEL_ARRAY_STRUC *FindMel (const LKF_CONTEXT *pContext, const char *sMel)
{
	return (MEL_ARRAY_STRUC *) bsearch (sMel, pContext->pStamm2001->mel_array, pContext->pStamm2001->mel_list_gesamt, sizeof(MEL_ARRAY_STRUC), compare_melarray_bsearch);
}

static MEL_ARRAY_STRUC *FindStammMel (const STAMM2001 *pStamm, const char *sMel)
{ /* V19.0 */
	return (MEL_ARRAY_STRUC *) bsearch (sMel, pStamm->mel_array, pStamm->mel_list_gesamt, sizeof(MEL_ARRAY_STRUC), compare_melarray_bsearch);
}

int FindMelIndex (const LKF_CONTEXT *pContext, const char *sMel)
{
	MEL_ARRAY_STRUC *pMelStruc = FindMel (pContext, sMel);
	if (!pMelStruc)
		return -1;
	return (int) (pMelStruc - pContext->pStamm2001->mel_array);
}

int FindMelIndexStamm (const STAMM2001 *pStamm, const char *sMel)
{
	MEL_ARRAY_STRUC *pMelStruc = (MEL_ARRAY_STRUC *) bsearch (sMel, pStamm->mel_array, pStamm->mel_list_gesamt, sizeof(MEL_ARRAY_STRUC), compare_melarray_bsearch);
	if (!pMelStruc)
		return -1;
	return (int) (pMelStruc - pStamm->mel_array);
}

MEL_ARRAY_STRUC *FindMelStamm (const STAMM2001 *pStamm, const char *sMel) {
    return (MEL_ARRAY_STRUC *) bsearch (sMel, pStamm->mel_array, pStamm->mel_list_gesamt, sizeof(MEL_ARRAY_STRUC), compare_melarray_bsearch);
}

int FindMelIndexStammWeak (const STAMM2001 *pStamm, const char *sMel, int bLower)
{
	int nUg = -1, nOg = (int) pStamm->mel_list_gesamt;
	int nCur, nCmp;
	while (nUg + 1 < nOg)
	{
		nCur = (nUg+nOg)>>1;
		nCmp = memcmp (sMel, pStamm->mel_array[nCur].MEL, 5);
		if (!nCmp)
			return nCur;
		if (nCmp > 0)
			nUg = nCur;
		else
			nOg = nCur;
	}

	return bLower ? nUg : nOg;
}

const PLZ_GEMCO_STRING *FindPLZGemco (const LKF_CONTEXT *pContext, const char *plz, const char *gemco)
{ /* V21.2 */
    PLZ_GEMCO_STRING plzgemco;
    sprintf (plzgemco, "%4.4s%5.5s", plz, gemco);
    return (const PLZ_GEMCO_STRING *) bsearch(plzgemco, pContext->pStamm2001->plz_gemco_list, pContext->pStamm2001->plz_gemco_gesamt, sizeof (PLZ_GEMCO_STRING), plz_gemco_compare);
}

int stamm_read_statonly (STAMM2001 *pStamm2001, const char *input_path, char *errbuf, int year) {
    int ret;
    const LKF_SATZART_ERRORDEF **ppErrors;
    LKF_CONTEXT *pContext;
    pContext = pStamm2001->pScoringContext;

    pStamm2001->stammAddLineFunc = pContext->stammAddLineFunc;
    pStamm2001->stammFilePathFunc = pContext->stammFilePathFunc;
    pStamm2001->nModellJahr = (unsigned int) year;

    if (year <= 2018 && input_path && *input_path) {
        ret = lkf_read_stamm (pStamm2001, "POST.dat", (char **) &pStamm2001->plz_list, sizeof (PLZ_LIST_STRING),
                              &pStamm2001->plz_gesamt, 0, errbuf);
        if (ret) return ret;
        qsort (pStamm2001->plz_list, pStamm2001->plz_gesamt, sizeof (PLZ_LIST_STRING), plz_compare);
    }
    
    ppErrors = GetAllMBDSErrors (pStamm2001->pScoringContext->modelljahr);
    pStamm2001->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
    
    pStamm2001->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
    lkf_errorinfo_sort (pStamm2001->pAllErrors);

    return 0;
}

/* function read_2001 *********************************************************
**    Liest die Stammdaten ein.                                              **
******************************************************************************/

static int read_2001 (STAMM2001 *pStamm2001, const char *input_path, char *errbuf, int year)
{	
	int ret;
	unsigned int i;
	int v;
    int save_ret = 0;
    int all_missing = 0;

	pStamm2001->nModellJahr = (unsigned int) year;

	/* Einlesen und Sortieren der Diagnosen-Datei */
    ret = lkf_read_diaglist (pStamm2001, (char **) &pStamm2001->icd_list, &pStamm2001->icd_gesamt, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->icd_list, pStamm2001->icd_gesamt, sizeof (ICD_LIST_STRING), diag_compare6);
	
    /* Einlesen und Sortieren der Funktionscode-Datei */
	ret = lkf_read_stamm (pStamm2001, "FUNK.dat", (char **) &pStamm2001->funk_list, sizeof (FUNK_LIST_STRING),
						  &pStamm2001->funk_gesamt, 0, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->funk_list, pStamm2001->funk_gesamt, sizeof (FUNK_LIST_STRING), funk_compare);
	/* Weiterbearbeitung der Funktionscode-Datei */
	pStamm2001->funk_quick = LKF_CALLOC (pStamm2001->pScoringContext, "read_2001.funk", 10000, sizeof (int));
    if (!pStamm2001->funk_quick) return LKF_OUTOFMEMORY_ERROR;
    
	for (i = 0; i < 10000; i++) pStamm2001->funk_quick[i] = 0;
	for (i = 0; i < pStamm2001->funk_gesamt; i++) {
		GetInt (pStamm2001->funk_list[i] + 2, 4,&v); 	
		if (v) pStamm2001->funk_quick[v] = 1;
	}

	/* Einlesen und Sortieren der Kostenträger-Datei */
	ret = lkf_read_stamm (pStamm2001, "KOST.dat", (char **) &pStamm2001->kost_list, sizeof (KOST_LIST_STRING),
						  &pStamm2001->kost_gesamt, 0, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->kost_list, pStamm2001->kost_gesamt, sizeof (KOST_LIST_STRING), kost_compare);
	
    /* Zweite Sortierreihenfolge für die Kostenträger-Datei */
	pStamm2001->kost_list2 = LKF_CALLOC (pStamm2001->pScoringContext, "read_2001.kostlist2", sizeof (char *), pStamm2001->kost_gesamt);
    if (!pStamm2001->kost_list2) return LKF_OUTOFMEMORY_ERROR;

	for (i = 0; i < pStamm2001->kost_gesamt; i++) pStamm2001->kost_list2[i] = pStamm2001->kost_list[i] + 6;
	qsort (pStamm2001->kost_list2, pStamm2001->kost_gesamt, sizeof (char *), kost2_compare);

	/* Einlesen und Sortieren der Länderkennzeichen-Datei */
	ret = lkf_read_stamm (pStamm2001, "LAND.dat", (char **) &pStamm2001->land_list, sizeof (LAND_LIST_STRING),
						  &pStamm2001->land_gesamt, 0, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->land_list, pStamm2001->land_gesamt, sizeof (LAND_LIST_STRING), staat_compare);
	pStamm2001->landAT = bsearch("AT ", pStamm2001->land_list, pStamm2001->land_gesamt, sizeof (LAND_LIST_STRING), staat_compare); /* V18.0 */

	/* Einlesen und Sortieren der KANummern-Datei */
	ret = lkf_read_stamm (pStamm2001, "LKFKA.dat", (char **) &pStamm2001->ka_list, sizeof (KA_LIST_STRING),
						  &pStamm2001->ka_gesamt, 0, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->ka_list, pStamm2001->ka_gesamt, sizeof (KA_LIST_STRING), ka_compare);
	
	pStamm2001->ktr_list = 0;
	pStamm2001->ktr_gesamt = 0;
    
    /* Einlesen der Kostentraeger */
    ret = lkf_read_stamm (pStamm2001, "LKFTR.dat", (char **) &pStamm2001->ktr_list, sizeof (KTR_LIST_STRING),
							  &pStamm2001->ktr_gesamt, 0, errbuf);
    if (!ret) {
        qsort (pStamm2001->ktr_list, pStamm2001->ktr_gesamt, sizeof (KTR_LIST_STRING), ktr_compare);
	}


    /* Einlesen und Sortieren der alternativen KANummern-Datei (wenn vorhanden) */
    ret = lkf_read_stamm (pStamm2001, "LKFGZ.dat", (char **) &pStamm2001->ka_list_alternate, sizeof (KA_LIST_STRING),
                            &pStamm2001->ka_alternate_gesamt, 0, errbuf);
    if (!ret) qsort (pStamm2001->ka_list_alternate, pStamm2001->ka_alternate_gesamt, sizeof (KA_LIST_STRING), ka_compare);

    /* Einlesen der MEL-Datei */
    ret = lkf_read_mel (pStamm2001, errbuf, year);
    if (ret) return ret;
    
    ret = lkf_read_meldiag6 (pStamm2001, errbuf, year);
    if (ret) return ret;
    
    /* Einlesen der unplausiblen MEL-KA */
    ret = lkf_read_stamm (pStamm2001, "MELKA.dat", (char **) &pStamm2001->mel_ka, sizeof (MEL_KA_STRING),
                          &pStamm2001->mel_ka_gesamt, 0, errbuf);
    if (ret) return ret;
    qsort (pStamm2001->mel_ka, pStamm2001->mel_ka_gesamt, sizeof (MEL_KA_STRING), mel_ka_compare2009); /* V16.0: mel_ka_compare -> mel_ka_compare2009 */
    /* V5.1T: Einlesen der unplausiblen Tagesklinik-MEL-KA-Kombinationen */
    ret = lkf_read_stamm (pStamm2001, "MELTKL.dat", (char **) &pStamm2001->mel_tkl, sizeof (MEL_KA_STRING),
                          &pStamm2001->mel_tkl_gesamt, 0, errbuf);
    if (ret && year >= 2012) return ret; /* V16.0 */
    pStamm2001->mel_tkl_fehler = ret;
    if (!ret) qsort (pStamm2001->mel_tkl, pStamm2001->mel_tkl_gesamt, sizeof (MEL_KA_STRING), mel_ka_compare2009); /* V16.0: mel_ka_compare -> mel_ka_compare2009 */
    
    /* Einlesen der unplausiblen MEL-MEL-Diagnosen */
    ret = lkf_read_stamm (pStamm2001, "MELMEL.dat", (char **) &pStamm2001->mel_mel, sizeof (MEL_MEL_STRING),
                          &pStamm2001->mel_mel_gesamt, 0, errbuf);
    if (ret) return ret;
    qsort (pStamm2001->mel_mel, pStamm2001->mel_mel_gesamt, sizeof (MEL_MEL_STRING), mel_mel_compare2009); /* V13.4: mel_mel_compare -> mel_mel_compare2009 */


	pStamm2001->nNPDisabled = 0;
    
    ret = lkf_read_npplaus_intdiag (year, pStamm2001 /* V11.1 */, &pStamm2001->npplaus_gesamt, errbuf);
    if (ret)
    {
        ++all_missing;
        save_ret = ret;
    }
    
    ret = lkf_read_npplaus_intmel (year, pStamm2001, errbuf);
    if (ret)
    {
        ++all_missing;
        save_ret = ret;
    }
    
    ret = lkf_read_npplaus_intbegrmel (year, pStamm2001, errbuf);
    if (ret)
    {
        ++all_missing;
        save_ret = ret;
    }
    
    ret = lkf_read_npplaus_invasmel (year, pStamm2001, errbuf);
    if (ret)
    {
        ++all_missing;
        save_ret = ret;
    }
    
    if (all_missing < 4 && save_ret)
        return save_ret; /* V11.1: wenn NP-Files teilweise fehlen, kein Fehler */
    ret = 0; /* V11.1: wenn alle NP-Dateien fehlen, wird kein Fehler geliefert, es ist aber nur Nicht-Fonds-Überprüfung möglich */
    
    ret = StammInitMELIndexLists (pStamm2001, year);
    if (ret) return ret;

	/* Einlesen der PLZ-Datei */
	ret = lkf_read_stamm (pStamm2001, "POST.dat", (char **) &pStamm2001->plz_list, sizeof (PLZ_LIST_STRING),
						  &pStamm2001->plz_gesamt, 0, errbuf);
	if (ret) return ret;
	qsort (pStamm2001->plz_list, pStamm2001->plz_gesamt, sizeof (PLZ_LIST_STRING), plz_compare);

	/* Einlesen der SPEZLIST-Datei */
	ret = lkf_read_stamm (pStamm2001, "SPEZLIST.dat", (char **) &pStamm2001->spez_list, sizeof (SPEZ_LIST_STRING),
						  &pStamm2001->spez_gesamt, 0, errbuf);
	if (ret) return ret;

	qsort (pStamm2001->spez_list, pStamm2001->spez_gesamt, sizeof (SPEZ_LIST_STRING), spez_compare);

	/* Einlesen der Stern-Kreuz-Datei */
	ret = lkf_read_stamm (pStamm2001, "STERNKR.dat", (char **) &pStamm2001->sternkr_list, sizeof (STERNKR_LIST_STRING),
						  &pStamm2001->sternkr_gesamt, 1, errbuf);
	if (ret) return ret;

	qsort (pStamm2001->sternkr_list, pStamm2001->sternkr_gesamt, sizeof (STERNKR_LIST_STRING), diag_compare5);
	memset (pStamm2001->sternkr_list[pStamm2001->sternkr_gesamt], 0, sizeof (STERNKR_LIST_STRING));

    if (year >= STERNKR_NEW_MODEL)
    { /* 2014 */
        DIAG_INFO *pDiagInfo;
        const ICD_LIST_STRING *icd;
        const char *diag;
        const STERNKR_LIST_STRING *sternkr;
        int dreisteller;
        
        pStamm2001->diag_info = (DIAG_INFO *) LKF_CALLOC(pStamm2001->pScoringContext, "read_2001.diaginfo", sizeof(DIAG_INFO), pStamm2001->icd_gesamt);
        if (!pStamm2001->diag_info) return LKF_OUTOFMEMORY_ERROR;
        
        sternkr = (const STERNKR_LIST_STRING *) pStamm2001->sternkr_list;
        
        for (i = 0; i < pStamm2001->icd_gesamt; i++)
        {
            pStamm2001->diag_info[i].icd_list_string  = (const ICD_LIST_STRING *) (pStamm2001->icd_list + i);
            if (pStamm2001->icd_list[i][7] == '*') pStamm2001->diag_info[i].stern_Diagnose = 1;
        }
        
        for (i = 0; i < pStamm2001->sternkr_gesamt; i++, sternkr++) {
        
            dreisteller = 0;
            if ((*sternkr)[4] == ' ')
            {
                icd = (const ICD_LIST_STRING *) FindDiag3 (pStamm2001, *sternkr);
                dreisteller = 1;
            }
            else if ((*sternkr)[5] == ' ')
                icd = (const ICD_LIST_STRING *) FindDiag5(pStamm2001, *sternkr);
            else
                icd = (const ICD_LIST_STRING *) FindDiag6(pStamm2001, *sternkr);
            
            if (!icd) {
                continue;
            }
            
            pDiagInfo = pStamm2001->diag_info + (icd - (const ICD_LIST_STRING *) pStamm2001->icd_list);
            
            do {
                
                diag = (const char *) pDiagInfo->icd_list_string;
                
                if ((dreisteller && (diag[4] == ' ' || diag[4] == 'x')) ||
                    (!dreisteller && diag[4] != ' ' && diag[4] != 'x')) {
                
                    if (!pDiagInfo->max_sternkr) {
                        pDiagInfo->min_sternkr = i;
                    }
                    pDiagInfo->max_sternkr = i + 1;
                }
                
                ++pDiagInfo;
                ++icd;
            } while (memcmp (*icd, *sternkr + 7, 6) <= 0);
        }
    } else
        pStamm2001->diag_info = 0;
    
    
	ret = lkf_read_stamm (pStamm2001, "GGART.dat", (char **) &pStamm2001->sGGArt, sizeof (GGART_STRING),
							&pStamm2001->lGGArtAnz, 0, errbuf); /* V9.1: Kleinbuchstaben */
	if (ret) 
	{
		if (year >= 2005) return ret; /* V9.2: im Jahr 2004 kein Fehler, wenn die Datei nicht existiert */
	}
	else 
		qsort (pStamm2001->sGGArt, pStamm2001->lGGArtAnz, sizeof (GGART_STRING), ggart_compare);

	ret = lkf_read_stamm (pStamm2001, "GGLEIST.dat", (char **) &pStamm2001->sGGLeist, sizeof (GGLEIST_STRING),
							&pStamm2001->lGGLeistAnz, 0, errbuf); /* V9.1: Kleinbuchstaben */
	if (ret) 
	{
		if (year >= 2005) return ret; /* V9.2: im Jahr 2004 kein Fehler, wenn die Datei nicht existiert */
	}
	else
		qsort (pStamm2001->sGGLeist, pStamm2001->lGGLeistAnz, sizeof (GGLEIST_STRING), ggleist_compare);

	
	pStamm2001->oe_mel_ka = 0;
	pStamm2001->oe_mel_ka_gesamt = 0;
	
    ret = lkf_read_stamm (pStamm2001, "OEMELKA.dat", (char **) &pStamm2001->oe_mel_ka, sizeof (OEMELKA_STRING),
							  &pStamm2001->oe_mel_ka_gesamt, 0, errbuf);
    if (ret)
    {
        pStamm2001->nOSGDisabled = 1;
        ret = 0;
    }
    else
    {
        pStamm2001->nOSGDisabled = 0;
        if (pStamm2001->oe_mel_ka_gesamt > 1)
        {
            qsort (pStamm2001->oe_mel_ka, pStamm2001->oe_mel_ka_gesamt, sizeof (OEMELKA_STRING), oemelka_compare_2009); /* V13.0 */
        }
    }

    ret = lkf_read_stamm (pStamm2001, "OEKASO.dat", (char **) &pStamm2001->oekaso, sizeof(OEKASO_STRING),
							  &pStamm2001->oekaso_gesamt, 0, errbuf);
    if (ret) return ret;
    qsort (pStamm2001->oekaso, pStamm2001->oekaso_gesamt, sizeof (OEKASO_STRING), oekaso_compare);

	if (year >= 2014) 
	{
		ret = lkf_read_stamm (pStamm2001, "DIAGDIAG.dat", (char **) &pStamm2001->diag_diag, sizeof(DIAG_DIAG_STRING),
							  &pStamm2001->diag_diag_gesamt, 0, errbuf);
		if (ret) return ret;
		
        if (year >= 2017) {
            qsort (pStamm2001->diag_diag, pStamm2001->diag_diag_gesamt, sizeof (DIAG_DIAG_STRING), diag_diag_compare2018);
        }
        else {
            qsort (pStamm2001->diag_diag, pStamm2001->diag_diag_gesamt, sizeof (DIAG_DIAG_STRING), diag_diag_compare);
        }
        
		
		ret = lkf_read_stamm (pStamm2001, "DIAGMEL.dat", (char **) &pStamm2001->diag_mel, sizeof(DIAG_MEL_STRING),
							  &pStamm2001->diag_mel_gesamt, 0, errbuf);
		if (ret) return ret;
		qsort (pStamm2001->diag_mel, pStamm2001->diag_mel_gesamt, sizeof (DIAG_MEL_STRING), diag_mel_compare);

	} 
	else 
	{
		pStamm2001->diag_mel = 0;
		pStamm2001->diag_diag = 0;
		pStamm2001->diag_mel_gesamt = 0;
		pStamm2001->diag_diag_gesamt = 0;
	}

    if (year >= 2017) {
        /* Einlesen der GKZ-Datei */
        typedef char PLZ_GEMCO_LONG_STRING[128];
        PLZ_GEMCO_LONG_STRING *plz_gemco;
        
        ret = lkf_read_stamm(pStamm2001, "GEMCO.dat", (char **) &pStamm2001->gkz_list, sizeof (GKZ_LIST_STRING), &pStamm2001->gkz_list_gesamt, 0, errbuf);
        if (ret) return ret;
        qsort (pStamm2001->gkz_list, pStamm2001->gkz_list_gesamt, sizeof (GKZ_LIST_STRING), gkz_compare);        
    
        ret = lkf_read_stamm(pStamm2001, "POST_GEMCO.dat", (char **) &plz_gemco, 128, &pStamm2001->plz_gemco_gesamt, 0, errbuf);
        if (ret) return ret;
        
        pStamm2001->plz_gemco_list = LKF_CALLOC(pStamm2001->pScoringContext, "read_2001", pStamm2001->plz_gemco_gesamt, sizeof (PLZ_GEMCO_STRING));
        if (!pStamm2001->plz_gemco_list) {
            return LKF_OUTOFMEMORY_ERROR;
        }
        for (i = 0; i < pStamm2001->plz_gemco_gesamt; i++) {
            sprintf ((char *) (pStamm2001->plz_gemco_list + i), "%4.4s%5.5s", plz_gemco[i], plz_gemco[i] + 34);
        }
        qsort (pStamm2001->plz_gemco_list, pStamm2001->plz_gemco_gesamt, sizeof(PLZ_GEMCO_STRING), plz_gemco_compare);
        LKF_FREE (pStamm2001->pScoringContext, "read_2001", plz_gemco);
    
    }
    else {
        pStamm2001->gkz_list = 0;
        pStamm2001->gkz_list_gesamt = 0;
        
        pStamm2001->plz_gemco_list = 0;
        pStamm2001->plz_gemco_gesamt = 0;
    }
    
    pStamm2001->atc_list = 0;
    pStamm2001->atc_list_gesamt = 0;
    if (year >= SATZART_A_MODEL) {
        ret = lkf_read_stamm(pStamm2001, "ATCLIST.dat", (char **) &pStamm2001->atc_list, sizeof (ATCLIST_STRING), &pStamm2001->atc_list_gesamt, 0, errbuf);
        if (!ret) {
            qsort (pStamm2001->atc_list, pStamm2001->atc_list_gesamt, sizeof (ATCLIST_STRING), atc_compare);
        }
    }

	return 0;
}


/******************************************************************************
** function lkf_set_global_inputpath: Setzt den Stammdaten-Pfad auf einen    **
**    Default-Wert, der herangezogen wird, wenn einzelne Aufrufe keinen      **
**    eigenen Input-Pfad mitgeben.                                           **
******************************************************************************/

static char global_input_path[1024] = "";

_SCORING_EXPORT void lkf_set_global_inputpath (const char *ip)
{
	strncpy (global_input_path, ip, 1024);
	global_input_path[1023] = 0;
}

void build_path (char *path, const char *srcpath, int year)
{
	char yearstring[6];
	sprintf(yearstring, "%04d", year);
    
    if (path == 0)
        path = global_input_path;
    
	combine_path(path, srcpath, yearstring);
}

/* function unloadStamm *******************************************************
**    Setzt die Stammdaten auf ungültig, sodaß sie beim nächsten Lauf wieder **
**    neu eingelesen werden.                                                 **
******************************************************************************/

long lkf_unloadStamm (void)
{
	STAMM2001 *pOldStamm2017, *pOldStamm2018, *pOldStamm2019, *pOldStamm2020, *pOldStamm2021,
#ifdef SUPPORT_PRE2017
    *pOldStamm2009, *pOldStamm2010, *pOldStamm2011, *pOldStamm2012, *pOldStamm2013,
              *pOldStamm2014, *pOldStamm2015, *pOldStamm2016,
#endif
    *pOldStamm2022, *pOldStamm2023, *pOldStamm2024, *pOldStamm2025;

#ifdef SUPPORT_PRE2017
	pOldStamm2009 = pStamm2009Global;
	stamm2009_status = 0;
	pStamm2009Global = 0;

	pOldStamm2010 = pStamm2010Global;
	stamm2010_status = 0;
	pStamm2010Global = 0;

	pOldStamm2011 = pStamm2011Global;
	stamm2011_status = 0;
	pStamm2011Global = 0;

	pOldStamm2012 = pStamm2012Global;
	stamm2012_status = 0;
	pStamm2012Global = 0;

	pOldStamm2013 = pStamm2013Global; /* V17.0 */
	stamm2013_status = 0;
	pStamm2013Global = 0;

	pOldStamm2014 = pStamm2014Global; /* V18.0 */
	stamm2014_status = 0;
	pStamm2014Global = 0;

    pOldStamm2015 = pStamm2015Global; /* V19.0 */
    stamm2015_status = 0;
    pStamm2015Global = 0;

    pOldStamm2016 = pStamm2016Global; /* V20.0 */
    stamm2016_status = 0;
    pStamm2016Global = 0;
#endif
    pOldStamm2017 = pStamm2017Global; /* V21.0 */
    stamm2017_status = 0;
    pStamm2017Global = 0;

    pOldStamm2018 = pStamm2018Global; /* V22.0 */
    stamm2018_status = 0;
    pStamm2018Global = 0;

    pOldStamm2019 = pStamm2019Global; /* V2019.0 */
    stamm2019_status = 0;
    pStamm2019Global = 0;

    pOldStamm2020 = pStamm2020Global; /* V2020.0 */
    stamm2020_status = 0;
    pStamm2020Global = 0;

    pOldStamm2021 = pStamm2021Global; /* V2021.0 */
    stamm2021_status = 0;
    pStamm2021Global = 0;

    pOldStamm2022 = pStamm2022Global; /* V2022.0 */
    stamm2022_status = 0;
    pStamm2022Global = 0;

    pOldStamm2023 = pStamm2023Global; /* V2023.0 */
    stamm2023_status = 0;
    pStamm2023Global = 0;

    pOldStamm2024 = pStamm2024Global; /* V2024.0 */
    stamm2024_status = 0;
    pStamm2024Global = 0;

    pOldStamm2025 = pStamm2025Global; /* V2025.0 */
    stamm2025_status = 0;
    pStamm2025Global = 0;
#ifdef SUPPORT_PRE2017
	if (pOldStamm2009) freeStamm2001 (pOldStamm2009);
	if (pOldStamm2010) freeStamm2001 (pOldStamm2010);
	if (pOldStamm2011) freeStamm2001 (pOldStamm2011);
	if (pOldStamm2012) freeStamm2001 (pOldStamm2012);
	if (pOldStamm2013) freeStamm2001 (pOldStamm2013); /* V17.0 */
	if (pOldStamm2014) freeStamm2001 (pOldStamm2014); /* V18.0 */
    if (pOldStamm2015) freeStamm2001 (pOldStamm2015); /* V19.0 */
    if (pOldStamm2016) freeStamm2001 (pOldStamm2016); /* V20.0 */
#endif
    if (pOldStamm2017) freeStamm2001 (pOldStamm2017); /* V22.0 */
    if (pOldStamm2018) freeStamm2001 (pOldStamm2018); /* V22.0 */
    if (pOldStamm2019) freeStamm2001 (pOldStamm2019); /* V2019.0 */
    if (pOldStamm2020) freeStamm2001 (pOldStamm2020); /* V2020.0 */
    if (pOldStamm2021) freeStamm2001 (pOldStamm2021); /* V2021.0 */
    if (pOldStamm2022) freeStamm2001 (pOldStamm2022); /* V2022.0 */
    if (pOldStamm2023) freeStamm2001 (pOldStamm2023); /* V2023.0 */
    if (pOldStamm2024) freeStamm2001 (pOldStamm2024); /* V2024.0 */
    if (pOldStamm2025) freeStamm2001 (pOldStamm2025); /* V2025.0 */
    return 0;
}
 
/* function freeStamm2001 *****************************************************
**    Gibt eine Stammdatenstruktur frei.                                     **
*** V6.0 *********************************************************************/

void freeStamm2001 (STAMM2001 *pStamm)
{
	unsigned int i;
    LKF_CONTEXT *pContext;
    pContext = pStamm->pScoringContext;
	if (pStamm->input_path)		LKF_FREE (pContext, "freeStamm2001", pStamm->input_path);
	if (pStamm->funk_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->funk_list);
	if (pStamm->ka_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->ka_list);
	if (pStamm->ka_list_alternate) LKF_FREE (pContext, "freeStamm2001", pStamm->ka_list_alternate); /* V12.1 */
	if (pStamm->kost_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->kost_list);
	if (pStamm->kost_list2)		LKF_FREE (pContext, "freeStamm2001", pStamm->kost_list2);
	if (pStamm->land_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->land_list);
	if (pStamm->plz_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->plz_list);
	if (pStamm->spez_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->spez_list);
	if (pStamm->icd_list)		LKF_FREE (pContext, "freeStamm2001", pStamm->icd_list);
	if (pStamm->sternkr_list)	LKF_FREE (pContext, "freeStamm2001", pStamm->sternkr_list);
	if (pStamm->mel_array)		LKF_FREE (pContext, "freeStamm2001", pStamm->mel_array);
	if (pStamm->mel_ka)			LKF_FREE (pContext, "freeStamm2001", pStamm->mel_ka);
	if (pStamm->mel_mel)		LKF_FREE (pContext, "freeStamm2001", pStamm->mel_mel);
	if (pStamm->funk_quick)		LKF_FREE (pContext, "freeStamm2001", pStamm->funk_quick);
	
	if (pStamm->mel_diag)		LKF_FREE (pContext, "freeStamm2001", pStamm->mel_diag);
	if (pStamm->mel_diag6)		LKF_FREE (pContext, "freeStamm2001", pStamm->mel_diag6);

	if (pStamm->mel_tkl)		LKF_FREE (pContext, "freeStamm2001", pStamm->mel_tkl);
	if (pStamm->ppKnoten)
	{
		for (i = 0; i < pStamm->knoten_gesamt; i++) LKF_FREE (pContext, "freeStamm2001", pStamm->ppKnoten[i]);
		LKF_FREE (pContext, "freeStamm2001", pStamm->ppKnoten);
	}
	if (pStamm->pNPPlaus)		LKF_FREE (pContext, "freeStamm2001", pStamm->pNPPlaus);

	if (pStamm->pAllErrors)		lkf_free_errorinfo (pStamm->pAllErrors);

	if (pStamm->sGGArt)			LKF_FREE (pContext, "freeStamm2001", pStamm->sGGArt);	  /* V11.0 */
	if (pStamm->sGGLeist)		LKF_FREE (pContext, "freeStamm2001", pStamm->sGGLeist);  /* V11.0 */

	/* V13.0 */
	for (i = 0; i < MEL_INDEX_LIST_COUNT; i++)
	{
		if (pStamm->ppMelIndexLists[i]) LKF_FREE (pContext, "freeStamm2001", pStamm->ppMelIndexLists[i]);
	}

	if (pStamm->oekaso)          LKF_FREE (pContext, "freeStamm2001", pStamm->oekaso); /* V13.0 */
	if (pStamm->diag_diag) LKF_FREE (pContext, "freeStamm2001", pStamm->diag_diag);    /* V18.0 */
	if (pStamm->diag_mel) LKF_FREE (pContext, "freeStamm2001", pStamm->diag_mel);      /* V18.0 */

    if (pStamm->diag_info) LKF_FREE (pContext, "freeStamm2001", pStamm->diag_info);    /* V18.0 */
    
    if (pStamm->gkz_list) LKF_FREE(pContext, "freeStamm2001", pStamm->gkz_list);       /* V21.0 */
    if (pStamm->plz_gemco_list) LKF_FREE (pContext, "freeStamm2001", pStamm->plz_gemco_list); /* V21.2 */
    
	if (pStamm->ktr_list) LKF_FREE(pContext, "freeStamm2001", pStamm->ktr_list); /* V18.2 */
	if (pStamm->oe_mel_ka) LKF_FREE (pContext, "freeStamm2001", pStamm->oe_mel_ka); /* V18.2 */
	if (pStamm->pMBDSErrors) lkf_free_errorgroup (pContext, pStamm->pMBDSErrors); /* V18.2 */
    
    if (pStamm->melmelInfoBuffer) LKF_FREE(pContext, "freeStamm2001", pStamm->melmelInfoBuffer); /* V2024.0 */
    
    if (pStamm->atc_list) LKF_FREE (pContext, "freeStamm2001", pStamm->atc_list); /* V2026.0 */
	LKF_FREE (pContext, "freeStamm2001", pStamm);
}

/* function allocStamm2001 ****************************************************
**    Belegt den Speicher für die Stamm2001-Struktur.                        **
******************************************************************************/

STAMM2001 *allocStamm2001 (LKF_CONTEXT *pContext, const char *input, long year)
{
	STAMM2001 *pStamm = (STAMM2001 *) LKF_CALLOC (pContext, "allocStamm2001", sizeof (STAMM2001),1);
    if (!pStamm) return 0;
	pStamm->input_path = LKF_MALLOC (pContext, "allocStamm2001", strlen (input) + 1); /* V5.1 */
    pStamm->pScoringContext = pContext;
    if (!pStamm->input_path) {
        LKF_FREE(pContext, "allocStamm2001", pStamm);
        return 0;
    }
	strcpy (pStamm->input_path, input);
	
	/* V6.0 */
	pStamm->ppKnoten = (LKF_KNOTEN **)LKF_CALLOC(pContext, "allocStamm2001", sizeof(LKF_KNOTEN *), 50);
    if (!pStamm->ppKnoten) {
        LKF_FREE(pContext, "allocStamm2001", pStamm->input_path);
        LKF_FREE(pContext, "allocStamm2001", pStamm);
        return 0;

    }
	pStamm->knoten_alloc = 50;
	pStamm->knoten_gesamt = 0;
	
	return pStamm;
}


#ifdef SUPPORT_PRE2017

/* function getStamm2009 * V13.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2009                                  **
********************************************************************************/

STAMM2001 *getStamm2009 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2009;
	const LKF_SATZART_ERRORDEF **ppErrors;
    
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2009_status || pContext->privateStamm) {
        
		char input[1024];

		if (!pContext->privateStamm) stamm2009_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2009);
		
		pStamm2009 = allocStamm2001 (input, 2009);
        if (pContext->privateStamm) pStamm2009->pScoringContext = pContext;
        pStamm2009->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2009->stammFilePathFunc = pContext->stammFilePathFunc;
        
		if (read_2001 (pStamm2009, input, errbuf, 2009)) {
            if (!pContext->privateStamm) {
                stamm2009_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2009);
            return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2009);
		pStamm2009->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2009->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2009->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2009Global = pStamm2009;
            stamm2009_status = 2;
        }
	}
	else if (stamm2009_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2009 = pStamm2009Global;
    }
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2009;
}

/* function getStamm2010 * V14.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2010                                  **
********************************************************************************/

STAMM2001 *getStamm2010 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2010;
	const LKF_SATZART_ERRORDEF **ppErrors;
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2010_status || pContext->privateStamm) {

		char input[1024];

		if (!pContext->privateStamm) stamm2010_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2010);
		
		pStamm2010 = allocStamm2001 (input, 2010);
        if (pContext->privateStamm) pStamm2010->pScoringContext = pContext;
        pStamm2010->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2010->stammFilePathFunc = pContext->stammFilePathFunc;

		if (read_2001 (pStamm2010, input, errbuf, 2010)) {
            if (!pContext->privateStamm) {
                stamm2010_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2010);
			return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2010);
		pStamm2010->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2010->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2010->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2010Global = pStamm2010;
            stamm2010_status = 2;
        }
	}
	else if (stamm2010_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2010 = pStamm2010Global;
    }
	
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2010;
}

/* function getStamm2011 * V15.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2011                                  **
********************************************************************************/

STAMM2001 *getStamm2011 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2011;
	const LKF_SATZART_ERRORDEF **ppErrors;
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2011_status || pContext->privateStamm) {

		char input[1024];

		if (!pContext->privateStamm) stamm2011_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2011);
		
		pStamm2011 = allocStamm2001 (input, 2011);
        if (pContext->privateStamm) pStamm2011->pScoringContext = pContext;
        pStamm2011->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2011->stammFilePathFunc = pContext->stammFilePathFunc;

		if (read_2001 (pStamm2011, input, errbuf, 2011)) {
            if (!pContext->privateStamm) {
                stamm2011_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2011);
			return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2011);
		pStamm2011->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2011->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2011->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2011Global = pStamm2011;
            stamm2011_status = 2;
        }
	}
	else if (stamm2011_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2011 = pStamm2011Global;
    }
	
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2011;
}

#endif

/* function getStamm2017 * V21.0 ***********************************************
 **    Liefert die Stammdaten des Jahres 2017                                  **
 ********************************************************************************/

STAMM2001 *getStamm2017 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2017;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2017_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2017_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2017);
        
        pStamm2017 = allocStamm2001 (pContext, input, 2017);
        if (pContext->privateStamm) pStamm2017->pScoringContext = pContext;
        pStamm2017->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2017->stammFilePathFunc = pContext->stammFilePathFunc;

        if (read_2001 (pStamm2017, input, errbuf, 2017)) {
            if (!pContext->privateStamm) {
                stamm2017_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2017);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2017);
        pStamm2017->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2017->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2017->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2017Global = pStamm2017;
            stamm2017_status = 2;
        }
    }
    else if (stamm2017_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2017 = pStamm2017Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2017;
}

/* function getStamm2019 * V2019.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2019                                  **
 ********************************************************************************/

STAMM2001 *getStamm2019 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2019;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2019_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2019_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2019);
        
        pStamm2019 = allocStamm2001 (pContext, input, 2019);
        if (pContext->privateStamm) pStamm2019->pScoringContext = pContext;
        pStamm2019->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2019->stammFilePathFunc = pContext->stammFilePathFunc;
        
        if (read_2001 (pStamm2019, input, errbuf, 2019)) {
            if (!pContext->privateStamm) {
                stamm2019_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2019);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2019);
        pStamm2019->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2019->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2019->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2019Global = pStamm2019;
            stamm2019_status = 2;
        }
    }
    else if (stamm2019_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2019 = pStamm2019Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2019;
}


STAMM2001 *getStamm2022 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2022;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2022_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2022_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2022);
        
        pStamm2022 = allocStamm2001 (pContext, input, 2022);
        if (pContext->privateStamm) pStamm2022->pScoringContext = pContext;
        pStamm2022->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2022->stammFilePathFunc = pContext->stammFilePathFunc;
        
        if (read_2001 (pStamm2022, input, errbuf, 2022)) {
            if (!pContext->privateStamm) {
                stamm2022_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2022);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2022);
        pStamm2022->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2022->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2022->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2022Global = pStamm2022;
            stamm2022_status = 2;
        }
    }
    else if (stamm2022_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2022 = pStamm2022Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2022;
}


/* function getStamm2023 * V2023.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2023                                  **
 ********************************************************************************/

STAMM2001 *getStamm2023 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2023;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2023_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2023_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2023);
        
        pStamm2023 = allocStamm2001 (pContext, input, 2023);
        if (pContext->privateStamm) pStamm2023->pScoringContext = pContext;
        pStamm2023->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2023->stammFilePathFunc = pContext->stammFilePathFunc;
        
        if (read_2001 (pStamm2023, input, errbuf, 2023)) {
            if (!pContext->privateStamm) {
                stamm2023_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2023);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2023);
        pStamm2023->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2023->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2023->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2023Global = pStamm2023;
            stamm2023_status = 2;
        }
    }
    else if (stamm2023_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2023 = pStamm2023Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2023;
}

/* function getStamm2024 * V2024.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2024                                  **
 ********************************************************************************/

STAMM2001 *getStamm2024 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    int ret;
    STAMM2001 *pStamm2024;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2024_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2024_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2024);
        
        pStamm2024 = allocStamm2001 (pContext, input, 2024);
        if (!pStamm2024) {
            if (!pContext->privateStamm) {
                stamm2026_status = 0;
                lkf_leave_semaphore (0);
            }
            return 0;
        }
        
        if (pContext->privateStamm) pStamm2024->pScoringContext = pContext;
        pStamm2024->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2024->stammFilePathFunc = pContext->stammFilePathFunc;
        
        ret = read_2001 (pStamm2024, input, errbuf, 2024);
        
        if (ret) {
            if (!pContext->privateStamm) {
                stamm2024_status = 0;
                lkf_leave_semaphore (0);
            }
            if (ret == LKF_OUTOFMEMORY_ERROR && errbuf) {
                strcpy (errbuf, "read metadata 2024: OutOfMemory");
                if (!pContext->outOfMemory[0]) strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2024);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2024);
        pStamm2024->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        if (!pStamm2024->pMBDSErrors) {
            const char *oomtext = lkf_context_get_outOfMemoryText(pContext);
            if (!pContext->privateStamm) {
                stamm2024_status = 0;
                lkf_leave_semaphore (0);
            }
            if (oomtext) {
                sprintf (errbuf, "read metadata 2024: %s", oomtext);
            }
            else {
                strcpy (errbuf, "read metadata 2024: OutOfMemory/lkf_alloc_errorgroup");
                strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2024);
            return 0;
        }

        pStamm2024->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2024->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2024Global = pStamm2024;
            stamm2024_status = 2;
        }
    }
    else if (stamm2024_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2024 = pStamm2024Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2024;
}

/* function getStamm2025 * V2025.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2025                                  **
 ********************************************************************************/


STAMM2001 *getStamm2025 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    int ret;
    STAMM2001 *pStamm2025;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2025_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2025_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2025);
        
        pStamm2025 = allocStamm2001 (pContext, input, 2025);
        if (!pStamm2025) {
            if (!pContext->privateStamm) {
                stamm2025_status = 0;
                lkf_leave_semaphore (0);
            }
            return 0;
        }
        if (pContext->privateStamm) pStamm2025->pScoringContext = pContext;
        pStamm2025->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2025->stammFilePathFunc = pContext->stammFilePathFunc;
                
        ret = read_2001 (pStamm2025, input, errbuf, 2025);
        
        if (ret) {
            if (!pContext->privateStamm) {
                stamm2025_status = 0;
                lkf_leave_semaphore (0);
            }
            if (ret == LKF_OUTOFMEMORY_ERROR && errbuf) {
                strcpy (errbuf, "read metadata 2025: OutOfMemory");
                if (!pContext->outOfMemory[0]) strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2025);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2025);
        pStamm2025->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        if (!pStamm2025->pMBDSErrors) {
            const char *oomtext = lkf_context_get_outOfMemoryText(pContext);
            if (!pContext->privateStamm) {
                stamm2025_status = 0;
                lkf_leave_semaphore (0);
            }
            if (oomtext) {
                sprintf (errbuf, "read metadata 2025: OutOfMemory: %s", oomtext);
            }
            else {
                strcpy (errbuf, "read metadata 2025: OutOfMemory/lkf_alloc_errorgroup");
                strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2025);
            return 0;
        }
        
        pStamm2025->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        if (!pStamm2025->pAllErrors) {
            const char *oomtext = lkf_context_get_outOfMemoryText(pContext);
            if (!pContext->privateStamm) {
                stamm2025_status = 0;
                lkf_leave_semaphore (0);
            }
            if (oomtext) {
                sprintf (errbuf, "read metadata 2025: OutOfMemory: %s", oomtext);
            }
            else {
                strcpy (errbuf, "read metadata 2025: OutOfMemory/lkf_create_errorinfo");
            }
            freeStamm2001(pStamm2025);
            return 0;
        }
        lkf_errorinfo_sort (pStamm2025->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2025Global = pStamm2025;
            stamm2025_status = 2;
        }
    }
    else if (stamm2025_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2025 = pStamm2025Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2025;
}

/* function getStamm2026 * V2026.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2026                                  **
 ********************************************************************************/


STAMM2001 *getStamm2026 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    int ret;
    STAMM2001 *pStamm2026;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2026_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2026_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2026);
        
        pStamm2026 = allocStamm2001 (pContext, input, 2026);
        if (!pStamm2026) {
            if (!pContext->privateStamm) {
                stamm2026_status = 0;
                lkf_leave_semaphore (0);
            }
            return 0;
        }

        if (pContext->privateStamm) pStamm2026->pScoringContext = pContext;
        pStamm2026->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2026->stammFilePathFunc = pContext->stammFilePathFunc;
        
        ret = read_2001 (pStamm2026, input, errbuf, 2026);
        
        if (ret) {
            if (!pContext->privateStamm) {
                stamm2026_status = 0;
                lkf_leave_semaphore (0);
            }
            if (ret == LKF_OUTOFMEMORY_ERROR && errbuf) {
                strcpy (errbuf, "read metadata 2026: OutOfMemory");
                if (!pContext->outOfMemory[0]) strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2026);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2026);
        pStamm2026->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        if (!pStamm2026->pMBDSErrors) {
            const char *oomtext = lkf_context_get_outOfMemoryText(pContext);
            if (!pContext->privateStamm) {
                stamm2024_status = 0;
                lkf_leave_semaphore (0);
            }
            if (oomtext) {
                sprintf (errbuf, "read metadata 2026: %s", oomtext);
            }
            else {
                strcpy (errbuf, "read metadata 2026: OutOfMemory/lkf_alloc_errorgroup");
                strcpy (pContext->outOfMemory, errbuf);
            }
            freeStamm2001(pStamm2026);
            return 0;
        }

        pStamm2026->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        if (!pStamm2026->pAllErrors) {
            const char *oomtext = lkf_context_get_outOfMemoryText(pContext);
            if (!pContext->privateStamm) {
                stamm2026_status = 0;
                lkf_leave_semaphore (0);
            }
            if (oomtext) {
                sprintf (errbuf, "read metadata 2026: %s", oomtext);
            }
            else {
                strcpy (errbuf, "read metadata 2026: OutOfMemory/lkf_create_errorinfo");
            }
            freeStamm2001(pStamm2026);
            return 0;
        }

        lkf_errorinfo_sort (pStamm2026->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2026Global = pStamm2026;
            stamm2026_status = 2;
        }
    }
    else if (stamm2026_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2026 = pStamm2026Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2026;
}



/* function getStamm2021 * V2021.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2021                                  **
 ********************************************************************************/

STAMM2001 *getStamm2021 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2021;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2021_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2021_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2021);
        
        pStamm2021 = allocStamm2001 (pContext, input, 2021);
        if (pContext->privateStamm) pStamm2021->pScoringContext = pContext;
        pStamm2021->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2021->stammFilePathFunc = pContext->stammFilePathFunc;
        
        if (read_2001 (pStamm2021, input, errbuf, 2021)) {
            if (!pContext->privateStamm) {
                stamm2021_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2021);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2021);
        pStamm2021->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2021->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2021->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2021Global = pStamm2021;
            stamm2021_status = 2;
        }
    }
    else if (stamm2021_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2021 = pStamm2021Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2021;
}


/* function getStamm2020 * V2020.0 **********************************************
 **    Liefert die Stammdaten des Jahres 2020                                  **
 ********************************************************************************/

STAMM2001 *getStamm2020 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2020;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2020_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2020_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2020);
        
        pStamm2020 = allocStamm2001 (pContext, input, 2020);
        if (pContext->privateStamm) pStamm2020->pScoringContext = pContext;
        pStamm2020->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2020->stammFilePathFunc = pContext->stammFilePathFunc;
        
        if (read_2001 (pStamm2020, input, errbuf, 2020)) {
            if (!pContext->privateStamm) {
                stamm2020_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2020);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2020);
        pStamm2020->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2020->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2020->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2020Global = pStamm2020;
            stamm2020_status = 2;
        }
    }
    else if (stamm2020_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2020 = pStamm2020Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2020;
}


/* function getStamm2018 * V22.0 ***********************************************
 **    Liefert die Stammdaten des Jahres 2018                                  **
 ********************************************************************************/

STAMM2001 *getStamm2018 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2018;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2018_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2018_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2018);
        
        pStamm2018 = allocStamm2001 (pContext, input, 2018);
        if (pContext->privateStamm) pStamm2018->pScoringContext = pContext;
        pStamm2018->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2018->stammFilePathFunc = pContext->stammFilePathFunc;

        if (read_2001 (pStamm2018, input, errbuf, 2018)) {
            if (!pContext->privateStamm) {
                stamm2018_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2018);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2018);
        pStamm2018->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2018->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2018->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2018Global = pStamm2018;
            stamm2018_status = 2;
        }
    }
    else if (stamm2018_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2018 = pStamm2018Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2018;
}

#ifdef SUPPORT_PRE2017

/* function getStamm2016 * V20.0 ***********************************************
 **    Liefert die Stammdaten des Jahres 2016                                  **
 ********************************************************************************/

STAMM2001 *getStamm2016 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2016;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2016_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2016_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2016);
        
        pStamm2016 = allocStamm2001 (input, 2016);
        if (pContext->privateStamm) pStamm2016->pScoringContext = pContext;
        pStamm2016->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2016->stammFilePathFunc = pContext->stammFilePathFunc;

        if (read_2001 (pStamm2016, input, errbuf, 2016)) {
            if (!pContext->privateStamm) {
                stamm2016_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2016);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2016);
        pStamm2016->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2016->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2016->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2016Global = pStamm2016;
            stamm2016_status = 2;
        }
    }
    else if (stamm2016_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2016 = pStamm2016Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2016;
}

/* function getStamm2015 * V19.0 ***********************************************
 **    Liefert die Stammdaten des Jahres 2015                                  **
 ********************************************************************************/

STAMM2001 *getStamm2015 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2015;
    const LKF_SATZART_ERRORDEF **ppErrors;
    if (!pContext->privateStamm) lkf_enter_semaphore(0);
    
    if (!stamm2015_status || pContext->privateStamm) {
        
        char input[1024];
        
        if (!pContext->privateStamm) stamm2015_status = 1;
        if (*global_input_path) input_path = global_input_path;
        build_path (input, input_path, 2015);
        
        pStamm2015 = allocStamm2001 (input, 2015);
        if (pContext->privateStamm) pStamm2015->pScoringContext = pContext;
        pStamm2015->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2015->stammFilePathFunc = pContext->stammFilePathFunc;

        if (read_2001 (pStamm2015, input, errbuf, 2015)) {
            if (!pContext->privateStamm) {
                stamm2015_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2015);
            return 0; /* FEHLER */
        }
        
        ppErrors = GetAllMBDSErrors (2015);
        pStamm2015->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        
        pStamm2015->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2015->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2015Global = pStamm2015;
            stamm2015_status = 2;
        }
    }
    else if (stamm2015_status == 1) {
        lkf_leave_semaphore (0);
        return 0;
    }
    else {
        pStamm2015 = pStamm2015Global;
    }
    
    if (!pContext->privateStamm) lkf_leave_semaphore (0);
    return pStamm2015;
}


/* function getStamm2014 * V18.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2014                                  **
********************************************************************************/

STAMM2001 *getStamm2014 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2014;
	const LKF_SATZART_ERRORDEF **ppErrors;
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2014_status|| pContext->privateStamm) {

		char input[1024];

		if (!pContext->privateStamm) stamm2014_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2014);
		
		pStamm2014 = allocStamm2001 (input, 2014);
        if (pContext->privateStamm) pStamm2014->pScoringContext = pContext;
        pStamm2014->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2014->stammFilePathFunc = pContext->stammFilePathFunc;

		if (read_2001 (pStamm2014, input, errbuf, 2014)) {
            if (!pContext->privateStamm) {
                stamm2014_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2014);
			return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2014);
		pStamm2014->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2014->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2014->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2014Global = pStamm2014;
            stamm2014_status = 2;
        }
	}
	else if (stamm2014_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2014 = pStamm2014Global;
    }
	
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2014;
}



/* function getStamm2013 * V17.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2013                                  **
********************************************************************************/

STAMM2001 *getStamm2013 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2013;
	const LKF_SATZART_ERRORDEF **ppErrors;
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2013_status || pContext->privateStamm) {

		char input[1024];

		if (!pContext->privateStamm) stamm2013_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2013);
		
		pStamm2013 = allocStamm2001 (input, 2013);
        if (pContext->privateStamm) pStamm2013->pScoringContext = pContext;
        pStamm2013->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2013->stammFilePathFunc = pContext->stammFilePathFunc;

		if (read_2001 (pStamm2013, input, errbuf, 2013)) {
            if (!pContext->privateStamm) {
                stamm2013_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2013);
			return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2013);
		pStamm2013->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2013->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2013->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2013Global = pStamm2013;
            stamm2013_status = 2;
        }
	}
	else if (stamm2013_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2013 = pStamm2013Global;
    }
	
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2013;
}


/* function getStamm2012 * V16.0 ***********************************************
**    Liefert die Stammdaten des Jahres 2012                                  **
********************************************************************************/

STAMM2001 *getStamm2012 (LKF_CONTEXT *pContext, const char *input_path, char *errbuf)
{
    STAMM2001 *pStamm2012;
	const LKF_SATZART_ERRORDEF **ppErrors;
	if (!pContext->privateStamm) lkf_enter_semaphore(0);

	if (!stamm2012_status || pContext->privateStamm) {

		char input[1024];

		if (!pContext->privateStamm) stamm2012_status = 1;
		if (*global_input_path) input_path = global_input_path;
		build_path (input, input_path, 2012);
		
        pStamm2012 = allocStamm2001 (input, 2012);
        if (pContext->privateStamm) pStamm2012->pScoringContext = pContext;
        pStamm2012->stammAddLineFunc = pContext->stammAddLineFunc;
        pStamm2012->stammFilePathFunc = pContext->stammFilePathFunc;

		if (read_2001 (pStamm2012, input, errbuf, 2012)) {
            if (!pContext->privateStamm) {
                stamm2012_status = 0;
                lkf_leave_semaphore (0);
            }
            freeStamm2001(pStamm2012);
			return 0; /* FEHLER */
		}

		ppErrors = GetAllMBDSErrors (2012);
		pStamm2012->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
 
		pStamm2012->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
		lkf_errorinfo_sort (pStamm2012->pAllErrors);
        if (!pContext->privateStamm) {
            pStamm2012Global = pStamm2012;
            stamm2012_status = 2;
        }
	}
	else if (stamm2012_status == 1) {
		lkf_leave_semaphore (0);
		return 0;
	}
    else {
        pStamm2012 = pStamm2012Global;
    }
	
	if (!pContext->privateStamm) lkf_leave_semaphore (0);
	return pStamm2012;
}

#endif

/* function KA_to_TA ********************************** 24.01. ****************
**    Konvertiert die KA-Nummer (z. B. aus Plaus-Satzart2) aus Parameter ka  **
**    in TA-Nummer (z. B. für Scoring-Satzart2) in Parameter ta.             ** 
**    Existiert die KA, so wird 1 retourniert, sonst 0.                      **
******************************************************************************/

_SCORING_EXPORT int lkf_KA_to_TA (const LKF_CONTEXT *pContext, const char *ka, char *ta)
{
	char *ka_entry;
	ka_entry = (char *) KAInArray (pContext->pStamm2001, ka);
	if (ka_entry) {
		memcpy (ta, ka_entry + 24, 4);
		return 1;
	}
	else return 0;
}

/* function KA_to_Name ********************************************************
**    Liefert zur KA-Nummer aus Parameter ka in name (Länge: 20) den Namen   **
**    der Krankenanstalt - oder blank, wenn sie nicht existiert.             **
**    Existiert die KA, so wird 1 retourniert, sonst 0.                      **
******************************************************************************/

_SCORING_EXPORT int lkf_KA_to_Name (const LKF_CONTEXT *pContext, const char *ka, char *name)
{
	char *ka_entry;

	if (!ka) {
		memset (name, ' ', 20);
		name[20] = 0;
		return 0;
	}

	ka_entry = (char *) KAInArray (pContext->pStamm2001, ka);		
	if (ka_entry) {
		memcpy (name, ka_entry + 4, 20);
		name[20] = 0;
		return 1;
	}

	/* V14.2 */
	ka_entry = (char *) KTRInArray (pContext->pStamm2001, ka);		
	if (ka_entry) {
		memcpy (name, ka_entry + 4, 20);
		name[20] = 0;
		return 1;
	}

	memset (name, ' ', 20);
	name[20] = 0;
	return 0;
}

/****************************************************************************************************
** lkf_ABTFC_to_Name (V8.1):                                                                            ** 
**        Liefert zum Abteilungs-Funktionscode den Text                                            **
****************************************************************************************************/
_SCORING_EXPORT int lkf_ABTFC_to_Name (const LKF_CONTEXT *pContext, const char *sAbtFC, char *buf)
{
	FUNK_LIST_STRING *pF = FunkInArray (pContext->pStamm2001, sAbtFC);
	if (pF) {
		strncpy (buf, ((char *)pF) + 6, 50);
		buf[50] = 0;
		return 1;
	}
	else {
		buf[0] = 0;
		return 0;
	}
}


void *StaatInArray (const LKF_CONTEXT *pContext, const char *staat)
{ /* überprüft, ob der Staat im Staatenarray eingetragen ist */
	char trimmed_staat[3];
    
    /* Um den 95-%-Fall (?) Österreich zu beschleunigen, wird das erste Element
       des Arrays ("A") separat zuerst abgefragt */
    if (pContext->modelljahr >= 2012 && !memcmp(staat, "AT", 2) && (!staat[2] || staat[2] == ' ') && pContext->pStamm2001->landAT)
        return pContext->pStamm2001->landAT;
    
    memcpy (trimmed_staat, staat, 3);
    if (!trimmed_staat[2]) {
        trimmed_staat[2] = ' ';
    }
	
	return bsearch (trimmed_staat, pContext->pStamm2001->land_list, pContext->pStamm2001->land_gesamt, sizeof (LAND_LIST_STRING), staat_compare);
}

const LKF_KNOTEN *AddLKFKnoten (STAMM2001 *pStamm, LKF_KNOTEN *pSrc)
{
	int left, right, act, cmp;
	const char *gruppenname;

	if (pStamm->knoten_alloc == pStamm->knoten_gesamt) {	
		pStamm->ppKnoten = LKF_REALLOC (pStamm->pScoringContext, "AddLKFKnoten", pStamm->ppKnoten, (pStamm->knoten_alloc+=50) * sizeof (LKF_KNOTEN *));
        if (!pStamm->ppKnoten) return 0;
	}

	left = -1;
	right = (int) pStamm->knoten_gesamt;

	gruppenname = pSrc->knoten;

	while (left+1 < right) {
		act = (left+right)>>1;

		cmp = memcmp (pStamm->ppKnoten[act]->knoten, gruppenname,8);
		
		if (cmp > 0) right = act;
		else if (cmp < 0) left = act;
		else return pStamm->ppKnoten[act];
	}

	if (right < (int) pStamm->knoten_gesamt)
		memmove (pStamm->ppKnoten + right + 1, pStamm->ppKnoten + right, sizeof(LKF_KNOTEN *) * (pStamm->knoten_gesamt - (unsigned int) right));

	pStamm->ppKnoten[right] = (LKF_KNOTEN *) LKF_MALLOC (pStamm->pScoringContext, "AddLKFKnoten", sizeof (LKF_KNOTEN));
    if (!pStamm->ppKnoten[right]) return 0;
    memcpy (pStamm->ppKnoten[right], pSrc, sizeof (LKF_KNOTEN));

	pStamm->knoten_gesamt++;

	return pStamm->ppKnoten[right];
}

const LKF_KNOTEN *GetAMBKnoten (STAMM2001 *pStamm, const char *gruppenname, char *errbuf) {
    return GetLKFKnoten(pStamm, gruppenname, errbuf);
}

static unsigned int lkf_getNextLine (const char **buffer, unsigned int targetLength, char *target, unsigned char fillChar) {
    unsigned int length;
    char *nextBuf;
    nextBuf = strchr(*buffer, '\n');
    if (!nextBuf) {
        length = (unsigned int) strlen (*buffer);
    }
    else {
        length = (unsigned int) (nextBuf - *buffer);
    }
    if (target && targetLength) {
        if (targetLength < length) {
            memcpy(target, *buffer, targetLength - 1);
            target[targetLength - 1] = 0;
        }
        else {
            memcpy (target, *buffer, length);
            if (fillChar) {
                memset(target + length, fillChar, targetLength - fillChar - 1);
                target[targetLength - 1] = 0;
            }
            else {
                target[length] = 0;
            }
        }
    }
    *buffer = nextBuf ? nextBuf + 1 : 0;
    return length;
}

const LKF_KNOTEN *GetLKFKnotenFromBuffer (unsigned int modelljahr, const char *buffer, LKF_KNOTEN *pKnoten, char *errorBuffer) {
    LKF_KNOTEN knoten;
    const char *nextLine;
    short kc1, kc2;
    short i, knoten_anz = 0;
    char buf[MAX_BUF_LEN+1];
    unsigned int lineLen;

    if (!buffer) {
        if (errorBuffer) {
            strcpy (errorBuffer, "No Source File Buffer provided");
        }
        return 0;

    }
    nextLine = buffer;
    
    lkf_getNextLine (&nextLine, 0, 0, 0); /*' [BAUMINFO]-Zeile*/
    if (nextLine) {
        lkf_getNextLine (&nextLine, MAX_BUF_LEN, buf, 0); /* Gruppe/Kommentar-Zeile*/
        memcpy (knoten.knoten, buf, 8);
        strncpy(knoten.langtext, buf + 9, 250);
        knoten.langtext[250] = 0;
    }
    if (nextLine) {
        lkf_getNextLine (&nextLine, MAX_BUF_LEN, buf, 0); /*' Anzahl der Knoten */
        knoten_anz = (short) simVal(buf, 3);
        lkf_getNextLine (&nextLine, 0, 0, 0); /* [STRUKTUR]-Zeile */
        if (!nextLine) {
            if (errorBuffer) {
                strcpy (errorBuffer, "Die Datei hat weniger als 4 Zeilen und daher keine Knoten");
            }
            return 0;
        }
        else if (knoten_anz <= 0) {
            if (errorBuffer) {
                sprintf (errorBuffer, "Ungueltige Knoten-Anzahl %d", knoten_anz);
            }
            return 0;
        }
    }
    else {
        if (errorBuffer) {
            strcpy (errorBuffer, "Die Datei hat weniger als 3 Zeilen und daher keine Knoten");
        }
        return 0;
    }

    knoten.knoten_anz = knoten_anz;
    kc1 = kc2 = 0;
    for (i = 0; nextLine && i < knoten_anz && i < MAX_KNOTEN; i++) {
        
        lineLen = lkf_getNextLine(&nextLine, 85, buf, ' ');
        knoten.nfl[i] = (short) simVal(buf, 3);                /*' Nachfolger einlesen*/
        knoten.median[i] = simVal(buf + 4, 7);                /*' Median einlesen*/
        knoten.tk_anteil[i] = simVal(buf + 12, 7);            /*' Tageskostenanteil einlesen*/
        knoten.lk_anteil[i] = simVal(buf + 20, 7);            /*' Leistungskostenanteil einlesen*/
        if (modelljahr >= 2025) {
#ifdef SUPPORT_OLD_TREEFILESTRUCTURE
            if (lineLen < 65) { /* voruebergehender Support der alten Split-Struktur */
                memmove(buf + 44, buf + 28, 40);
                sprintf (buf + 28, "%7d %7d", (int) (knoten.lk_anteil[i] * 8 / 10), (int) (knoten.lk_anteil[i] * 2 / 10));
                buf[43] = ' ';
            }
#endif
            knoten.lk_filterSplit[i] = simVal(buf + 28, 7);            /* Filter/Split-MEL */
            knoten.lk_nichtFilter[i] = simVal(buf + 36, 7);            /* Nicht-Filter-MEL */

            knoten.merkmals_typ[i] = (short) simVal(buf + 66, 3);    /*' Merkmal einlesen (numerisch)*/
            CopyUCaseLTrim(knoten.mk[i], buf + 70, 8);                    /*' Merkmal einlesen (alphanumerisch)*/
                
            if (knoten.nfl[i] == -99)
            {
                knoten.knoten_char[i] = (char) ((kc1++) + 'A');
                knoten.melzu_reduziert[i] = simVal (buf + 44, 7); /* V21.1 */
                knoten.vd_u[i] = (short) simVal(buf + 52, 3);            /*' untere VD einlesen*/
                knoten.vd_o[i] = (short) simVal(buf + 56, 3);            /*' obere VD einlesen*/
                knoten.vd_mw[i] = simVal(buf + 60, 5);                /*' VD-Mittelwert einlesen*/
            }
            else
            {
                knoten.knoten_char[i] = (++kc2 % 10) + '0';
                knoten.melzu_reduziert[i] = 0.0;
                CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 60, 5);
                knoten.merkmals_typ[i] = (short) simVal(buf + 66, 3);    /*' Merkmal einlesen (numerisch)*/
                CopyUCaseLTrim(knoten.mk[i], buf + 70, 8);                    /*' Merkmal einlesen */
            }

        }
        else {
            knoten.vd_u[i] = (short) simVal(buf + 36, 3);            /*' untere VD einlesen*/
            knoten.vd_o[i] = (short) simVal(buf + 40, 3);            /*' obere VD einlesen*/
            knoten.vd_mw[i] = simVal(buf + 44, 5);                /*' VD-Mittelwert einlesen*/
            CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 44, 5);
            knoten.merkmals_typ[i] = (short) simVal(buf + 50, 3);    /*' Merkmal einlesen (numerisch)*/
            CopyUCaseLTrim(knoten.mk[i], buf + 54, 8);                    /*' Merkmal einlesen (alphanumerisch)*/
                
            if (knoten.nfl[i] == -99)
            {
                knoten.knoten_char[i] = (char) ((kc1++) + 'A');
                knoten.melzu_reduziert[i] = simVal (buf + 28, 7); /* V21.1 */
            }
            else
            {
                knoten.knoten_char[i] = (++kc2 % 10) + '0';
                knoten.melzu_reduziert[i] = 0.0;
            }
        }
    }
    memcpy (pKnoten, &knoten, sizeof(LKF_KNOTEN));
    return pKnoten;
}

const LKF_KNOTEN *GetLKFKnoten (STAMM2001 *pStamm, const char *gruppenname, char *errbuf)
{
    const LKF_KNOTEN *pKnoten;
    LKF_KNOTEN knoten;
    short i, knoten_anz = 0;
    short kc1, kc2;
    const char *ret;
    STAMM_FILE* fp;
    unsigned int modelljahr, lineLen;
    
    char inbuf[MAX_BUF_LEN+1]; /* V13.0 */
    char buf[MAX_BUF_LEN+1];
    char fileName8[10];

    if (!memcmp(gruppenname, "MEL06.06", 8)) {
        pKnoten = 0;
    }

    lkf_enter_semaphore (0);
    pKnoten = LKFKnotenInArray (pStamm, gruppenname);
    lkf_leave_semaphore (0);
    
    
	if (pKnoten) return pKnoten;

	/* V9.1 - Kleinbuchstaben fuer UNIX-Systeme */
    
    sprintf (fileName8, "%8.8s", gruppenname);

    fp = lkf_stamm_open(pStamm, fileName8, 0);
    
    if (!fp) {
        strcpy (errbuf, "GetLKFKnoten: out of memory");
        return 0;
    }
    
    if (!fp->fp && !fp->fileContent) {
        strcpy (errbuf, fp->error);
        lkf_stamm_close (fp);
        return 0;
    }
	ret = lkf_stamm_line (buf, MAX_BUF_LEN, fp); /*' [BAUMINFO]-Zeile*/
    ret = lkf_stamm_line(buf, MAX_BUF_LEN, fp); /*' Gruppe/Kommentar-Zeile*/
    if (ret) {
        strncpy(knoten.langtext, buf + 9, 250);
        knoten.langtext[250] = 0;
        ret = lkf_stamm_line(buf, MAX_BUF_LEN, fp); /*' Anzahl der Knoten*/
    }
	if (ret) {
		knoten_anz = (short) simVal(buf, 3);					
	    ret = lkf_stamm_line(buf, MAX_BUF_LEN, fp); /*' [STRUKTUR]-Zeile*/
	}

	if (!ret) {
		lkf_stamm_close (fp);
		return 0;
	}

	memcpy (knoten.knoten, gruppenname, 8);
	knoten.knoten_anz = knoten_anz;
    modelljahr = pStamm->nModellJahr;
	kc1 = kc2 = 0;
#ifdef SUPPORT_PRE2017
    if (pStamm->nModellJahr >= 2017)
    { /* Neues Format ab Modelljahr 2017 */
#endif
        for (i = 0; ret && i < knoten_anz && i < MAX_KNOTEN; i++) {
            
            ret = lkf_stamm_line (inbuf, MAX_BUF_LEN, fp);
            if (ret) {
                
                lineLen = expand_string (inbuf, 85, buf); /* V12.1 */
                
                knoten.nfl[i] = (short) simVal(buf, 3);				/*' Nachfolger einlesen*/
                knoten.median[i] = simVal(buf + 4, 7);				/*' Median einlesen*/
                knoten.tk_anteil[i] = simVal(buf + 12, 7);            /*' Tageskostenanteil einlesen*/
                knoten.lk_anteil[i] = simVal(buf + 20, 7);            /*' Leistungskostenanteil einlesen*/
                if (modelljahr >= 2025) {
#ifdef SUPPORT_OLD_TREEFILESTRUCTURE
                    if (lineLen < 65) { /* voruebergehender Support der alten Split-Struktur */
                        memmove(buf + 44, buf + 28, 40);
                        sprintf (buf + 28, "%7d %7d", (int) (knoten.lk_anteil[i] * 8 / 10), (int) (knoten.lk_anteil[i] * 2 / 10));
                        buf[43] = ' ';
                    }
#endif
                    knoten.lk_filterSplit[i] = simVal(buf + 28, 7);            /* Filter/Split-MEL */
                    knoten.lk_nichtFilter[i] = simVal(buf + 36, 7);            /* Nicht-Filter-MEL */
                    CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 60, 5);
                    knoten.merkmals_typ[i] = (short) simVal(buf + 66, 3);    /*' Merkmal einlesen (numerisch)*/
                    CopyUCaseLTrim(knoten.mk[i], buf + 70, 8);                    /*' Merkmal einlesen (alphanumerisch)*/
                        
                    if (knoten.nfl[i] == -99)
                    {
                        knoten.knoten_char[i] = (char) ((kc1++) + 'A');
                        knoten.melzu_reduziert[i] = simVal (buf + 44, 7); /* V21.1 */
                        knoten.vd_u[i] = (short) simVal(buf + 52, 3);            /*' untere VD einlesen*/
                        knoten.vd_o[i] = (short) simVal(buf + 56, 3);            /*' obere VD einlesen*/
                        knoten.vd_mw[i] = simVal(buf + 60, 5);                /*' VD-Mittelwert einlesen*/
                    }
                    else
                    {
                        knoten.knoten_char[i] = (++kc2 % 10) + '0';
                        knoten.melzu_reduziert[i] = 0.0;
                        CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 60, 5);
                        knoten.merkmals_typ[i] = (short) simVal(buf + 66, 3);    /*' Merkmal einlesen (numerisch)*/
                        CopyUCaseLTrim(knoten.mk[i], buf + 70, 8);                    /*' Merkmal einlesen */
                    }

                }
                else {
                    knoten.vd_u[i] = (short) simVal(buf + 36, 3);            /*' untere VD einlesen*/
                    knoten.vd_o[i] = (short) simVal(buf + 40, 3);            /*' obere VD einlesen*/
                    knoten.vd_mw[i] = simVal(buf + 44, 5);                /*' VD-Mittelwert einlesen*/
                    CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 44, 5);
                    knoten.merkmals_typ[i] = (short) simVal(buf + 50, 3);    /*' Merkmal einlesen (numerisch)*/
                    CopyUCaseLTrim(knoten.mk[i], buf + 54, 8);                    /*' Merkmal einlesen (alphanumerisch)*/
                        
                    if (knoten.nfl[i] == -99)
                    {
                        knoten.knoten_char[i] = (char) ((kc1++) + 'A');
                        knoten.melzu_reduziert[i] = simVal (buf + 28, 7); /* V21.1 */
                    }
                    else
                    {
                        knoten.knoten_char[i] = (++kc2 % 10) + '0';
                        knoten.melzu_reduziert[i] = 0.0;
                    }
                }
            }
        }
#ifdef SUPPORT_PRE2017
    }
    else
    {
        for (i = 0; ret && i < knoten_anz && i < MAX_KNOTEN; i++) {
            
            ret = lkf_stamm_line (inbuf, MAX_BUF_LEN, fp);
            if (ret) {
                
                expand_string (inbuf, 70, buf); /* V12.1 */
                
                knoten.nfl[i] = (short) simVal(buf, 3);				/*' Nachfolger einlesen*/
                knoten.median[i] = simVal(buf + 4, 10);				/*' Median einlesen*/
                knoten.vd_u[i] = (short) simVal(buf + 15, 3);			/*' untere VD einlesen*/
                knoten.vd_o[i] = (short) simVal(buf + 19, 3);			/*' obere VD einlesen*/
                knoten.vd_mw[i] = simVal(buf + 23, 5);				/*' VD-Mittelwert einlesen*/
                knoten.tk_anteil[i] = simVal(buf + 29, 5);			/*' Tageskostenanteil einlesen*/
                knoten.lk_anteil[i] = simVal(buf + 35, 5);			/*' Leistungskostenanteil einlesen*/
                CopyUCaseLTrim (knoten.splitmerkmal[i], buf + 41, 5);
                knoten.merkmals_typ[i] = (short) simVal(buf + 47, 3);	/*' Merkmal einlesen (numerisch)*/
                CopyUCaseLTrim(knoten.mk[i], buf + 51, 8);					/*' Merkmal einlesen (alphanumerisch)*/
                
                if (knoten.nfl[i] == -99)
                {
                    knoten.knoten_char[i] = (char) ((kc1++) + 'A');
                    knoten.melzu_reduziert[i] = simVal (buf + 41, 5); /* V13.0 */
                }
                else 
                {
                    knoten.knoten_char[i] = (++kc2 % 10) + '0';
                    knoten.melzu_reduziert[i] = 0.0;
                }
            }
        }
    }
#endif

	if (i < knoten_anz) {
		lkf_stamm_close (fp);
		return 0; /* FEHLER - Gruppe kann nicht gelesen werden, da er zuviele Knoten hat */ 
	}

    if (pStamm->nModellJahr < 2017)
    {
        ret = lkf_stamm_line (buf, MAX_BUF_LEN, fp); /*' [INTERVALL]-Zeile*/
        if (ret) ret = lkf_stamm_line (buf, MAX_BUF_LEN, fp); /*' Alters-Intervall*/
    
        if (ret) {
            for (i = 0; i < 6; i++) knoten.Intervall[0][i] = (short) simVal(buf + i*4, 3);
        
            ret = lkf_stamm_line (buf, MAX_BUF_LEN, fp); /*' Mel-Intervall*/
            if (ret) for (i = 0; i < 6; i++) knoten.Intervall[1][i] = (short) simVal(buf + i*4, 3);
        }
    }
    
	lkf_stamm_close (fp);

	if (!ret) return 0;

	lkf_enter_semaphore (0);
	pKnoten = AddLKFKnoten (pStamm, &knoten);
	lkf_leave_semaphore (0);

	return pKnoten;
}

/***************************************************************************
** lkf_get_spez_anz: Liefert die Anzahl Einträge in der SpezList          **
***************************************************************************/

_SCORING_EXPORT unsigned int lkf_get_spez_anz (const LKF_CONTEXT *pContext)
{
	if (pContext->spez_gesamt == 0) return pContext->pStamm2001->spez_gesamt;
	else return pContext->spez_gesamt;
}

/***************************************************************************
** lkf_get_kost_anz: Liefert die Anzahl Einträge in der KostenträgerListe **
***************************************************************************/

_SCORING_EXPORT unsigned int lkf_get_kost_anz (const LKF_CONTEXT *pContext)
{
	if (pContext->kost_gesamt == 0) return pContext->pStamm2001->kost_gesamt;
	else return pContext->kost_gesamt;
}

/***************************************************************************
** lkf_get_melka_anz: Liefert die Anzahl Einträge in der MELKA-Liste      **
***************************************************************************/

_SCORING_EXPORT unsigned int lkf_get_melka_anz (const LKF_CONTEXT *pContext)
{
	if (pContext->mel_ka_gesamt == 0) return pContext->pStamm2001->mel_ka_gesamt;
	else return pContext->mel_ka_gesamt;
}

/***************************************************************************
** lkf_get_meltkl_anz: Liefert die Anzahl Einträge in der MELKA-Liste      **
***************************************************************************/

_SCORING_EXPORT unsigned int lkf_get_meltkl_anz (const LKF_CONTEXT *pContext)
{ /* V2022.1 */
    if (pContext->mel_tkl_gesamt == 0) return pContext->pStamm2001->mel_tkl_gesamt;
    else return pContext->mel_tkl_gesamt;
}


/***************************************************************************
** lkf_stamm_line_spez: Liefert eine Zeile der Spez-List.                 **
***************************************************************************/

_SCORING_EXPORT int lkf_stamm_line_spez  (const LKF_CONTEXT *pContext, unsigned int i, char *line)
{ /* V2022.1 not supported */
    if (line) *line = 0;
	return 0;
}

/***************************************************************************
** lkf_stamm_line_kost: Liefert eine Zeile der Kostenträger-Liste         **
***************************************************************************/

_SCORING_EXPORT int lkf_stamm_line_kost  (const LKF_CONTEXT *pContext, const char *kanr, unsigned int i, char *line)
{ /* V2022.1 not supported */
    if (line) *line = 0;
    return 0;
}

/***************************************************************************
** lkf_stamm_line_melka: Liefert eine Zeile der MELKA-Liste               **
***************************************************************************/

_SCORING_EXPORT int lkf_stamm_line_melka (const LKF_CONTEXT *pContext, unsigned int i, char *line)
{/* V2022.1 not supported */
    if (line) *line = 0;
    return 0;
}

_SCORING_EXPORT unsigned int lkf_get_stamm_path (const LKF_CONTEXT *pContext, const char *filename, char *buf, unsigned int buflen)
{ 
	unsigned int len;
	len = (unsigned int) strlen (pContext->pStamm2001->input_path);
	if (filename) len += (unsigned int) strlen (filename);
	if (buflen > len) {
		strcpy (buf, pContext->pStamm2001->input_path);
		if (filename) strcat (buf, filename);
	}
	return len + 1;
}

/******************************************************************************************
** Hardcodierte MEL-Listen                                                               **
******************************************************************************************/

static int CreateMelIndexList (STAMM2001 *pStamm, int **ppDestIndexList, const char **psSourceMels)
{ /* V13.0 */
	int i;
	for (i = 0; psSourceMels[i]; i++);
	*ppDestIndexList = (int *) LKF_CALLOC (pStamm->pScoringContext, "CreateMelIndexList", (unsigned long)i+1, sizeof(int));
    if (*ppDestIndexList == 0) {
        return LKF_OUTOFMEMORY_ERROR;
    }
	for (i = 0; psSourceMels[i]; i++)
	{
		
		(*ppDestIndexList)[i] = FindMelIndexStamm (pStamm, psSourceMels[i]);
	}
	(*ppDestIndexList)[i] = 0;
    return 0;
}

int MELInIndexList (const LKF_CONTEXT *pContext, unsigned int nIndexList, int nMelIndex)
{
	unsigned int i = 0;
	int *indexlist = pContext->pStamm2001->ppMelIndexLists[nIndexList];
	while (indexlist[i] != 0 /* V17.1 */)
	{
		if (indexlist[i++] == (int) nMelIndex)
			return 1;
	}
	return 0;
}

int IsConstMEL (const LKF_CONTEXT *pContext, unsigned int nConstMelId, int nMelIndex)
{
    /* V22.5: false wenn MEL ungueltig */
	return nMelIndex >= 0 && (pContext->pStamm2001->ppMelIndexLists[nConstMelId>>8][nConstMelId%256] == nMelIndex);
}



int StammInitMELIndexLists (STAMM2001 *pStamm, int nModellJahr)
{
	static const char *melarray_ib3_new[] = {"GL050", "GL040", "GL030", 0};
	static const char *melarray_ib3_new_2013[] = {"GL035", "GL050", "GL040", "GL030", 0}; /* V17.0 */
	static const char *melarray_ib3_new_2014[] = {"GL035", "GL050", 0}; /* V18.0 */
	
    static const char *melarray_ib11_new[] = {"HS010", 0};
    static const char *melarray_ib11_new_2025[] = {"HS015", 0};
    
    static const char *melarray_ib13_new[] = {"FV030", "FV020", "FV040", "FV010", "FV050", "FV060",
	                                          "FV100", "FV080", "AV010", "FV090", "FV120", "FV110",
											  "ZN380", "AC010", "DL020", "DH010", "ZZ030", 0};
	static const char *melarray_ib13_new_2013[] = /* V17.0 */ 
											 {"FV045", "FV010", "FV100", "FV080", "AV010", "FV090", 
											  "FV120", "FV110", "ZN380", "AC010", "DL020", "DH010", 
											  "FV020", "FV030", "FV040", "FV050", "FV060", "ZZ030", 0};
	static const char *melarray_ib13_new_2014[] = /* V18.0 */ 
											 {"FV045", "FV010", "FV100", "FV080", "AV010", "FV090", 
											  "FV120", "FV110", "ZN380", "AC010", "DL020", "DH010", 
											  "ZZ030", 0};
    static const char *melarray_ib13_new_2025[] = /* V18.0 */
                                             {"FV046", "FV047", "FV010", "FV100", "FV080", "AV010", "FV090",
                                              "FV120", "FV110", "ZN380", "AC010", "DL020", "DH010",
                                              "ZZ030", 0};


	static const char *melarray_pu5_new[] = {"DH010", 0};
	static const char *melarray_pb4_new[] = {"DL020", 0};
	
    static const char *melarray_pb5_new[] = {"HS010", "HS020", "DH010", 0};
    static const char *melarray_pb5_new_2025[] = {"HS015", "DH010", 0};
    
	static const char *melarray_plaus_psych1_new[] = {"AM060", "AM070", "AM080", 0};
	static const char *melarray_plaus_psych2_new[] = {"AM090", "AM100", 0};
	static const char *melarray_constlist1_new[] = {"AM030", "AM020", "AA040", "AM040", "AM050", "LH020", "DB020", 0};
	static const char *melarray_constlist2_new[] = {"AM060", "AM070", "AM080", "AM090", "AM110" /* V15.0 */, 0};
	static const char *melarray_constlist3_new[] = {"ZZ020", "AJ140", "AK010", "LH010", "LH021", 0}; /* V13.3, erweitert V14.0 */

	static const char *melnichtreduziert_new[] = {"FA040", "GF060", "HP050", "HP060", "HP070", 0};
	static const char *melnichtreduziert_new_2014[] = {"FA040", "GF060", 0};

	static const char *melarray_constlist4_new[] = {"AM120", 0}; /* V17.0 */
    static const char *melarray_constlist4_2017[] = {"AM120", "AM130", "AM100" /* V21.4 */, 0}; /* V21.0 */
    
    static const char *melarray_mel50_new[] = {"BG020", "BG030", "GE010", "HH020", "QE010", 00}; /* V17.0 */

    static const char *melarray_constlist5[] = {"ZZ050", "AM140", "AM150", 0}; /* V22.0; AM140+AM150 ab V2022.0 */

    int ret = 0;
    
    if (nModellJahr >= 2025) {
        ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB3], melarray_ib3_new_2014);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB13], melarray_ib13_new_2025);
    }
    else if (nModellJahr >= 2014)
    { /* V18.0 */
        ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB3], melarray_ib3_new_2014);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB13], melarray_ib13_new_2014);
    }
    else if (nModellJahr >= 2013)
    { /* V17.0 */
        ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB3], melarray_ib3_new_2013);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB13], melarray_ib13_new_2013);
    }
    else
    {
        ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB3], melarray_ib3_new);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB13], melarray_ib13_new);
    }
    
    if (nModellJahr >= 2025) {
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB11], melarray_ib11_new_2025);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_PB5], melarray_pb5_new_2025);
    }
    else {
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_IB11], melarray_ib11_new);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_PB5], melarray_pb5_new);
    }
    
    
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_PU5], melarray_pu5_new);
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NPPLAUS_PB4], melarray_pb4_new);
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_PLAUS_PSYCH1], melarray_plaus_psych1_new);
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_PLAUS_PSYCH2], melarray_plaus_psych2_new);
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST1], melarray_constlist1_new);
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST2], melarray_constlist2_new);
    
    if (pStamm->nModellJahr >= 2014)
    { /* V18.0 */
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NICHTREDUZIERT], melnichtreduziert_new_2014);
    }
    else
    {
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_NICHTREDUZIERT], melnichtreduziert_new);
    }
    
    if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST3], melarray_constlist3_new);
    
    if (pStamm->nModellJahr >= 2017) {
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[MEL_LEIST50_LIST], melarray_mel50_new);
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST4], melarray_constlist4_2017);
        if (pStamm->nModellJahr >= 2018) {
            if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST5], melarray_constlist5);
        }
    }
    else {
        if (!ret) ret = CreateMelIndexList (pStamm, &pStamm->ppMelIndexLists[CONSTMEL_LIST4], melarray_constlist4_new);
    }
    return ret;
}

_SCORING_EXPORT int lkf_stamm_getMelCount (const LKF_CONTEXT *pContext)
{ /* V2024.1 */
    STAMM2001 *pStamm;
    pStamm = pContext->pStamm2001;
    if (!pStamm) {
        return -1;
    }
    return (int) pStamm->mel_list_gesamt;
}

_SCORING_EXPORT int lkf_stamm_getDiagCount (const LKF_CONTEXT *pContext)
{ /* V2024.1 */
    STAMM2001 *pStamm;
    pStamm = pContext->pStamm2001;
    if (!pStamm) {
        return -1;
    }
    return (int) pStamm->icd_gesamt;
}

_SCORING_EXPORT int lkf_stamm_getMelIndex (const LKF_CONTEXT *pContext, const char *mel) {
    MEL_ARRAY_STRUC *arr = FindMel(pContext, mel);
    if (!arr) return -1;
    return (int) (arr - pContext->pStamm2001->mel_array);
}

_SCORING_EXPORT int lkf_stamm_getDiagIndex (const LKF_CONTEXT *pContext, const char *diag) {
    char diag6[8];
    ICD_LIST_STRING* arr;
    sprintf (diag6, "%-6.6s", diag);
    arr = FindDiag6(pContext->pStamm2001, diag6);
    if (!arr) return -1;
    return (int) (arr - pContext->pStamm2001->icd_list);
}

_SCORING_EXPORT void lkf_stamm_getMelField (const LKF_CONTEXT *pContext, unsigned int melIndex, unsigned int melField, char *buf, unsigned int buflen) {
    MEL_ARRAY_STRUC *pMelDef;
    const char *r;
    unsigned int rl;
    char intBuf[20];
    pMelDef = pContext->pStamm2001->mel_array + melIndex;
    
    switch (melField) {
        case LKFSTAMM_MELFIELD_MEL:
            r = pMelDef->MEL;
            rl = 5;
            break;
        case LKFSTAMM_MELFIELD_TEXT:
            r = pMelDef->sText;
            rl = 100;
            break;
        case LKFSTAMM_MELFIELD_SEITE:
            r = &pMelDef->cSeite;
            rl = 1;
        case LKFSTAMM_MELFIELD_AMBGRUPPE:
            r = pMelDef->sAmbGruppe;
            rl = 8;
            break;
        case LKFSTAMM_MELFIELD_STATGRUPPE:
            r = pMelDef->sGruppe;
            rl = 8;
            break;
        case LKFSTAMM_MELFIELD_GESCHL:
            r = &pMelDef->cGeschlecht;
            rl = 1;
            break;
        case LKFSTAMM_MELFIELD_LGR:
            r = pMelDef->sLGR;
            rl = 5;
            break;
        case LKFSTAMM_MELFIELD_AMBLGR:
            r = pMelDef->sAmbLeistGrp;
            rl = 5;
            break;
        case LKFSTAMM_MELFIELD_MEL_WARN:
            sprintf (intBuf, "%d", pMelDef->mel_warn);
            r = intBuf;
            rl = 6;
            break;
        case LKFSTAMM_MELFIELD_MEL_ERR:
            sprintf (intBuf, "%d", pMelDef->mel_anz);
            r = intBuf;
            rl = 6;
            break;
        case LKFSTAMM_MELFIELD_MEL_TAGWARN:
            sprintf (intBuf, "%d", pMelDef->tag_mel_warn);
            r = intBuf;
            rl = 6;
            break;
        case LKFSTAMM_MELFIELD_MEL_TAGERR:
            sprintf (intBuf, "%d", pMelDef->tag_mel_anz);
            r = intBuf;
            rl = 6;
            break;
        case LKFSTAMM_MELFIELD_MEL_AMBWARN:
            sprintf (intBuf, "%d", pMelDef->amb_melanz_warn);
            r = intBuf;
            rl = 6;
            break;
        case LKFSTAMM_MELFIELD_MEL_AMBERR:
            sprintf (intBuf, "%d", pMelDef->amb_melanz_error);
            r = intBuf;
            rl = 6;
            break;
        default:
            rl = 0;
            r = "";
    }
    
    if (buflen > 0) {
        if (rl > 0) {
            if (buflen < rl) {
                rl = buflen;
            }
            strncpy(buf, r, buflen);
            buf[rl] = 0;
        }
    }
}

static void lkf_build_melmelinfo (STAMM2001 *pStamm) {
    
#define LKF_MELMELINFO_EXTEND 20
    
    unsigned int i;
    MEL_ARRAY_STRUC *pLeft, *pRight;
    MEL_MEL_STRING *pCurrent;
    char melmelType;
    char *melmelBuffer;
    unsigned int melmelRemainingBytes;
    
    melmelRemainingBytes = 1024000;
    melmelBuffer = (char *) LKF_MALLOC(pStamm->pScoringContext, "lkf_build_melmelinfo", melmelRemainingBytes);
    
    pStamm->melmelInfoBuffer = melmelBuffer;
    
    for (i = 0, pCurrent = pStamm->mel_mel; i < pStamm->mel_mel_gesamt; i++, pCurrent++) {
    
        pLeft = GetStammMel (pStamm, (*pCurrent));
        pRight = GetStammMel(pStamm, (*pCurrent) + 5);
        
        if (!pLeft || !pRight) {
            continue;
        }
        
        if (!pLeft->melmelInfoAlloc) {
            if (melmelRemainingBytes > LKF_MELMELINFOSTRING_LEN * LKF_MELMELINFO_EXTEND) {
                pLeft->melmelInfoAlloc = LKF_MELMELINFO_EXTEND;
                pLeft->melmelInfo = melmelBuffer;
                melmelBuffer += pLeft->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
                melmelRemainingBytes -= pLeft->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
            }
            else {
                continue; // should not be reached
            }
        }
        else if (pLeft->melmelInfoCount == pLeft->melmelInfoAlloc) {
            if (melmelRemainingBytes > LKF_MELMELINFOSTRING_LEN * (pLeft->melmelInfoAlloc + LKF_MELMELINFO_EXTEND)) {
                const char *prev = pLeft->melmelInfo;
                pLeft->melmelInfo = melmelBuffer;
                memcpy (melmelBuffer, prev, pLeft->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN);
                pLeft->melmelInfoAlloc += LKF_MELMELINFO_EXTEND;
                melmelBuffer += pLeft->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
                melmelRemainingBytes -= pLeft->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
            }
            else {
                continue; // should not be reached
            }
        }
        
        melmelType = (*pCurrent)[12];
        if (melmelType == 'S') {
            melmelType = 'L';
        }
        
        if (pLeft->melmelInfoCount) {
            sprintf (pLeft->melmelInfo + pLeft->melmelInfoCount * LKF_MELMELINFOSTRING_LEN - 1, ";%5.5s:%2.2s%c", (*pCurrent) + 5, (*pCurrent) + 10, melmelType);
        }
        else {
            sprintf (pLeft->melmelInfo + pLeft->melmelInfoCount * LKF_MELMELINFOSTRING_LEN, "%5.5s:%2.2s%c", (*pCurrent) + 5, (*pCurrent) + 10, melmelType);
        }
        
        pLeft->melmelInfoCount++;
        
        if (pRight == pLeft) {
            continue;
        }
        if (melmelType == 'L') {
            melmelType = 'W';
        }

        if (!pRight->melmelInfoAlloc) {
            if (melmelRemainingBytes > LKF_MELMELINFOSTRING_LEN * LKF_MELMELINFO_EXTEND) {
                pRight->melmelInfoAlloc = LKF_MELMELINFO_EXTEND;
                pRight->melmelInfo = melmelBuffer;
                melmelBuffer += pRight->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
                melmelRemainingBytes -= pRight->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
            }
            else {
                continue; // should not be reached
            }
        }
        else if (pRight->melmelInfoCount == pRight->melmelInfoAlloc) {
            if (melmelRemainingBytes > LKF_MELMELINFOSTRING_LEN * (pRight->melmelInfoAlloc + LKF_MELMELINFO_EXTEND)) {
                const char *prev = pRight->melmelInfo;
                pRight->melmelInfo = melmelBuffer;
                memcpy (melmelBuffer, prev, pRight->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN);
                pRight->melmelInfoAlloc += LKF_MELMELINFO_EXTEND;
                melmelBuffer += pRight->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
                melmelRemainingBytes -= pRight->melmelInfoAlloc * LKF_MELMELINFOSTRING_LEN;
            }
            else {
                continue; // should not be reached
            }
        }
        if (pRight->melmelInfoCount > 0) {
            sprintf (pRight->melmelInfo + pRight->melmelInfoCount * LKF_MELMELINFOSTRING_LEN - 1, ";%5.5s:%2.2s%c", (*pCurrent), (*pCurrent) + 10, melmelType);
        }
        else {
            sprintf (pRight->melmelInfo + pRight->melmelInfoCount * LKF_MELMELINFOSTRING_LEN, "%5.5s:%2.2s%c", (*pCurrent), (*pCurrent) + 10, melmelType);
        }
        pRight->melmelInfoCount++;
    }
    
    
}

_SCORING_EXPORT unsigned int lkf_stamm_getMelMel (LKF_CONTEXT *pContext, unsigned int melIndex, char *buf, unsigned int buflen)
{
    STAMM2001 *pStamm;
    MEL_ARRAY_STRUC *pMel;
    
    pStamm = pContext->pStamm2001;
    if (!pStamm) {
        return 0;
    }
    
    if (!pStamm->melmelInfoBuffer) {
        lkf_build_melmelinfo(pStamm);
    }
    
    pMel = GetMELArrayString(pContext->pStamm2001, melIndex);
    
    if (!pMel || pMel->melmelInfoCount == 0 || pMel->melmelInfo == 0) {
        return 0;
    }
    
    if (buflen > pMel->melmelInfoCount * LKF_MELMELINFOSTRING_LEN) {
        memcpy (buf, pMel->melmelInfo, pMel->melmelInfoCount * LKF_MELMELINFOSTRING_LEN);
        buf[pMel->melmelInfoCount * LKF_MELMELINFOSTRING_LEN] = 0;
    }
    else if (buflen > 0) {
        buf[0] = 0;
    }
    
    return pMel->melmelInfoCount;
}

_SCORING_EXPORT unsigned int lkf_stamm_getMelDiags (const LKF_CONTEXT *pContext, unsigned int melIndex, char *buf, unsigned int buflen) {
    MEL_ARRAY_STRUC *pMel;
    STAMM2001 *pStamm;
    MEL_DIAG_STRUC6 *pMELDiag;
    unsigned int i, count, lastPos;
    ICD_LIST_STRING *pMin, *pMax;
    char maxDiag[7];
    
    pStamm = pContext->pStamm2001;
    if (!pStamm) {
        return 0;
    }
    
    pMel = GetMELArrayString(pContext->pStamm2001, melIndex);
    
    if (!pMel || pMel->mel_diag_ug == 0) {
        return 0;
    }
    
    count = 0;
    lastPos = 0;
    for (i = pMel->mel_diag_ug; i < pContext->pStamm2001->mel_diag_gesamt && i <= pMel->mel_diag_og; i++) {
        
        pMELDiag = GetMELDiag6 (pStamm, i);
        
        pMin = FindDiag3(pStamm, pMELDiag->hdg_min);
        pMax = FindDiag3(pStamm, pMELDiag->hdg_max);
        
        if (pMELDiag->hdg4_min != ' ') {
            char minDiag[6];
            sprintf (minDiag, "%3.3s.%c", pMELDiag->hdg_min, pMELDiag->hdg4_min);
            while (memcmp (pMin, minDiag, 5) < 0) ++pMin;
        }
            
        if (pMELDiag->hdg4_max != ' ') {
            sprintf (maxDiag, "%3.3s.%cZ", pMELDiag->hdg_max, pMELDiag->hdg4_max);
        }
        else {
            sprintf (maxDiag, "%3.3s.ZZ", pMELDiag->hdg_max);
        }
        while (memcmp (pMax, maxDiag, 5) < 0) ++pMax;

        if (pMax >= pMin) {
            count += (unsigned int) (pMax - pMin + 1);
            if (buflen  > count * 7) {
                for (; pMin<=pMax; pMin++) {
                    memcpy (buf + lastPos, pMin, 6);
                    buf[lastPos + 6] = ';';
                    lastPos += 7;
                }
            }
        }
    }
    buf[lastPos-1] = 0;
    return count;
}

_SCORING_EXPORT void lkf_stamm_getDiagField (const LKF_CONTEXT *pContext, unsigned int diagIndex, unsigned int diagField, char *buf, unsigned int buflen) {
    const char *pDiagDef;
    const char *r;
    unsigned int rl;
    pDiagDef = (const char *) (pContext->pStamm2001->icd_list + diagIndex);

    switch (diagField) {
        case LKFSTAMM_DIAGFIELD_DIAG:
            r = pDiagDef;
            rl = 6;
            break;
        case LKFSTAMM_DIAGFIELD_GRP:
            r = pDiagDef + 9;
            rl = 8;
            break;
        case LKFSTAMM_DIAGFIELD_DGR:
            r = pDiagDef + 18;
            rl = 5;
            break;
        case LKFSTAMM_DIAGFIELD_TEXT:
            r = pDiagDef + 50;
            rl = 80;
            break;
        default:
            rl = 0;
            r = "";
    }
    if (buflen > 0) {
        if (rl > 0) {
            if (buflen < rl) {
                rl = buflen;
            }
            strncpy(buf, r, buflen);
            buf[rl] = 0;
        }
    }
}

_SCORING_EXPORT const LKF_KNOTEN *lkf_stamm_getGroup (LKF_CONTEXT *pContext, const char *group) {
    return GetLKFKnoten(pContext->pStamm2001, group, (char *) pContext->errortext);
}

_SCORING_EXPORT LKF_KNOTEN *lkf_stamm_getGroupFromBuffer (LKF_CONTEXT *pContext, unsigned int modelljahr, const char *buffer, char *errorBuffer) {
    
    LKF_KNOTEN *pKnoten = (LKF_KNOTEN *) LKF_MALLOC (pContext, "lkf_stamm_getGroupFromBuffer", sizeof(LKF_KNOTEN));
    memset(pKnoten, 0, sizeof(LKF_KNOTEN));
    if (GetLKFKnotenFromBuffer(modelljahr, buffer, pKnoten, errorBuffer)) {
        return pKnoten;
    }
    return 0;
}

_SCORING_EXPORT void lkf_stamm_freeGroupFromBuffer (LKF_CONTEXT *pContext, LKF_KNOTEN *pKnoten) {
    LKF_FREE (pContext, "lkf_stamm_freeGroupFromBuffer", pKnoten);
}

_SCORING_EXPORT int lkf_stamm_getGroupInfo (LKF_KNOTEN *pKnoten, int fieldNo, char *buf, unsigned int buflen) 
{
    unsigned int langtextLen;
    switch (fieldNo) {
        case 0:
            if (buf && buflen >= 4) {
                sprintf (buf, "%3d", pKnoten->knoten_anz);
            }
            return 4;
        case 1:
            if (buf && buflen >= 8) {
                memcpy(buf, pKnoten->knoten, 8);
                if (buflen > 8) {
                    buf[8] = 0;
                }
            }
            return 8;
            
        case 2:
            langtextLen = (unsigned int) strlen(pKnoten->langtext);
            if (buf) {
                if (langtextLen > buflen) {
                    memcpy(buf, pKnoten->langtext, buflen);
                }
                else {
                    memcpy(buf, pKnoten->langtext, langtextLen);
                    if (buflen > langtextLen) {
                        buf[langtextLen] = 0;
                    }
                }
            }
            return langtextLen;
        default:
            return 0;
    }
}




unsigned int lkf_stamm_getGroupLine (LKF_CONTEXT *pContext, const LKF_KNOTEN *pKnoten, unsigned int index, char *buf, unsigned int buflen) {
    char localBuf[1024];
    int pos;
    
    if (pKnoten->nfl[index] != -99) {
        char splitmerkmal[6];
        int v;
        sprintf (splitmerkmal, "%5.5s", pKnoten->splitmerkmal[index]);
        pos = sprintf (localBuf, "S|%d|%d|%d|%s", pKnoten->nfl[index] - 1, pKnoten->nfl[index], pKnoten->merkmals_typ[index], splitmerkmal);
        v = atoi(splitmerkmal);
        
        switch (pKnoten->merkmals_typ[index]) {
            case 6: /* ALTER */
                if (splitmerkmal[0] == '-') {
                    if (v == 0) {
                        pos += sprintf(localBuf + pos, "|=|0");
                    }
                    else {
                        pos += sprintf(localBuf + pos, "|<=|%d", -v);
                    }
                }
                else {
                    pos += sprintf(localBuf + pos, "|>|%d", v);
                }
            default:
                if (v != 0 && (v < -6 || v > 6)) {
                    if (v < 0) v = -v;
                    pos += sprintf(localBuf + pos, "|><|%d|%d", pKnoten->Intervall[0][v], pKnoten->Intervall[1][v]);
                }
        }
    }
    else if (pContext->modelljahr >= 2025) {
        pos = sprintf (localBuf, "L|%c|%.1f|%.1f|%.1f|%.1f|%d|%d|%.2f|%.1f|%.1f", pKnoten->knoten_char[index], pKnoten->tk_anteil[index], pKnoten->lk_anteil[index], pKnoten->melzu_reduziert[index], pKnoten->median[index], pKnoten->vd_u[index], pKnoten->vd_o[index], pKnoten->vd_mw[index], pKnoten->lk_filterSplit[index], pKnoten->lk_nichtFilter[index]);
    }
    else {
        pos = sprintf (localBuf, "L|%c|%.1f|%.1f|%.1f|%.1f|%d|%d|%.2f", pKnoten->knoten_char[index], pKnoten->tk_anteil[index], pKnoten->lk_anteil[index], pKnoten->melzu_reduziert[index], pKnoten->median[index], pKnoten->vd_u[index], pKnoten->vd_o[index], pKnoten->vd_mw[index]);
    }
    
    if (pos < (int) buflen) {
        memcpy (buf, localBuf, pos + 1);
    }
    return (unsigned int) pos;
}

_SCORING_EXPORT LKF_METADATA *lkf_alloc_stamm (unsigned int year, const char *path, unsigned int errorLength, char *error)
{
    return lkf_alloc_stamm2(0, year, path, errorLength, error);
}

_SCORING_EXPORT LKF_METADATA *lkf_alloc_stamm2 (LKF_CONTEXT *pContext, unsigned int year, const char *path, unsigned int errorLength, char *error)
{
    LKF_METADATA *pMetaData;
    STAMM2001 *pStamm2001;
    const LKF_SATZART_ERRORDEF **ppErrors;
    char errorBuffer[2048];

    pStamm2001 = allocStamm2001 (pContext, path, year);
    if (!pStamm2001) {
        if (error) {
            strncpy (error, "cannot alloc metadata - out of memory", errorLength);
        }
        return 0;
    }
    if (read_2001(pStamm2001, path, errorBuffer, year) == 0) {
        *errorBuffer = 0;
    }
    else if (!*errorBuffer) {
        sprintf (errorBuffer, "cannot read private metadata from %s", path);
    }
    ppErrors = GetAllMBDSErrors (year);
    if (!ppErrors) {
        sprintf (errorBuffer, "mbds errors missing for model %d", year);
    }
    else {
        LKF_CONTEXT *pContext = lkf_alloc_context(year, 0, errorBuffer);
        if (!pContext) {
            if (error) {
                strncpy (error, errorBuffer, errorLength);
                if (strlen(errorBuffer) >= errorLength) {
                    error[errorLength-1] = 0;
                }
            }
            return 0;
        }
        pStamm2001->pMBDSErrors = lkf_alloc_errorgroup (pContext, ppErrors);
        pStamm2001->pAllErrors = lkf_create_errorinfo (pContext, LKF_ERRORFILTER_ALL);
        lkf_errorinfo_sort (pStamm2001->pAllErrors);
        lkf_free_context(pContext);
    }
    
    if (*errorBuffer) {
        if (error) {
            strncpy (error, errorBuffer, errorLength);
        }
        freeStamm2001(pStamm2001);
        return 0;
    }
    else {
        *error = 0;
    }
            
    pMetaData = LKF_MALLOC (pContext, "lkf_alloc_stamm", sizeof(LKF_METADATA));
    pMetaData->pStamm = pStamm2001;
    pMetaData->modelYear = year;
    return pMetaData;
}


static const LKF_MEL_CUSTOM_CHECK *lkf_internal_getMelCustomCheck (const STAMM2001 *pStamm, unsigned int melIndex) {
    if (pStamm && pStamm->mel_list_gesamt > melIndex) {
        return pStamm->mel_array[melIndex].pCustomCheck;
    }
    return 0;
}

static void lkf_internal_getMelCustomCheckData (const STAMM2001 *pStamm, const LKF_MEL_CUSTOM_CHECK *pCustomCheck, char *pError, const char **ppDescription, const LKF_MEL_CUSTOM_CHECK **ppNextTrue, const LKF_MEL_CUSTOM_CHECK **ppNextFalse) {
    if (pError) {
        if (pCustomCheck->pError) {
            lkf_getErrorIdent (0, pCustomCheck->pError, 6, pError);
        }
        else {
            *pError = 0;
        }
    }
    if (ppDescription) {
        if (pCustomCheck->description) {
            *ppDescription = pCustomCheck->description;
        }
        else {
            *ppDescription = lkf_get_function_Description(pCustomCheck->checkFunction);
        }
    }
    if (ppNextTrue) {
        *ppNextTrue = pCustomCheck->nextTrue;
    }
    if (ppNextFalse) {
        *ppNextFalse = pCustomCheck->nextFalse;
    }
}


_SCORING_EXPORT const LKF_MEL_CUSTOM_CHECK *lkf_context_getMelCustomCheck (const LKF_CONTEXT *pContext, unsigned int melIndex) {
    return lkf_internal_getMelCustomCheck (pContext->pStamm2001, melIndex);
}

_SCORING_EXPORT const LKF_MEL_CUSTOM_CHECK *lkf_stamm_getMelCustomCheck (const LKF_METADATA *pMetaData, unsigned int melIndex) {
    return lkf_internal_getMelCustomCheck (pMetaData->pStamm, melIndex);
}

_SCORING_EXPORT void lkf_context_getMelCustomCheckData (const LKF_CONTEXT *pContext, const LKF_MEL_CUSTOM_CHECK *pCustomCheck, char *pError, const char **ppDescription, const LKF_MEL_CUSTOM_CHECK **ppNextTrue, const LKF_MEL_CUSTOM_CHECK **ppNextFalse) {
    lkf_internal_getMelCustomCheckData (pContext->pStamm2001, pCustomCheck, pError, ppDescription, ppNextTrue, ppNextFalse);
}

_SCORING_EXPORT void lkf_stamm_getMelCustomCheckData (const LKF_METADATA *pMetaData, const LKF_MEL_CUSTOM_CHECK *pCustomCheck, char *pError, const char **ppDescription, const LKF_MEL_CUSTOM_CHECK **ppNextTrue, const LKF_MEL_CUSTOM_CHECK **ppNextFalse) {
    lkf_internal_getMelCustomCheckData (pMetaData->pStamm, pCustomCheck, pError, ppDescription, ppNextTrue, ppNextFalse);
}

_SCORING_EXPORT void lkf_free_stamm (LKF_METADATA *pMetaData) {
    LKF_CONTEXT *pContext = pMetaData->pStamm ? pMetaData->pStamm->pScoringContext : 0;
    freeStamm2001(pMetaData->pStamm);
    LKF_FREE (pContext, "lkf_free_stamm", pMetaData);
}

