****************************************************************************************************
*                                           PROGRAM OVERVIEW
****************************************************************************************************
*
* PROGRAM: combo.sas  
*
* Created (mm/dd/yyyy): 12/19/2014
* Last modified: 11/12/2018
* Version: 2.11
*
*--------------------------------------------------------------------------------------------------
* PURPOSE:
*   This macro will create combination items.                                        
*   
*  Program inputs:                                                                                   
*     - A file containing NDC codes to extract 
*     - A file containing diagnosis codes to extract 
*     - A file containing procedure codes to extract
*     - A file containing laboratory codes to extract
*
*  Program outputs:                                                                                                                                       
*     - A file containing combination items that behave like NDCs 
*     - A file containing combination items that behave like NDCs  
*     - A file containing combination items that behave like NDCs  
* 
*  PARAMETERS:                                                                       
*     COMBFILE          = Name of the SAS dataset defining the Combo Input File         
*     CODESFILE         = Name of the SAS dataset defining the Combo Codes Input File       
*     DXENVEL           = Indicator to determine if the envelope should be run on diagnosis claims                                               
*     PXENVEL           = Indicator to determine if the envelope should be run on procedure claims                                                              
*     OUTNAME           = Name of the prefix that will be used to produce the output files in DPLocal
*                         if the SAVETODPLOCAL parameter is set to Y              
*     STOCKPILING       = Indicator to determine if stockpiling should be executed on outpatient pharmacy 
*                         dispensings                            
*     STOCKPILINGFILE   = Name of the SAS dataset defining the stockpiling settings for each RawGroup 
*                         found in the Combo Codes Input File                                                                    
*     ENROLGAP          = The number of days that will be bridged between two consecutive enrollment 
*                         periods to create a �continuously enrolled� period                                             
*     LABSCODEMAP       = Name of the SAS dataset defining the Lab Code Map File                         
*     SAVETODPLOCAL     = Indicator to determine if output files should be created in DPLocal                        
*     PTSTOEXCLUDE      = Name of the SAS dataset defining the patients to exclude from MSDD data extraction                                                                               
*                                                                                                  
*  Programming Notes:                                                                                
*                                                                           
*
*
*--------------------------------------------------------------------------------------------------
* CONTACT INFO: 
*  Sentinel Coordinating Center
*  info@sentinelsystem.org
*
*--------------------------------------------------------------------------------------------------
*  CHANGE LOG: 
*
*   Version   Date       Initials      Comment (reference external documentation when available)
*   -------   --------   --------   ---------------------------------------------------------------
*     2.1     01/12/15     DM       Defensive coding added to eliminate uninitialized variable notes
*
*     2.2     02/23/15     DM       Modified the creation of _VCDrug2 to fix an issue when combo 
*                                   items should behave as RX
*
*     2.3     06/26/15     DM       Fix to Lab extraction when extracting based on Lookup 
*
*     2.4     09/22/15     AP       Removed code to convert PERCENTDAYS from 0 to missing
*
*     2.5     09/29/15     DM       Added the demographics criteria 
*
*     2.6     11/23/15     DM       Added changes required by the new laboratory table specs (v5)
*
*     2.7     11/14/16     AP       Added ability to turn off envelope
*
*     2.8     03/27/17     DM       Changed RawSubGroup for RawStockGroup
*
*     2.9     01/17/18     AP       Changed NOBS to NLOBS in %isdata to be consistent with QRP
*
*     2.10    11/12/18     AP       Saved CIDA datasets to DPLOCAL
*
*     2.11    07/21/19     AP       Initialize missing variables when no combo exclusion
*
***************************************************************************************************;

%MACRO combo(COMBFILE=,CODESFILE=,DXENVEL=,PXENVEL=,OUTNAME=,STOCKPILING=,
             STOCKPILINGFILE=,ENROLGAP=,LABSCODEMAP=,SAVETODPLOCAL=,PTSTOEXCLUDE=);

%PUT =====> MACRO CALLED: combo v2.11;
   
/*Set performance time variables*/

    data _NULL_;
       temp=DATETIME();
       call symputx("STARTCOMBO",put(temp,best.));
       call symputx("STARTDATECOMBO",put(datepart(temp),date9.));
       call symputx("STARTTIMECOMBO",put(timepart(temp),time4.));
    run;

*Macro to determine whether a dataset is empty or not;
%MACRO ISDATA(dataset=);
    %GLOBAL NOBS;
    %let NOBS=0;
    %if %sysfunc(exist(&dataset.))=1 and %LENGTH(&dataset.) ne 0 %then %do;
        data _null_;
        dsid=open("&dataset.");
        call symputx("NOBS",attrn(dsid,"NLOBS"));
        run;
    %end;   
%PUT &NOBS.;
%MEND ISDATA;

    *Macro to check wheter an optional variable exists in an input file;
    %MACRO VAREXIST(DS,VAR);
    %GLOBAL VAREXIST;
    %LET VAREXIST=0;
    %LET DSID = %SYSFUNC(OPEN(&DS));
    %IF (&DSID) %THEN %DO;
        %IF %SYSFUNC(VARNUM(&DSID,&VAR)) %THEN %LET VAREXIST=1;
        %LET RC = %SYSFUNC(CLOSE(&DSID));
    %END;
    %MEND VAREXIST;

    
/*******************/
/* 1. INPUTS PHASE */
/*******************/

    *This variable must always be 0 when combo is used as standalone;
    %IF %SYMEXIST(COMBORUN)=0 %THEN %DO;
        %GLOBAL COMBORUN;
        %LET COMBORUN=0;
    %END;

    %LET COMBFILE=%LOWCASE(&COMBFILE.);
    %LET CODESFILE=%LOWCASE(&CODESFILE.);
    %LET SAVETODPLOCAL=%UPCASE(&SAVETODPLOCAL.);

    /*Set default values*/
    %MACRO WRAPPER;      
       %GLOBAL DPLOCALLIB;
       %IF %STR("&SAVETODPLOCAL.")=%STR("") OR %STR("&SAVETODPLOCAL.")=%STR("N") %THEN %DO;
          %LET DPLOCALLIB=;
       %END; 
       %ELSE %DO;
          %LET DPLOCALLIB=DPLocal.;
       %END; 
    %MEND WRAPPER;
    %WRAPPER;

    %put &DPLOCALLIB.;

    *Convert special characters to underscores;
    %MACRO TRANS(var=);
        *Upcase added to prevent group deduplication caused by uppercase/lowercase typos in the input file;
        &var. = upcase(compress(trim(left(&var.))));
        &var. = translate(&var.,"_","-",
                                "_",".",
                                "_",",",
                                "_","%",
                                "_","$",
                                "_","!",
                                "_","*",
                                "_","&",
                                "_","#",
                                "_","@");
    %MEND TRANS;

    %MACRO IMPORTFILES(var=);

        %IF %INDEX(%UPCASE("&VAR."),CPORT) %THEN %DO;
            proc cimport infile="&infolder.&VAR." library=infolder memtype=data;
            run;
        %END;
    
    %MEND IMPORTFILES;
        
    * CombFile;
    %IMPORTFILES(var=&COMBFILE.); 

    data _COMBFILE;
	length combcode $&codelength.;			
	informat combcode $&codelength..;			
	format combcode $&codelength..;	
    set infolder.&COMBFILE.;
    %trans(var=CombGroup);
    %trans(var=RawGroup);
    CombPDX = upcase(CombPDX);
    CombEncType=upcase(CombEncType);
    run;    

    *CodesFile;
    %IMPORTFILES(var=&CODESFILE.); 

    data _CODESFILE _OTHCODE;
	length rawcode $&codelength.;			
	informat rawcode $&codelength..;			
	format rawcode $&codelength..;	
    set infolder.&CODESFILE.;       
    %trans(var=RawGroup);
    RawCode=compress(RawCode, '.' );
    if RawCodeType in:("DX") then do;
        *temporality creating codetype for MS_ProcessWildcards;
        codetype=substr(RawCodeType,3,2);
        output _CODESFILE;
    end;
    else output _OTHCODE;
    run;

    %ISDATA(dataset=_CODESFILE);
    %IF %eval(&NOBS.>0) %THEN %DO;   
        *Process wildcards;
        %MS_ProcessWildcards(InFile=_CODESFILE, 
                             CodeVar=RawCode,
                             codetype=CodeType, 
                             OutFile=_CODESFILE);
    %END;

    *put non diag codes back in the codesfile;
    data _CODESFILE;
    set  _OTHCODE _CODESFILE ;
    RawLength=length(RawCode); 
    drop codetype;
    run;        

    *check if the CombOrder variable has been defined;
    %VAREXIST(_COMBFILE,CombOrder);
    %put &VAREXIST.;
    %let CombOrder=;
    %IF &VAREXIST.>0 %THEN %DO;  
        %let CombOrder=CombOrder;                
        %put &CombOrder.;
    %END;

    *Sort by RawOrder to make sure the number of RawGroups to process by the Two-By-Two Method will be calculated correctly;
    proc sort data=_COMBFILE;
    by &CombOrder. CombGroup RawOrder;
    run;
    
    proc sort nodupkey data = _COMBFILE out = _CombGroupList(keep=CombGroup);
    by &CombOrder.  CombGroup; 
    run;


    *Create lookup tables for raw record extraction;
    data _cdiag
         _cproc
         /*_cndc(keep=RawCode comb)*/
         _cndc(keep=RawCode comb RawCodeType Codetype)
         _cenc
         _cdem
         _cenr
         _clab;
    set _CODESFILE;

        comb=1;
        length=length(RawCode);

        RawPDX = upcase(RawPDX);                
        RawCaresetting=upcase(RawCaresetting);
                
        if missing(RawExclusion) then RawExclusion=0;

        codetype='   ';
        if RawCodeType in:("DX") then do; codetype=compress(RawCodeType,"DX"); output _cdiag; end;
        if RawCodeType in:("RX") then do; codetype=compress(RawCodeType,"RX"); output _cndc; end;
        if RawCodeType in:("PX") then do; codetype=compress(RawCodeType,"PX"); output _cproc; end;   
        if RawCodeType in:("EN") then do; codetype=compress(RawCodeType,"EN"); output _cenc; end;
        if RawCodeType in:("DM") then do; codetype=compress(RawCodeType,"DM"); output _cdem; end; 
        if RawCodeType in:("EL") then do; codetype=compress(RawCodeType,"EL"); output _cenr; end;   
        if RawCodeType in:("LAB") then do; codetype=compress(RawCodeType,"LAB"); output _clab; end;   
    run;




    %MACRO CREATELOOKUP(file=);
        *To verify if the _ndc dataset exists and has some observations. 
         This dataset will come from the Modular program (if combo tool is not used as standalone);
        %ISDATA(dataset=_&file.);
        %IF %eval(&NOBS.>0) %THEN %DO;   *diags were also defined in cida;
            data _&file.;
            set _&file.(in=a) _c&file.(in=b);
                if a then cida=1;
                if b then do;
                   Comb=1;
                   Code=RawCode;
                   CODECAT=substr(RawCodeType,1,2);
                end;
            run;
        %END;
        %ELSE %DO;
            data _&file.;
            set _c&file.;
                Comb=1;
                cida=0;
                Code=RawCode;
                CODECAT=substr(compress(RawCodeType,'Aa'),1,2);
            run;
        %END;
    %MEND CREATELOOKUP;
    %CREATELOOKUP(file=NDC);
    %CREATELOOKUP(file=diag);
    %CREATELOOKUP(file=proc);
    %CREATELOOKUP(file=lab);



/***********************/
/* 2. MSCDM EXTRACTION */
/***********************/

    %MACRO EXTRACTCOMBMEDS();
        *Copy MSCDM headers/formats into an empty dataset such that 
         there is no need for multiple "existence" management thereafter;
        data _ComboProc   /*Required by Combo Tool*/
             worktemp._procextract /*Required by MPs*/; 
        set indata.&proctable.(obs=0);
        proc=1;
        keep PatID ADate EncType PX PX_CodeType proc; 
        run;
        %ISDATA(dataset=_proc);
        %IF %eval(&NOBS.>0) %THEN %DO; 

            *cleaning lookup file in case the code is used both the combo and the cida settings;

            proc means data=_proc nway noprint;
            var Comb cida;
            class Code Codetype;
            output out=_proc(keep=code Codetype comb cida) max=;
            run;

           %ms_extractmeds(datafile=indata.&proctable.,
                           datacode=PX ,
                           datacodetype=PX_codetype ,
                           lookfile=_proc, 
                           mindate=,
                           outfile=worktemp._procextract(keep=PatID ADate EncType PX PX_CodeType cida comb));

           *delete data for patients in DPs &PTSTOEXCLUDE. files; 
           %ms_delpatients(datafile=worktemp._procextract,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=worktemp._procextract);

            %IF &PXENVEL.=1 /*| &COMBORUN.=1*/ %THEN %DO; *&PXENVEL.= 0 can only happen standalone use of combo;
                %isdata(dataset=worktemp._procextract);
                %IF %eval(&NOBS.>=1) %THEN %DO;
                %MS_ENVELOPE(INFILE=worktemp._procextract,
                             ENCOUNTERINFILE=indata.&enctable.,
                             OUTFILE=worktemp._procextract);
                %END;
            %END;

            data _ComboProc
                 worktemp._procextract /*Required by MPs*/;
            set worktemp._procextract;
            Proc=1;
            PX=compress(PX,'.');
            *Records specific to the creation of Combos;
            if comb = 1 then output _ComboProc;
            *Records that are specific to the Modular Program;
            if cida = 1 then output worktemp._procextract;
            keep PatID ADate EncType PX PX_CodeType proc; 
            run;

            Proc sort nodupkey data=_ComboProc;
            by _ALL_;
            run;

        %END;

        data _ComboDiag   /*Required by Combo Tool*/
             worktemp._diagextract /*Required by MPs*/;
        set indata.&Diatable.(obs=0);
        proc=0;
        keep PatID ADate EncType DX DX_CodeType PDX proc; 
        run;

        %ISDATA(dataset=_diag);
        %IF %eval(&NOBS.>0) %THEN %DO; 

            *cleaning lookup file in case the code is used both the combo and the cida settings;
            proc means data=_diag nway noprint;
            var Comb cida;
            class Code Codetype;
            output out=_diag(keep=code Codetype comb cida) max=;
            run;

           %ms_extractmeds(datafile=indata.&diatable.,
                           datacode=DX ,
                           datacodetype=DX_codetype ,
                           lookfile=_diag, 
                           mindate=,
                           outfile=worktemp._diagextract(keep = PatID ADate EncType DX DX_CodeType PDX cida comb));

           *delete data for patients in DPs &PTSTOEXCLUDE. files; 
           %ms_delpatients(datafile=worktemp._diagextract,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=worktemp._diagextract);

    *Run envelope for diagnoses (macro parameter);
            %macro wrapper;
            %IF %eval(&DXENVEL.=1 /*| &COMBORUN.=1*/) %THEN %DO;  *&DXENVEL.= 0 can only happen standalone use of combo;
                %isdata(dataset=worktemp._diagextract);
                %IF %eval(&NOBS.>=1) %THEN %DO;
                %MS_ENVELOPE(INFILE=worktemp._diagextract,
                             ENCOUNTERINFILE=indata.&enctable.,
                             OUTFILE=worktemp._diagextract);
                %END;
            %END;
            %mend;
            %wrapper;

            data _ComboDiag
                 worktemp._diagextract /*Required by MPs*/;
            set worktemp._diagextract;
            proc=0;
            DX=compress(DX,'.');
            *Records specific to the creation of Combos;            
            if comb = 1 then output _ComboDiag;
            *Records that are specific to the Modular Program;
            if cida = 1 then output worktemp._diagextract;
            keep PatID ADate EncType DX DX_CodeType PDX proc; 
            run;

            Proc sort nodupkey data=_ComboDiag;
            by _ALL_;
            run;
        
        %END;
    %MEND EXTRACTCOMBMEDS;
    %ExtractCombMeds;


    %MACRO EXTRACTCOMBDRUGS();

        *Copy MSCDM headers/formats into an empty dataset such that 
         there is no need for multiple "existence" management thereafter;
        data worktemp._drugs;
        set indata.&distable.(obs=0)  _ndc(obs=0);
        rename RxDate=ADate;
        comb=0;
        cida=0;
        Proc=0;
        %if %eval(&scdmver1) > 7 %then %do;
        RawCode=Rx;
        drop Rx;       
        %end;
        %else %do;
        RawCode=NDC;
        drop NDC;
        %end;
        run;

        proc means data=_ndc nway noprint;
        var Comb cida;
        class Code Codetype;
        output out=_ndc(keep=code Codetype comb cida) max=;
        run;

        %ms_extractdrugs(datafile=indata.&distable.,
                      lookfile=_ndc, 
                      lookvar=code , 
                      mindate=,
                      outfile=_ComboDrug);

           *delete data for patients in DPs &PTSTOEXCLUDE. files; 
           %ms_delpatients(datafile=_ComboDrug,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=_ComboDrug);

        *Create two groups of claims (potentially non mutually exclusive): 
         Raw records for needed by the combo tool, and those needed by the MP;
        data _ComboDrug(rename=RxDate=Adate)
             worktemp.Drugs(drop=cida comb %if %eval(&scdmver1 > 7) %then %do; code %end;);  /*For MP linking Only*/
        set _ComboDrug;
        proc=0;
        if comb=1 then output _ComboDrug;
        if cida=1 then do;
            comb=0;
            output worktemp.Drugs;
        end;
        run;

        proc sort nodupkey data = _ComboDrug;
           by _ALL_;
        run;
    %MEND EXTRACTCOMBDRUGS;
    %EXTRACTCOMBDRUGS();


    %MACRO EXTRACTENR();

        %GLOBAL MedCov;
        %GLOBAL DrugCov;
        %GLOBAL MedDrugCov;

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboEnr1
             _ComboEnr2
             _ComboEnr3;
        set indata.&enrtable.(obs=0);
        EStart=.;
        EEnd=.;
        keep PatID EStart EEnd; 
        run;

        *At least one combo with enrollment criterion;
        %ISDATA(dataset=_cenr);

        %MACRO WRAPPER;
            %IF %EVAL(&NOBS.>0) %THEN %DO;

                %LET MedCov=No;
                %LET DrugCov=No;
                %LET MedDrugCov=No;

                data _null_;
                   set _cenr;
                   if substr(RawCodeType,3,2) in('01') then call symput("MedCov","Yes");
                   if substr(RawCodeType,3,2) in('02') then call symput("DrugCov","Yes");
                   if substr(RawCodeType,3,2) in('03') then call symput("MedDrugCov","Yes");
                run;

                %PUT &MedCov.;
                %PUT &DrugCov.;
                %PUT &MedDrugCov.;

                %IF &MedCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.,
                                   OUTFILE=_ComboEnr1,
                                   MEDCOV=Yes,  
                                   DRUGCOV=No,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr1,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr1);
                %END;
                %IF &DrugCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.,
                                   OUTFILE=_ComboEnr2,
                                   MEDCOV=No,   
                                   DRUGCOV=Yes,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr2,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr2);
                %END;
                %IF &MedDrugCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.,
                                   OUTFILE=_ComboEnr3,
                                   MEDCOV=Yes,  
                                   DRUGCOV=Yes,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr3,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr3);
                %END;
            %END;
        %MEND WRAPPER;
        %WRAPPER;
    %MEND EXTRACTENR;
    %EXTRACTENR;


    %MACRO EXTRACTENC();

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboEnc;
        set indata.&enctable.(obs=0);
        keep PatID Adate Ddate EncType DRG; 
        run;

        %LET enctypes='AV' 'IP' 'OA' 'IS' 'ED';
        %LET los=.;
        %LET drgs='-';

        *at least one combo with Encouter criterion;
        %ISDATA(dataset=_cenc);
        
        %MACRO WRAPPER;
            %IF %EVAL(&NOBS.>0) %THEN %DO;

                *Take the smallest LOS possible across CombGroups to extract all possible eligible patients;                
                data _los;
                set _cenc;
                where index(CodeType,'01');
                LOS=input(RawCode,best.); 
                keep LOS;   
                run;

                proc sort data=_los;
                by LOS;                             
                run;

                data _los;
                set _los;               
                if _N_=1 then call symput("los",put(LOS,best.));
                run;                
                %put &los.;

                *EncType and Caresetting extraction;
                proc sort nodupkey data=_cenc out=_enctype(keep=RawCaresetting);
                by RawCaresetting;
                where index(CodeType,'01');
                run;

                %ISDATA(dataset=_enctype);
                %IF %EVAL(&NOBS.>0) %THEN %DO;
                    data _enctype;
                    set _enctype;
                    format EncType $30.;
                    EncType=trim(left(RawCaresetting));                 
                    drop RawCaresetting;
                    run;

                    proc sql noprint;
                    select distinct EncType
                       into :EncTypes separated by ' '
                       from _EncType;
                    quit;
                    %put &EncTypes.;
                %END;

                *DRG Extraction;
                proc sort nodupkey data=_cenc out=_drg(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'02');
                run;

                %ISDATA(dataset=_drg);
                %IF %EVAL(&NOBS.>0) %THEN %DO;
                    data _drg;
                    set _drg;
                    format drg $6.;
                    drg="'"||trim(left(RawCode))||"'";
                    drop RawCode;
                    run;

                    proc sql noprint;
                    select drg
                       into :drgs separated by ','
                       from _drg;
                    quit;
                    %put &drgs.;
                %END;

                data _ComboEnc;
                set indata.&enctable.;

                *Encounter observations may have missing DDates. Therefore we use a max function to
                 assign ADate to DDate when this is the case (as it is done in MPs);
                where (EncType in(&enctypes.) and (max(Ddate,ADate) - Adate + 1) >= &LOS.) or DRG in:(&drgs.);
                if missing(DDate) then DDate=ADate;
                keep PatID Adate Ddate EncType DRG; 
                run;
               *delete data for patients in DPs &PTSTOEXCLUDE. files; 
               %ms_delpatients(datafile=_ComboEnc,
                               ptsfile=&PTSTOEXCLUDE.,
                               Outfile=_ComboEnc);

            %END;
        %MEND WRAPPER;
        %WRAPPER;
    %MEND EXTRACTENC;
    %EXTRACTENC;

    %MACRO EXTRACTDEM();

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboDem;
        set indata.&demtable.(obs=0);
        keep PatID Sex Hispanic Race; 
        run;        

        *at least one combo with Demographic criterion;
        %ISDATA(dataset=_cdem);
        
        %MACRO WRAPPER;
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                
                *Sex Extraction;
                proc sort nodupkey data=_cdem out=_sex(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'01');
                run;

                %LET sexes='F' 'M' 'U' 'A';

                %ISDATA(dataset=_sex);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select RawCode
                       into :sexes separated by ','
                       from _sex;
                    quit;
                    %put &sexes.;
                %END;

                *Races Extraction;
                proc sort nodupkey data=_cdem out=_race(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'02');
                run;
    
                %LET races='0' '1' '2' '3' '4' '5';

                %ISDATA(dataset=_race);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select RawCode
                       into :races separated by ','
                       from _race;
                    quit;
                    %put &races.;
                %END;

                *Hispanic Extraction;
                proc sort nodupkey data=_cdem out=_hispanic(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'03');
                run;

                %LET hispanics='Y' 'N' 'U';

                %ISDATA(dataset=_hispanic);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select RawCode
                       into :hispanics separated by ','
                       from _hispanic;
                    quit;
                    %put &hispanics.;
                %END;

                data _ComboDem;
                set indata.&demtable.;              
                where upcase(sex) in (&sexes.) and upcase(hispanic) in (&hispanics.) and upcase(race) in (&races.);             
                keep PatID Sex Hispanic Race; 
                run;
               *delete data for patients in DPs &PTSTOEXCLUDE. files; 
               %ms_delpatients(datafile=_ComboDem,
                               ptsfile=&PTSTOEXCLUDE.,
                               Outfile=_ComboDem);

            %END;
        %MEND WRAPPER;
        %WRAPPER;
    %MEND EXTRACTDEM;
    %EXTRACTDEM;

        %MACRO EXTRACTCOMBOLAB();

            proc means data=_lab nway noprint missing;
            var Comb cida;
            class Code Codetype RawLabResult RawLabDateType RawCaresetting RawGroup;
            output out=_lab_(keep=code Codetype RawLabResult RawLabDateType RawCaresetting RawGroup comb cida) max=;
            run;

            %ms_extractlabs(datafile=indata.&labtable.,
                            lookfile=_lab_, 
                            mindate=,
                            outfile=worktemp.labextract);

           *delete data for patients in DPs &PTSTOEXCLUDE. files; 
            %ms_delpatients(datafile=worktemp.labextract,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=worktemp.labextract);

            %isdata(dataset=worktemp.labextract);

            data _ComboLab(keep=patid Adate ms_result_c ms_result_n ms_test_name  ms_test_sub_category specimen_source 
                                ms_result_unit result_type fast_ind pt_loc loinc px px_codetype ResultTyp RawGroup)
                 worktemp.labextract /*Required by MPs*/;

                set worktemp.labextract;
                PX=compress(PX,'.');
                %if %eval(&nobs.>0) %then %do;
                    *Records specific to the creation of Combos;
                    if comb = 1 then output _ComboLab;
                    *Records that are specific to the Modular Program;
                    if cida = 1 then output worktemp.labextract;

                    drop comb cida;
                %end;
                %else %do;
                    ResultTyp = '';
                %end;
            run;

            Proc sort nodupkey data=_ComboLab;
            by _ALL_;
            run;

        proc datasets library=WORK nowarn nolist;
        delete _lab_;
        quit;
        %MEND EXTRACTCOMBOLAB;


    %macro RunLab;
    %ISDATA(dataset=_lab);
    %IF %EVAL(&NOBS.>0) %THEN %DO;
        %EXTRACTCOMBOLAB;
    %end;
    %mend RunLab;
    %RunLab;

    *clean up;
    proc datasets library=WORK nowarn nolist;
    delete _cdiag _cProc  _cndc _cenr _cenc _cdem;
    quit;

/*****************************************************************/
/* Utility Macros for 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION */
/*****************************************************************/

    *Prepare diagnosis records (Duplic inevitable);
    %MACRO LoopDiag(); 
        *Restrict to diagnosis inputs;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType=:"DX";
        run;

        *Select required raw records and add combo input file information to this-combo raw records;
        proc sql noprint;
        Create table _LoopDiag(drop=RawCode: DX: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboDiag as claim
           Where DX_codetype = RawCodetype and compress(claim.DX,'.') = input.RawCode ; 
        quit;

        *Apply caresetting criteria;
        data _LoopDiag;
        set _LoopDiag;
        if RawCaresetting ne "" then do;
            if indexw(upcase(translate(RawCaresetting,'',"'")),upcase(EncType));
        end;
        
        *Apply extra criterion for PDX only in the presence of IP (defensive coding);
        if RawPDX ne "" and upcase(EncType) in('IP') then do;
            if upcase(PDX)=upcase(RAWPDX);
        end;

        *create claim end date;
        format Edate mmddyy10.;
        if RawDurat then Adjust=-1; else Adjust=0;
        Edate=sum(ADate,RawDurat,Adjust);
        drop RawCaresetting EncType RAWPDX PDX RawGroup Adjust;
        run;

    %MEND LoopDiag;


    *Prepare Procedure records (Duplic inevitable);
    %MACRO LoopProc();

        *Restrict to procedure inputs;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType=:"PX";
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopProc(drop=RawCode: PX: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboProc as claim
                Where PX_codetype = RawCodetype and compress(claim.PX,'.') = input.RawCode ; 
        quit;

        *Apply caresetting criteria;
        data _LoopProc;
        set _LoopProc;
        if RawCaresetting ne "" then do;
            if indexw(upcase(translate(RawCaresetting,'',"'")),upcase(EncType));
        end;
        
        *create claim end date;
        format Edate mmddyy10.;     
        if RawDurat then Adjust=-1; else Adjust=0;
        Edate=sum(ADate,RawDurat,Adjust);
        drop RawCaresetting EncType RawPDX RawGroup Adjust;
        run;

    %MEND LoopProc;


    *Prepare Raw Dispensing Records;
    %MACRO LoopDrug();
        proc datasets library=work nolist nowarn;
        delete _LoopDrug;
        quit;

        %let RawLen=0;
        *Restrict to drug inputs;
        data _LoopRaw2;
        set _LoopRaw;
        /* check to see if Rawcode is a length of 9 - assign to macro variable */
        if RawLength = 9 then call symputx("RawLen",RawLength);
        /* Defensive coding - change RX09 and RX11 values all to ND if they exist */
        if RawCodeType in ("09" "11") then RawCodeType = "ND";
        where RawType=:"RX";
        run;


        %MACRO WRAPPER;
                *Always run this query to create empty dataset if there is no Rx9 observation;
                proc sql noprint;
                Create table _LoopDrug(drop=RawCode: Rx RawLength) as
                Select claim.*,
                       input.*
                   from _LoopRaw2 as input,
                        _ComboDrug as claim
                    Where Rx = RawCode and RawLength = 11; 
                quit;       
                *Select Rx9 observations and add them to the Rx11 observations;
                %if %eval(&RAWLEN) = 9 %then %do;
                proc sql noprint;
                Create table _LoopDrug2(drop=RawCode: Rx RawLength) as
                Select claim.*,
                       input.*
                   from _LoopRaw2 as input,
                        _ComboDrug as claim
                    Where substr(Rx,1,9) = RawCode and RawLength = 9; 
                quit;

                proc datasets library=work nolist nowarn;
                append data=_LoopDrug2 base=_LoopDrug force;
                delete _LoopDrug2;
                quit;
                %end;

            %put &STOCKPILING;
           
            %IF %eval(&STOCKPILING.=1) %THEN %DO;

                proc sql noprint;
                    create table _RawGroupList as 
                    select distinct RawGroup
                    from _LoopDrug;

                    select count (distinct RawGroup) into :NBRAWGROUPS
                    from _RawGroupList;
                quit;

                %put &NBRAWGROUPS.;

                %isdata(dataset=infolder.&STOCKPILINGFILE);
                %if %eval(&NOBS>0) %then %do;
                proc sql noprint;
                /* Set defaults and merge in information from original stockpiling file */
                create table STOCKPILINGCOMBO as
                select distinct upper(A.rawgroup) as rawgroup, A.rawstockgroup, 
                    case when(B.sameday2 is missing) then 'aa' else B.sameday2 end as sameday, 
                    case when (B.suprange2 is missing) then '0<-HIGH' else B.suprange2 end as suprange, 
                    case when (B.amtrange2 is missing) then '0<-HIGH' else B.amtrange2 end as amtrange, 
                    B.percentdays
                    from infolder.&CODESFILE(where=(rawcodetype=:'RX')) as A
                    LEFT JOIN 
                          (select upper(b.rawgroup) as rawgroup, b.rawstockgroup, b.sameday as sameday2, 
                                  b.suprange as suprange2, 
                                  b.amtrange as amtrange2, 
                                  b.percentdays
                          from infolder.&STOCKPILINGFILE b) AS B
                        on a.rawgroup=b.rawgroup and a.rawstockgroup = b.rawstockgroup;
                quit;

               /* De-duplicate rows to correctly assign stocknumber based on group */
               proc sort data = STOCKPILINGCOMBO nodupkey out=stock_num;
                by rawgroup sameday suprange amtrange percentdays;
               run;

               data stock_num;
                set stock_num;
                by rawgroup sameday suprange amtrange percentdays;
                retain stocknumber;
                if first.rawgroup then stocknumber=1;
                else stocknumber+1;
               run;

                /* Join stocknumber back onto original dataset */
                proc sql noprint undo_policy=none;
                  create table STOCKPILINGCOMBO as
                    select a.*, b.stocknumber 
                    from STOCKPILINGCOMBO a, stock_num b
                    where a.sameday = b.sameday and a.suprange = b.suprange
                          and a.amtrange = b.amtrange and a.percentdays = b.percentdays;
                quit;

                proc datasets nowarn noprint lib=work;
                  delete stock_num;
                quit;
                %end;

                %do j = 1 %to &NBRAWGROUPS.;

                    data _null_;
                       set _RawGroupList;
                       if _n_ = &j. then call symput('ITRAWGROUP',strip(RawGroup));
                    run;       

                    %let combo_stock_params = 0;
                    %ISDATA(dataset=infolder.&STOCKPILINGFILE.);        
                    %IF %EVAL(&NOBS.>0) %THEN %DO;
                    /* store maximum number of combination of stockpiling parameters */
                    proc sql noprint;
                      select max(stocknumber)
                        into :combo_stock_params
                        from STOCKPILINGCOMBO
                        where rawgroup="&ITRAWGROUP.";
                    quit;
                    %END;

                    %if &combo_stock_params <= 1 %then %do;

                    %if &combo_stock_params <= 0 %then %do;
                    %let SAMEDAY=aa;
                    %let SUPRANGE=0<-HIGH;
                    %let AMTRANGE=0<-HIGH;
                    %let PERCENTDAYS=;
                    %end;

                    %else %do;
                    data _null_;
                    set STOCKPILINGCOMBO;
                    where rawgroup="&ITRAWGROUP";
                    call symputx("SAMEDAY",strip(SameDay));
                    call symputx("SUPRANGE",strip(SupRange));
                    call symputx("AMTRANGE",strip(AmtRange));
                    call symputx("PERCENTDAYS",put(PercentDays,best.));
                    run;
                    %end;

                    data _LoopDrugsIter;
                    set _LoopDrug;
                    where RawGroup = "&ITRAWGROUP.";
                    run;

                    %MS_STOCKPILING(INFILE=_LoopDrugsIter,          
                                    CLMDATE=ADate,                                  
                                    CLMSUP=RxSup,                                   
                                    CLMAMT=RxAmt,                                   
                                    PROCFLAG=,                                      
                                    PERCENTDAYS=&PERCENTDAYS.,                                  
                                    GROUPING=RawOrder RawGroup RawStockGroup,                           
                                    SAMEDAY=&SAMEDAY.,                                  
                                    SUPRANGE=&SUPRANGE.,                                    
                                    AMTRANGE=&AMTRANGE.,                                    
                                    ID=RawDurat RawEpiType RawEpiGap RawEdateType RawAdateType RawExclusion RawDaysFromStartDt RawDaysFromEndDt,                                            
                                    OUTFILE=_LoopDrugsIter,         
                                    OUTFILEEXCL=_LoopDrugsIterEx    
                    );
                    %end; /* <= 1 */

                    %if &combo_stock_params > 1 %then %do;

                    %do z = 1 %to &combo_stock_params;

                        data STOCKPILINGCOMBO_&z;
                        set STOCKPILINGCOMBO;
                        where RawGroup = "&ITRAWGROUP." and stocknumber=&z;
                          call symput('SAMEDAY',strip(SameDay));
                          call symput('SUPRANGE',strip(SupRange)); 
                          call symput('AMTRANGE',strip(AmtRange)); 
                          call symput('PERCENTDAYS',put(PercentDays,best.));
                        run;
                    
                    %put &ITRAWGROUP. &SAMEDAY. &SUPRANGE. &AMTRANGE. &PERCENTDAYS.;

                    /* Only process dataset if there are subsetted observations */
                    %ISDATA(dataset=STOCKPILINGCOMBO_&z);
                    %if %eval(&NOBS>0) %then %do;
                    %let stock_list=;
                    proc sql noprint;
                      select distinct quote(strip(rawstockgroup))
                      into :stock_list separated by ' ' 
                      from STOCKPILINGCOMBO_&z;
                    quit;
            
                    data _LoopDrugsIter_&z;
                    set _LoopDrug;
                    where RawGroup = "&ITRAWGROUP." and RawStockGroup in (&stock_list);
                    run;

                    %MS_STOCKPILING(INFILE=_LoopDrugsIter_&z,          
                                    CLMDATE=ADate,                                  
                                    CLMSUP=RxSup,                                   
                                    CLMAMT=RxAmt,                                   
                                    PROCFLAG=,                                      
                                    PERCENTDAYS=&PERCENTDAYS.,                                  
                                    GROUPING=RawOrder RawGroup RawStockGroup,                           
                                    SAMEDAY=&SAMEDAY.,                                  
                                    SUPRANGE=&SUPRANGE.,                                    
                                    AMTRANGE=&AMTRANGE.,                                    
                                    ID=RawDurat RawEpiType RawEpiGap RawEdateType RawAdateType RawExclusion RawDaysFromStartDt RawDaysFromEndDt,                                            
                                    OUTFILE=_LoopDrugsIter_&z,         
                                    OUTFILEEXCL=_LoopDrugsIterEx_&z    
                    );

                    %end; /* STOCKPILINGCOMBO_&z */

                    %end; /* z */

                    /* Stack all datasets together */
                    data _LoopDrugsIter;
                      set _LoopDrugsIter_:;
                    run;

                    data _LoopDrugsIterEx;
                      set _LoopDrugsIterEx_:;
                    run;

                    /* Delete temporary datasets */
                    proc datasets nowarn noprint lib=work;
                    delete _LoopDrugsIter_: _LoopDrugsIterEx_: stockpilingcombo_:;
                    quit;

                    %end; /* > 1 */

                    %let SAMEDAY=aa;
                    %let SUPRANGE=0<-HIGH;
                    %let AMTRANGE=0<-HIGH;
                    %let PERCENTDAYS=;

                    %if &j.=1 %then %do;
                        data _LoopDrugs;
                           set _LoopDrugsIter;
                        run;                                
                    %end;
                    %else %do;
                        proc append base = _LoopDrugs
                                    data = _LoopDrugsIter force;
                        run;                                
                    %end;
                %end;       

                %ISDATA(dataset=_loopdrugs);        
                %IF %EVAL(&NOBS.>0) %THEN %DO;
                    proc datasets library=work;
                    delete _loopdrug;
                    change _loopdrugs = _loopdrug;
                    quit;   
                %END;                
            %END;


        %MEND WRAPPER;
        %WRAPPER;

        *Creating Edate;
        data _LoopDrug;
        set _LoopDrug;
        length Adate 4.;
        format Edate mmddyy10.;
        /*Parameter RawDurat commented on purpose. Usage deferred to a future version*/
        Edate=sum(ADate,RxSup/*,RawDurat*/)-1; *already Stockpiled - for overlap only;        
        drop RawDurat;
        run;

    %MEND LoopDrug;

    *Prepare Enrollment records;
    %MACRO LoopEnr();

        *Restrict to eligibility inputs;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType=:"EL";
        Rawcode2=input(RawCode,best.);
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopEnr1(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboEnr1 as claim
           Where EEnd - EStart  + 1 >= Rawcode2; 
        quit;

        proc sql noprint;
        Create table _LoopEnr2(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboEnr2 as claim
           Where EEnd - EStart  + 1 >= Rawcode2; 
        quit;

        proc sql noprint;
        Create table _LoopEnr3(drop= RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboEnr3 as claim
           Where EEnd - EStart  + 1 >= Rawcode2; 
        quit;

        *Combine the three types - Duplicates records only at the RawOrder level;
        data _LoopEnr;
        set _LoopEnr1 
            _LoopEnr2
            _LoopEnr3;
        length Adate Edate 4.;
        format Adate Edate mmddyy10.;
        Adate=EStart;
        /*Parameter RawDurat commented on purpose. Usage deferred to a future version
        Edate=sum(EEnd,RawDurat);*/
        Edate=EEnd;
        drop RawCaresetting EStart EEnd RawPDX RawGroup;
        run;

        proc datasets library=work nolist nowarn;
        delete _LoopEnr1 _LoopEnr2 _LoopEnr3;
        quit;

    %MEND LoopEnr;

    *Prepare Encounter records;
    %MACRO LoopEnc();

        *Restrict to encounter based on EncType inputs;
        *Need output Method for Enctype;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="EN" and RawCodeType="01";
        LOS=input(RawCode,best.);
        ET=compress(RawCaresetting, "'");
        NumEncType=COUNTW(RawCaresetting);
        do i=1 to NumEncType;
            RawCaresetting=SCAN(ET,i); 
            output;
        end;
        drop i NumEncType ET;
        run;

        *select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopEnc1(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboEnc as claim
            /*If LOS =. then always true for that part of the criteria*/
            Where RawCaresetting = EncType and (DDate-ADate+1) >= LOS; 
        quit;

        *Restrict to encounter based on DRG inputs;
        *Need output Method for Enctype;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="EN" and RawCodeType="02";
        ET=compress(RawCaresetting, "'");
        NumEncType=COUNTW(RawCaresetting);
        do i=1 to NumEncType;
            RawCaresetting=SCAN(ET,i); 
            output;
        end;
        drop i ET NumEncType;
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopEnc2(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboEnc as claim
            Where RawCaresetting = EncType and strip(drg)=strip(RawCode); 
        quit;

        *Combine the two types - Duplicates records only at the RawOrder level after nodupkey;
        data _LoopEnc;
        set _LoopEnc1 
            _LoopEnc2;
        format Edate mmddyy10.;
        /*Parameter RawDurat commented on purpose. Usage deferred to a future version
        Edate=sum(Ddate,RawDurat); */
        Edate=DDate;
        drop RawCaresetting EncType DDate RawGroup RawPDX;
        run;

        proc datasets library=work nolist nowarn;
        delete _LoopEnc1 _LoopEnc2;
        quit;

    %MEND LoopEnc;

    *Prepare Demographic records;
    %MACRO LoopDem();

        *Restrict to demogs based on sex;
        *Need output Method;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="DM" and RawCodeType="01";    
        Sex=compress(RawCode, "'");
        NumSex=COUNTW(RawCode);
        do i=1 to NumSex;
            RawCode=SCAN(Sex,i); 
            output;
        end;
        drop i Sex NumSex;  
        run;

        *select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopDem1(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboDem as claim            
           Where sex = RawCode; 
        quit;

        *Restrict to demogs based on race;
        *Need output Method;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="DM" and RawCodeType="02";
        Race=compress(RawCode, "'");
        NumRace=COUNTW(RawCode);
        do i=1 to Numrace;
            RawCode=SCAN(Race,i); 
            output;
        end;
        drop i Race NumRace;
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopDem2(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboDem as claim
            Where Race = RawCode; 
        quit;

        *Restrict to demogs based on hispanic;
        *Need output Method;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="DM" and RawCodeType="03";
        Hispanic=compress(RawCode, "'");
        NumHispanic=COUNTW(RawCode);
        do i=1 to NumHispanic;
            RawCode=SCAN(Hispanic,i); 
            output;
        end;
        drop i Hispanic NumHispanic;
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopDem3(drop=RawCode: RawLength) as
        Select claim.*,
               input.*
           from _LoopRaw2 as input,
                _ComboDem as claim
            Where Hispanic = RawCode; 
        quit;

        *Combine the two types - Duplicates records only at the RawOrder level after nodupkey;
        data _LoopDem;
        set _LoopDem1 
            _LoopDem2
            _LoopDem3;
        length Adate Edate 4.;
        format Adate Edate mmddyy10.;
        /*Parameter RawDurat commented on purpose. Usage deferred to a future version
        Edate=sum(Ddate,RawDurat); */
        ADate=-99999;
        Edate=99999;
        drop RawCaresetting RawGroup RawPDX;
        run;

        proc datasets library=work nolist nowarn;
        delete _LoopDem1 _LoopDem2 _LoopDem3;
        quit;

    %MEND LoopDem;

    *Prepare Laboratory records;
    %MACRO LoopLab();

        *Lab extraction based on lookup - LAB01;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="LB" and RawCodeType="01";    
        run;

        *Obtain the ms_test_name, ms_test_sub_category, specimen_source, ms_result_unit
         result_type, fast_ind and pt_loc based on RawCode;
        proc sort data=_LoopRaw2;
        by RawCode;
        run;

        *Code in infolder.&LABSCODEMAP. should be unique;
        proc sort nodupkey data=infolder.&LABSCODEMAP.(rename=Code=RawCode) out=_Map dupout=_dups;
        by RawCode;
        run;

        data _LoopRaw2;
        merge _LoopRaw2(in=a) _Map(in=b);
        by RawCode;
        if a and b;
        run;

        *Select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopLab1(drop=RawCode: RawLength) as
        Select claim.Patid,
               claim.Adate,
               claim.ms_result_n,
               claim.ms_result_c,
               claim.ResultTyp,
               input.*
           from _LoopRaw2 as input,
                _ComboLab as claim
           where input.ms_test_name         = claim.ms_test_name and 
                 input.ms_test_sub_category = claim.ms_test_sub_category  and 
                 input.specimen_source      = claim.specimen_source and 
                 input.ms_result_unit       = claim.ms_result_unit and
                 input.result_type          = claim.result_type and
                 input.fast_ind             = claim.fast_ind and
                 input.pt_loc               = claim.pt_loc and
                 input.RawGroup             = claim.RawGroup;
        quit;


        *Lab extraction based on LOINC - LAB02;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="LB" and RawCodeType="02";        
        run;

        *select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopLab2(drop=RawCode: RawLength) as
        Select claim.Patid,
               claim.Adate,
               claim.ms_result_n,
               claim.ms_result_c,
               claim.ResultTyp,
               input.*
           from _LoopRaw2 as input,
                _ComboLab as claim
           where input.RawCode  = claim.LOINC and
                 input.RawGroup = claim.RawGroup;
        quit;


        *Lab extraction based on PX - LABXX (XX not in (01 02));
        data _LoopRaw2;
        set _LoopRaw;
        where RawType="LB" and RawCodeType not in("01","02");       
        run;

        *select required raw records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopLab3(drop=RawCode: RawLength) as
        Select claim.Patid,
               claim.Adate,
               claim.ms_result_n,
               claim.ms_result_c,
               claim.ResultTyp,
               input.*
           from _LoopRaw2 as input,
                _ComboLab as claim
           where input.RawCode     = claim.Px and 
                 input.RawCodeType = claim.px_codetype and
                 input.RawGroup    = claim.RawGroup;
        quit;


        *Combine the three types - Duplicates records only at the RawOrder level after nodupkey;
        data _LoopLab;
        set _LoopLab1 
            _LoopLab2
            _LoopLab3;
        format Edate mmddyy10.;
        Edate=Adate;
        keep=0;
        if ResultTyp="C" then do;
        if ms_result_c = RawLabResult;
        end;
        drop RawCaresetting RawGroup RawPDX MS_Test_Name MS_Test_Sub_Category
             Specimen_Source MS_Result_unit result_type fast_ind pt_loc;* RawOrigCode;
        run;

        *Removing the true duplicates as a record could have been extracted three time
         but using three different criteria;
        proc sort nodupkey data=_LoopLab;
        by _ALL_;  
        run;

        proc datasets library=work nolist nowarn;
        delete _LoopLab1 _LoopLab2 _LoopLab3;
        quit;

    %MEND LoopLab;



    %MACRO CreateEpi(INFILE=,OUTFILE=,STARTDT=,ENDDT=);

        *Initialization of episode creation macro variables;
        %LET epi=0;
        %LET gap=0;

        data _null_;
        set &infile.(obs=1);
            *check if need to create episodes;
            if RawEpiType>0 then call symput("epi",put(1,best.));
            *Group variables: 1=by RawGroup 2=by RawStockGroup;
            if RawEpiType=1 then do;
                *Programming Note: Episode creation is already by RawGroup - thus, Patid is enough;
                call symput("by","PatId");
                call symput("by2","PatId");
                call symput("by3","PatId");
            end;
            else if RawEpiType=2 then do;
                call symput("by","PatId RawStockGroup");
                call symput("by2","PatId,RawStockGroup");
                call symput("by3","RawStockGroup");
            end;
            if RawEpiGap =. then  RawEpiGap =0;
            if RawEpiGap>=0 then call symput("Gap",strip(put(RawEpiGap,best.)));
        run;

        %put &epi. &Gap.;

        %IF &epi.=1 %THEN %DO;
            *Episode creation required (RawEpiType variable > 0);
            proc sql noprint;
            create table _episodes_ as
            Select *
            from &INFILE.
            order by &by2., &STARTDT., &ENDDT.;
            quit;

            *Create episodes considering allowed gaps;
            data _episodes_(drop=LRunOutDt);
            set  _episodes_;
            by &by.;

            if first.&by3. then do;
              LRunOutDt=&ENDDT.;
              episode=1;
              gap=.;
            end;
            else do; 
              gap = &STARTDT. - LRunOutDt-1;         
              if gap > &Gap. then do;
                  episode=episode+1; 
                  LRunOutDt = &ENDDT.;
              end;
              else LRunOutDt = max(&ENDDT.,LRunOutDt);  * in case of encapsulated records;
            end;

            retain episode LRunOutDt;
            run;

            *Create episodes considering allowed gaps;
*           data _episodes_;
*           set  _episodes_;
*           by &by.;
*           if &STARTDT.-lag(&ENDDT.)-1 > &GAP. then episode=episode+1; 
*           if first.&by3. then episode=1;
*           retain episode;
*           run;

            Proc SQL Noprint;
            Create Table &OUTFILE. as
            Select  file.*,
                    min(&STARTDT.) as EStart format=mmddyy10.,
                    max(&ENDDT.) as EEnd format=mmddyy10.
            From _episodes_ as file  
            group by &by2., episode;
            quit;

            proc sort data=&OUTFILE. out=&OUTFILE.(drop=&STARTDT. &ENDDT. rename=EStart=&STARTDT. rename=EEnd=&ENDDT.) NODUPKEY;
            by &by. EStart EEnd;
            run;
        %END;
    %MEND CreateEpi;

    %MACRO LoopCB();

    *initialize/reset;
    %global CBDIAG;
    %global CBPROC;
    %global CBDRUG;
    %let CBDIAG=;
    %let CBPROC=;
    %let CBDRUG=;

    *Restrict to diagnosis-like combo virtual claims;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType=:"DX" and RawCodeType="CB";
        run;

        %ISDATA(dataset=_LoopRaw2);     
        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw virtual records and add combo input file information to this-combo raw records;
            proc sql noprint;
            Create table _LoopDiagCB(drop=RawCode: DX: RawLength) as
            Select claim.*,
                   input.*
               from _LoopRaw2 as input,
                    _VCDiag as claim
               Where DX_codetype = RawCodetype and compress(claim.DX,'.') = input.RawCode ; 
            quit;

            *Apply caresetting criteria;
            data _LoopDiagCB;
            set _LoopDiagCB;
            if RawCaresetting ne "" then do;
                if indexw(upcase(translate(RawCaresetting,'',"'")),upcase(EncType));
            end;
            
            *Apply extra criterion for PDX only in the presence of IP (defensive coding);
            if RawPDX ne "" and upcase(EncType) in('IP') then do;
                if upcase(PDX)=upcase(RAWPDX);
            end;

            *create claim end date;
            format Edate mmddyy10.;
            if RawDurat then Adjust=-1; else Adjust=0;
            Edate=sum(ADate,RawDurat,Adjust);
            drop RawCaresetting EncType RAWPDX PDX RawGroup Adjust;
            run;

            *set macro variable to dynamically be included in later datastep;
            %let CBDIAG=_LoopDiagCB;
        %END;
       

    *Restrict to procedure-like combo virtual claims;
        data _LoopRaw2;
        set _LoopRaw;
        where RawType=:"PX" and RawCodeType="CB";
        run;

        %ISDATA(dataset=_LoopRaw2);     
        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw records and add combo input file information to combo raw records;
            proc sql noprint;
            Create table _LoopProcCB(drop=RawCode: PX: RawLength) as
            Select claim.*,
                   input.*
               from _LoopRaw2 as input,
                    _VCProc as claim
                    Where PX_codetype = RawCodetype and compress(claim.PX,'.') = input.RawCode ; 
            quit;

            *Apply caresetting criteria;
            data _LoopProcCB;
            set _LoopProcCB;
            if RawCaresetting ne "" then do;
                if indexw(upcase(translate(RawCaresetting,'',"'")),upcase(EncType));
            end;
            
            *create claim end date;
            format Edate mmddyy10.;     
            if RawDurat then Adjust=-1; else Adjust=0;
            Edate=sum(ADate,RawDurat,Adjust);
            drop RawCaresetting EncType RawPDX RawGroup Adjust;
            run;

            *set macro variable to dynamically be included in later datastep;
            %let CBPROC=_LoopProcCB;
        %END;

    *Restrict to Drug-like combo virtual claims;
        data _LoopRaw2;
        set _LoopRaw;
where RawType=:"RX" and RawCodeType="CB";
        run;

        %ISDATA(dataset=_LoopRaw2);     
        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw records and add combo input file information to combo raw records;
            proc sql noprint;
            Create table _LoopDrugCB as
            Select claim.*,
                   input.*
               from _LoopRaw2 as input,
                    _VCDrug as claim
                Where compress(claim.Rx,'.') = input.RawCode ; 
            quit;

            *Apply caresetting criteria;
            data _LoopDrugCB;
            set _LoopDrugCB(rename=RxDate=ADate);
            *create claim end date;
            format Edate mmddyy10.;
    *       Edate=sum(ADate,RxSup/*,RawDurat*/)-1; *already Stockpiled - for overlap only;        
            if RawDurat then Adjust=-1; else Adjust=0;
            Edate=sum(ADate,RxSup-1,RawDurat,Adjust);
            drop RawGroup Adjust;
            run;

            *set macro variable to dynamically be included in later datastep;
            %let CBDRUG=_LoopDrugCB;
        %END;

    %MEND LoopCB;



/***************************/
/* START 3. COMBGROUP LOOP */
/***************************/

    *Extract the number of combo groups for iteration loop;
    data _null_;
       set _CombGroupList end=fin;
       if fin then call symput('NCOMBGROUPS',put(_n_,best.));
    run;
   %PUT &NCOMBGROUPS.;

   *Loop through ComboGroups;
   %DO i = 1 %TO &NCOMBGROUPS.;

        *Get current combo group;
        data _null_;
           set _CombGroupList;
           if _n_ = &i. then call symput('ITCOMBGROUP',strip(CombGroup));
        run;
        %PUT &ITCOMBGROUP.;      
    
        proc sql noprint;
        create table _LoopRaw as 
        select Comb.RawOrder,
               Comb.RawEpiType,
               Comb.RawEpiGap,
               Comb.RawExclusion,
               Comb.RawDaysFromStartDt,
               Comb.RawDaysFromEndDt,
               Comb.RawDurat,
               Comb.RawADateType,
               Comb.RawEDateType,
               Raw.*
        from _COMBFILE(keep=CombGroup Raw:) as Comb,
             _CODESFILE as Raw
        where CombGroup in("&ITCOMBGROUP.") and Comb.RawGroup=Raw.RawGroup;
        quit;

        data _LoopRaw(keep=Raw:);  /*input parameters for the raw records fileres and to identify the combos*/
            set _LoopRaw;
            RawLength=length(RawCode);

            * letters in RawType and numbers in RawCodeType;
            if RawCodeType in:("LAB") then RawType="LB";
            else RawType=substr(RawCodetype,1,2);

            if RawCodeType in:("DX")  then RawCodeType=compress(RawCodeType,"DX");
            if RawCodeType in:("RX")  then RawCodeType=compress(RawCodeType,"RX");
            if RawCodeType in:("PX")  then RawCodeType=compress(RawCodeType,"PX");   
            if RawCodeType in:("EN")  then RawCodeType=compress(RawCodeType,"EN"); 
            if RawCodeType in:("DM")  then RawCodeType=compress(RawCodeType,"DM");  
            if RawCodeType in:("EL")  then RawCodeType=compress(RawCodeType,"EL"); 
            *Must remove the "N" or "C" at the end of codetype; 
            if RawCodeType in:("LAB") then do;
                RawCodeType=compress(RawCodeType,"LAB");  
                RawCodeType=substr(RawCodeType,1,2);
            end;
        run;

        data _LoopComb(keep=Comb:); /*input parameters to create the virtual record*/           
            set _COMBFILE(where=(CombGroup in("&ITCOMBGROUP.")))  end=eof1;         
            if _N_=1 then output _LoopComb;

            call symput("Behave",upcase(strip(CombBehavior)));          

            *Max Num Orders for *this combo;
            if eof1 then call symput("NUMORDER",put(RawOrder,best.));
        run;
        %put &NUMORDER.;  *the number of times to apply the two-by-two method - 1;
        
/**********************************************/
/* 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION */
/**********************************************/

        *Get MSCDM data for *this loop (current combo group);
        %LoopDiag;
        %LoopProc;
        %LoopDrug;
        %LoopEnr;
        %LoopEnc;
        %loopDem;
        %LoopCB;

        %macro RunLab;
            %ISDATA(dataset=_clab);
            %global lab;
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                %LoopLab;
                %let lab=_LoopLab;
            %end;
            %else %do;
                %let lab=;
            %end;
        %mend RunLab;
        %RunLab;
        %put &lab.;

        *Initialize two_by_two;
        data _One(drop=RawDays:)
             _ForCombine;
        set _LoopDiag
            _LoopProc
            _LoopDrug
            _loopEnr
            _loopEnc
            _loopDem
            &lab.
            &CBPROC.
            &CBDIAG.
            &CBDRUG.;
        *Suffix B is for Base, Suffix C is for CombineTo;
        format ADateB EDateB ADateC EDateC mmddyy10.;
        if  RawOrder=1 then do;
            ADateB=Adate; *Suffix B is for Base;
            EdateB=Edate; 
            output _One;
        end;
        else do;
            ADateC=Adate;*Suffix C is for CombineTo;
            EdateC=Edate;
            output _ForCombine; 
        end;
        Drop Adate Edate;
        run;

        *Create Episode if necessary;
        %CreateEpi(INFILE=_One,
                 OUTFILE=_One,
                 STARTDT=ADateB,
                 ENDDT=EdateB);
   
        *No combos to create because there is only one RawOrder value - proceed to formatting the _One dataset as if final;
        %MACRO wrapper;
        %if &NUMORDER.=1 %then %do;  
            data _One(rename=Adate=AdateB rename=Edate=EdateB);
            set _One;

            format Adate Edate date9.;

            Adate=adateB;
            Edate=EdateB;

            *safecoding the type of dates to keep;
            RawAdateType=Upcase(strip(RawAdateType));
            RawEdateType=Upcase(strip(RawEdateType));

            *Create Virtual claim Adate;
            if RawADateType="EDATEB"  Then Adate=EdateB;                *The end of the Base interval;
            *Create Virtual claim Edate;
            if RawEDateType="ADATEB"  Then Edate=adateB;                *The start of the Base interval;

            keep PatId Adate Edate;* NumDaysB2C low high; 
            run;
        %end;*if &order.=1;
        %mend wrapper;
        %wrapper;

/************************/
/* 3.2. TWO-BY-TWO LOOP */
/************************/

        *Loop through items to combine;
        %DO order=2 %TO &NUMORDER.;
            
            *Check to see if this step is to apply an exclusion rule;
            %let RawExclusion=0;
            proc sql noprint;
                select max(RawExclusion) into :RawExclusion
                from _LoopRaw
                where RawOrder=&order.;
            quit;
            %PUT &RawExclusion.;

            *Select records to combine to _One dataset - leave the rest in _ForCombine for 
             the next iteration of the TwoByTwo method;
            data _Two(keep=PatId RawOrder ADateC EDateC RawExclusion RawADateType RawEDateType RawDays: RawEpi: RawStockGroup)
                 _ForCombine;*rest of raw records;
            set _ForCombine;

            if RawOrder=&order. then output _Two;       
            else output _ForCombine;  *RawOrder> order;
            run;
            
            *Create Episodes if necessary;
            %CreateEpi(INFILE=_Two,
                       OUTFILE=_Two,
                       STARTDT=ADateC,
                       ENDDT=EdateC);

            %ISDATA(dataset=_One);
            %put NOBS in _One: &NOBS.;
            
            %MACRO WRAPPER;
                *Assign relationship to Base and compute the "reach" of base record to identify overlap with _two;
                Data _One;
                set _One;
                if _N_=1 then set _Loopraw(obs=1 where=(&order.=RawOrder) keep=RawDaysFromStartDt RawDaysFromEndDt RawOrder);

                format AdateLeft AdateRight EdateLeft EdateRight OverlapStartDt OverlapStopDt mmddyy10.;

            %IF %eval(&NOBS.>0) %THEN %DO;
                *Creating Overlap looking start date;
                if RawDaysFromStartDt eq '' and RawDaysFromEndDt eq '' then do;
                AdateLeft=AdateB; *If empty, use actual date;
                EdateLeft=EdateB; *If empty, use actual date;
                end;

                if RawDaysFromStartDt ne '' then do;
                    lowAdateB=input(SCAN(RawDaysFromStartDt,1,","),best.);
                    AdateLeft=sum(AdateB,lowAdateB);
                    if upcase(SCAN(RawDaysFromStartDt,2,",")) not in("X","") then do;
                        HighAdateB=input(SCAN(RawDaysFromStartDt,2,","),best.);
                        AdateRight=sum(AdateB,HighAdateB);  *missing if second argument is x;
                    end;
                end;

                *Creating Overlap looking Stop date;
                if RawDaysFromEndDt ne '' then do;
                    lowEdateB=input(SCAN(RawDaysFromEndDt,1,","),best.);
                    EdateLeft=sum(EdateB,lowEdateB);
                    if upcase(SCAN(RawDaysFromEndDt,2,","))  not in("X","") then do;
                        HighEdateB=input(SCAN(RawDaysFromEndDt,2,","),best.);
                        EdateRight=sum(EdateB,HighEdateB);  *missing if second argument is x;
                    end;
                end;

                OverlapStartDt=min(AdateLeft,AdateRight,EdateLeft,EdateRight);
                OverlapStopDt =max(AdateLeft,AdateRight,EdateLeft,EdateRight);
            %END;
            %ELSE %DO;
                AdateLeft=.;
                AdateRight=.;
                EdateLeft=.;
                EdateRight=.;
                OverlapStartDt=.;
                OverlapStopDt=.;            
            %END;
                *Keep numline to identify lines to delete in the case of an exclusion - will be used to merge;
                NumLine=_N_;        
                run;

            %MEND WRAPPER;
            %WRAPPER;

            *Identify combinations;
            proc sql noprint;
            Create table _TwoByTwo as
                Select one.ADateB, 
                       one.EdateB,
                       one.NumLine, 
                       two.*
                from _One(keep=PatId ADateB EDateB OverlapStartDt OverlapStopDt NumLine) as one,
                     _Two as two
                Where one.Patid = two.PatId and 
                      max(OverlapStartDt,ADateC) <= min(OverlapStopDt,EDateC) /*MUST OVERLAP*/
                order PatId, ADateB, EDateB, ADateC, EDateC; 
            quit;


            *In the case where exclusion=1, we will only have kept the overlaping records to exclude.
             We now need to remerge by NumLine and only keep those lines that are not in _TwoByTwo;
            %ISDATA(dataset=_TwoByTwo);
            %MACRO WRAPPER;
                %IF %eval(&NOBS.>0) & &RawExclusion.=1 %THEN %DO;
                    proc sort nodupkey data=_TwoByTwo(keep=NumLine ADateC EDateC);
                    by NumLine;
                    run;

                    data _TwoByTwo;
                    merge _One(in=a) _TwoByTwo(in=b); 
                    by NumLine;
                    if a and not b;         *Programing Note: ADateC EDateC will be generated empty to eliminate w.a.r.n.i.n.g.s. further below;
                    RawADateType="ADATEB";  *Cannot be something else because of exclusion;
                    RawEDateType="EDATEB";  *Cannot be something else because of exclusion;
                    run;
                %END;
                %ELSE %IF &RawExclusion.=1 %THEN %DO;
                    *_TwoByTwo is empty: nothing to exclude;
                    data _TwoByTwo;
                    set _One;
                    RawADateType="ADATEB";  
                    RawEDateType="EDATEB";  
                        *initialize to prevent warnings; 
                        adatec = adateb;
    					call missing(adatec);
    					edatec =edateb;
    					call missing(edatec); 
                    run;
                %END;
            %MEND WRAPPER;
            %WRAPPER;
                
            %ISDATA(dataset=_TwoByTwo); 
            *Apply final criteria to overlapping records;
            data _One(rename=Adate=AdateB rename=Edate=EdateB); *rename to B because _One will become the new base in the next iteration;
            set _TwoByTwo;

        %IF %eval(&NOBS.>0) or &RawExclusion.=0 %THEN %DO;
            *Safecoding the type of dates to keep;
            RawAdateType=Upcase(strip(RawAdateType));
            RawEdateType=Upcase(strip(RawEdateType));
    
            format Adate Edate date9.;

            *Create Virtual claim Adate;
            if RawADateType="ADATEB"  Then Adate=adateB;                        *The start of the Base interval;
            else if RawADateType="ADATEC"  Then Adate=adateC;                   *The start of the Combined-to interval;
            else if RawADateType="MINADATEBC" Then Adate=min(adateB,adateC);    *The minimum between Base and Combined-to start date;
            else if RawADateType="MAXADATEBC" Then Adate=max(adateB,adateC);    *The maximum between Base and Combined-to start date;
            else if RawADateType="EDATEB"  Then Adate=EdateB;                   *The end of the Base interval;
            else if RawADateType="EDATEC"  Then Adate=EdateC;                   *The end of the Combined-to interval;
            else if RawADateType="MINEDATEBC" Then Adate=min(EdateB,EdateC);    *The minimum between Base and Combined-to end date;
            else if RawADateType="MAXEDATEBC" Then Adate=max(EdateB,EdateC);    *The maximum between Base and Combined-to end date;
            else if RawADateType="MINOVER" Then Adate=max(adateB,adateC);       *The start of the overlapping period between Base and Combined-to;
            else if RawADateType="MAXOVER" Then Adate=min(EdateB,EdateC);       *The end of the overlapping period between Base and Combined-to;

            *Create Virtual claim Edate;
            if RawEDateType="ADATEB"  Then Edate=adateB;                        *The start of the Base interval;
            else if RawEDateType="ADATEC"  Then Edate=adateC;                   *The start of the Combined-to interval;
            else if RawEDateType="MINADATEBC" Then Edate=min(adateB,adateC);    *The minimum between Base and Combined-to start date;
            else if RawEDateType="MAXADATEBC" Then Edate=max(adateB,adateC);    *The maximum between Base and Combined-to start date;
            else if RawEDateType="EDATEB"  Then Edate=EdateB;                   *The end of the Base interval;
            else if RawEDateType="EDATEC"  Then Edate=EdateC;                   *The end of the Combined-to interval;
            else if RawEDateType="MINEDATEBC" Then Edate=min(EdateB,EdateC);    *The minimum between Base and Combined-to end date;
            else if RawEDateType="MAXEDATEBC" Then Edate=max(EdateB,EdateC);    *The maximum between Base and Combined-to end date;         
            else if RawEDateType="MINOVER"  Then Edate=max(adateB,adateC);      *The start of the overlapping period between Base and Combined-to;
            else if RawEDateType="MAXOVER"  Then Edate=min(EdateB,EdateC);      *The end of the overlapping period between Base and Combined-to;

            * Because of the the flexibility of RawDaysFromStartDt and RawDaysFromEndDt combined with 
              the selection of dates for the virtual claim, we need to check that the 
              event did not end before starting;
            if Adate <=Edate;
        %END;
        %ELSE %DO;
            Adate=.;    
            Edate=.;
        %END;

            keep PatId Adate Edate;
            run;

            *The claim virtual date selection could have created duplicates;
            proc sort nodupkey data=_One;
            by _ALL_;
            run;

        %END;* ORDER LOOP - this marks the end of the two-by-two step for this combgroup;

/*************/
/* 4. OUTPUT */
/*************/

        *Add Comb* input variables to create final virtual claim;
        data _one;
        set _one;
        if _N_=1 then set _LoopComb;
        if CombBehavior='PX' then proc=1;else proc=0;
        run;
        %put &Behave.;

        *Get specific data partners formats and lengths (defensive coding to avoid format errors and/or truncation);
        data _DISFORMATS;
        set indata.&DISTABLE.(obs=0);
        run;
        data _DIAFORMATS;
        set indata.&DIATABLE.(obs=0);
        run;
        data _PROCFORMATS;
        set indata.&PROCTABLE.(obs=0);
        run;

        %if %eval(&scdmver1. = 7) %then %let CodeType=CodeType;
        %else %let CodeType=Rx_CodeType;

        %MACRO WRAPPER;
        %IF &Behave.=RX %THEN %DO;          
            data _VC;
            retain CombGroup CombDescr;
            set _DISFORMATS /*Empty*/
                _one;
            format Rxdate mmddyy10.;
            Rxdate=AdateB;
            Rx=CombCode;
            &CodeType='CB';
            RxSup=EdateB-AdateB+1;
            RxAmt=1;
            keep Patid Rxdate Rx &CodeType RxSup RxAmt proc CombGroup CombDescr;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCDrug force;
            delete _VC;
            quit;
        %END;
        %IF &Behave.=DX %THEN %DO;          
            data _VC;
            retain CombGroup CombDescr;
            set _DIAFORMATS _one;
            EncType=CombEncType;
            Dx=CombCode;
            Dx_CodeType='CB';
            PDX=CombPDX;
            do Adate=AdateB to EdateB;
                output;
            end;
            keep Patid Adate EncType Dx Dx_CodeType PDX proc CombGroup CombDescr;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCDiag force;
            delete _VC;
            quit;
        %END;

        %IF &Behave.=PX %THEN %DO;          
            data _VC;
            retain CombGroup CombDescr;
            set _PROCFORMATS _one;
            EncType=CombEncType;
            Px=CombCode;
            Px_CodeType='CB';
            do Adate=AdateB to EdateB;
                output;
            end;
            keep Patid Adate EncType Px Px_CodeType proc CombGroup CombDescr;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCProc force;
            delete _VC;
            quit;
        %END;
        %MEND WRAPPER;
        %WRAPPER;
    %END;*NCOMBGROUPS LOOP;

/*  options NOSERROR;
    %SYMDEL PXENVEL COMBFILE CVECT_PROC VECT_PROC TVECT_DIAG VECT_DIAG
            CVECT_DIAG NOBS DXENVEL TVECT_PROC MedCov DrugCov MedDrugCov;
    options SERROR;*/

    /************************************************/
    /* Keep the virtual records in a permanent file */
    /************************************************/  
    %MACRO PERMANENT;
        %ISDATA(dataset=_VCDiag);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &DPLOCALLIB.&outname._diag;
            set _VCDiag;
            drop proc;
            run;
        %END;
        %ELSE %DO;
            data &DPLOCALLIB.&outname._diag;
            retain CombGroup CombDescr;
            set _DIAFORMATS _one;
            EncType=CombEncType;
            Dx=CombCode;
            Dx_CodeType='CB';
            PDX=CombPDX;
            Adate=AdateB;
            stop;
            keep Patid Adate EncType Dx Dx_CodeType PDX proc CombGroup CombDescr;
            run;
        %END;
        %ISDATA(dataset=_VCDrug);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &DPLOCALLIB.&outname._drug;
            set _VCDrug;
            drop proc;
            run;
        %END;
        %ELSE %DO;
            data &DPLOCALLIB.&outname._drug;
            retain CombGroup CombDescr;
            set _DISFORMATS _one;
            format Rxdate mmddyy10.;
            Rxdate=AdateB;
            Rx=CombCode;
            &CodeType='CB';           
            RxSup=EdateB-AdateB+1;
            RxAmt=1;
            stop;
            keep Patid Rxdate Rx RxSup &CodeType RxAmt proc CombGroup CombDescr;
            run;
        %END;
        %ISDATA(dataset=_VCProc);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &DPLOCALLIB.&outname._proc;
            set _VCProc;
            drop proc;
            run;
        %END;
        %ELSE %DO;
            data &DPLOCALLIB.&outname._proc;
            retain CombGroup CombDescr;
            set _PROCFORMATS _one;
            EncType=CombEncType;
            Px=CombCode;
            Px_CodeType='CB';
            Adate=AdateB;
            stop;
            keep Patid Adate EncType Px Px_CodeType proc CombGroup CombDescr;
            run;
        %END;
    %MEND PERMANENT;
    %PERMANENT;

*link to MPs if tool ne used as standalone;
%PUT &COMBORUN.;
%IF %EVAL(&COMBORUN. eq 1) %THEN %DO;  *Combo was called within a MP;
    %ISDATA(dataset=_VCDrug);
    %IF %eval(&NOBS.>0) %THEN %DO;
        *Adding parameters to raw drug claims for MPs usage;
        proc sql noprint;
        create table _VCDrug2 as
        select CodeList.*, 
              1 as comb,
              Dispensing.Patid, 
              Dispensing.Rx,
              Dispensing.RxDate,
              Dispensing.RxSup,
              Dispensing.RxAmt,
              %if %eval(&scdmver1. > 7) %then %do;
              Dispensing.Rx_CodeType,
              %end; 
              0 as Proc
        from _ndc(drop=comb) as CodeList right join
            _VCDrug (where=(RxSup > 0)) as Dispensing
        on Dispensing.Rx = CodeList.Code;
        quit;    

        data _VCDrug2;
        set _VCDrug2;
        if comb=1 then call symputx("CHKCOMBREC",1,'G');  
        if strip(&CodeType)="" then do;
        &CodeType="CB";
        cida=1;
        drop code %if %eval(&scdmver1 > 7) %then %do; codetype %end;;
        end;
        run;
    %END;

    %ISDATA(dataset=_VCDrug2);
    %IF %eval(&NOBS.>0) %THEN %DO;
        data worktemp.drugsComb(drop=cida comb);
          set _VCDrug2(where=(comb=1));
          if RxAmt>0 and RxSup>0;
        run;
    %END;
    %ISDATA(dataset=_VCDiag);
    %IF %eval(&NOBS.>0) %THEN %DO;
        proc append base= worktemp._diagextract data=_VCDiag(drop=CombGroup CombDescr) force;
        run;
    %END;
    %ISDATA(dataset=_VCProc);
    %IF %eval(&NOBS.>0) %THEN %DO;
        proc append base= worktemp._procextract data=_VCProc(drop=CombGroup CombDescr) force;
        run;
    %END;
%END;

*execution time;
    data _NULL_;
       temp=DATETIME();
       seconds=temp-&STARTCOMBO.;
       hours=int(seconds/3600);
       minutes=int((seconds-hours*3600)/60);
       seconds2=int((seconds-hours*3600-minutes*60));
       call symput('STOP',put(temp,best.));
       call symput('hours',put(hours,4.0));
       call symput('minutes',put(minutes,2.0));
       call symput('seconds',put(seconds2,2.0));
    run;

    %PUT TOTAL RUN TIME was &hours. h &minutes. m &seconds. s; 

        proc datasets library=work nolist nowarn;
        delete _VC:;
        quit;

%put NOTE: ********END OF MACRO: combo v2.11 ********;

%MEND combo;