import numpy as np
from copy import deepcopy
from AvenirCommon.Util import GBRange
from SpectrumCommon.Const.VW.VWConst import *
from SpectrumCommon.Const.VW.VWTags import *
from SpectrumCommon.Const.CS import *
from SpectrumCommon.Const.CS.CSInterventionIDs import *
from SpectrumCommon.Util.CS.CSUtil import *

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def CreateArr(val, len):
    return [ deepcopy(val) for element in range( len ) ] 

def Create1DSrcArr(len):
    result = ''
    if len > 1:
        result = CreateArr('', len)
    return result

def Create2DSrcArr(SrcLengthD1, SrcLengthD2):
    return CreateArr(Create1DSrcArr(SrcLengthD2), SrcLengthD1)

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def Simple_Value_Init(value):
    return {'value' : value}

def Value_1DSource_Init(value = 0, SrcLength = 1):
    return {'value' : value, 'source' : Create1DSrcArr(SrcLength)}

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                          DBC Methods                                                     /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def Get_DBC_YearRec(DataByCountry_DB):
    return {
        'exploreLiST'      : DataByCountry_DB['exploreLiST'], 
        'DBC_FirstYr'      : DataByCountry_DB['firstYear'], 
        'DBC_FinalYr'      : DataByCountry_DB['finalYear'],
        'ProjFirstYear'    : DataByCountry_DB['ProjFirstYear'], 
        'ProjFinalYearIdx' : DataByCountry_DB['ProjFinalYearIdx']
    }

def getVal(sourceData, index):
    result = 0
    if sourceData != CS_TG_Zeros: 
        result = sourceData[index]
    return result

def getVal2(sourceData, key, index):
    result = 0
    if key in sourceData:
        result = getVal(sourceData[key], index)
    return result

def Fill_Data_DBC(DBC_YearRec, destData, sourceData, tag = ''):
    
    def getLastIndex(sourceData):
        result = -1
        if sourceData != CS_TG_Zeros: 
            result = len(sourceData)-1
        return result
    
    exploreLiST = DBC_YearRec['exploreLiST']
    DBC_FirstYr = DBC_YearRec['DBC_FirstYr']
    DBC_FinalYr = DBC_YearRec['DBC_FinalYr']
    ProjFirstYear = DBC_YearRec['ProjFirstYear']
    ProjFinalYearIdx = DBC_YearRec['ProjFinalYearIdx']

    is_GNI_Per_Cap = tag == TG_GNIPerCap
    is_Food_Insecure = tag == TG_AFBalancedEnergySupp
    
    # Set the values for the DBC years 
    index = 0
    for year in GBRange(DBC_FirstYr, DBC_FinalYr):
        t = year - ProjFirstYear
        value = getVal(sourceData, index)
        if (not is_Food_Insecure) or (value > 0):
            if (t >= 0) and (t <= ProjFinalYearIdx):
                if (not is_GNI_Per_Cap) or (year < 2020):
                    destData[t] = value
        index += 1

    # Flatline earlier years 
    value = getVal(sourceData, 0)
    if (not is_Food_Insecure) or (value > 0):
        DBCFirstYrIdx = DBC_FirstYr - ProjFirstYear
        for t in GBRange(0, DBCFirstYrIdx):
            if (t <= ProjFinalYearIdx):
                if (not is_GNI_Per_Cap) or ((ProjFirstYear + t) < 2020):
                    destData[t] = value

    # Flatline later years 
    if not is_GNI_Per_Cap:
        value = getVal(sourceData, getLastIndex(sourceData))
        if (not is_Food_Insecure) or (value > 0):
            DBCFinalYrIdx = DBC_FinalYr - ProjFirstYear
            if DBCFinalYrIdx < 0:
                DBCFinalYrIdx = 0
            for t in GBRange(DBCFinalYrIdx, ProjFinalYearIdx):
                destData[t] = value

def GetDBCBaseYearIndex(DataByCountry_DB):
    #  Find the year to read from 
    yearToRead = DataByCountry_DB['ProjFirstYear']
    if yearToRead < DataByCountry_DB['firstYear']:
        yearToRead = DataByCountry_DB['firstYear']
    if yearToRead > DataByCountry_DB['finalYear'] :
        yearToRead = DataByCountry_DB['finalYear']

    # Find the index to read from
    index = 0
    found = False
    for DBC_Year in GBRange(DataByCountry_DB['firstYear'], DataByCountry_DB['finalYear']):
        if not found and (yearToRead != DBC_Year):
            index += 1
        else:
            found = True

    return index

def Fill_Data_DBC2(destData, destKey, sourceData, index):
    destData[destKey] = getVal(sourceData, index)
    
def Fill_Data_DBC3(destData, sourceData, sourceIndex, destIndex):
    destData[destIndex] = getVal(sourceData, sourceIndex)
     
def Fill_Data_DBC4(destData, sourceData, sourceKey1, sourceKey2, destIndex, divBy100 = False):
    if not sourceData == None:
        if sourceKey1 in sourceData:
            if sourceKey2 in sourceData[sourceKey1]:
                destData[destIndex] = sourceData[sourceKey1][sourceKey2]
                if divBy100:
                    destData[destIndex]  /= 100    
    
def Fill_Source_DBC(destData, sourceData, key):
    if 'sources' in sourceData:
        if key in sourceData['sources']:
            destData['source'] = sourceData['sources'][key]

def Fill_Sources_DBC(destData, sourceData, key, index):
    if 'sources' in sourceData:
        if key in sourceData['sources']:
            destData['source'][index] = sourceData['sources'][key]

def getDBCAgeKey(a):
    if a == CS_0t1months   : return '0 to 1'
    if a == CS_1t6months   : return '1 to 6'
    if a == CS_6t12months  : return '6 to 12'
    if a == CS_12t24months : return '12 to 24'
    if a == CS_24t60months : return '24 to 60'
    return ''

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                      Default data methods                                                /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def Fill_Data(destData, destIndex, sourceData, key):
    if key in sourceData:
        destData[destIndex] = sourceData[key]

def Fill_Source(destData, sourceData, key):
    if key in sourceData:
        destData['source'] = sourceData[key]

def Fill_1D_Sources(destData, index, sourceData, key):
    if key in sourceData:
        destData['source'][index] = sourceData[key]

def Fill_2D_Sources(destData, index1, index2, sourceData, key):
    if key in sourceData:
        destData['source'][index1][index2] = sourceData[key]

def getDefaultDataAgeKey(a):
    if a == CS_0t1months   : return '0-1 months'
    if a == CS_1t6months   : return '1-6 months'
    if a == CS_6t12months  : return '6-12 months'
    if a == CS_12t24months : return '12-23 months'
    if a == CS_24t60months : return '24-59 months'
    return ''

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def CalculateQuality(IVInfo, Readiness_DB, CSection_Cov_DB, YearRec, Coverage):
    BLOOD_TAG     = '<Blood>'
    BP_TAG        = '<BP>'
    URINE_TAG     = '<Urine>'
    CSECT_TAG     = '<Csect>'
    DB_FIRSTYEAR  = 1970
    DB_FINALYEAR  = 2030
    NUM_DB_YEARS  = DB_FINALYEAR - DB_FIRSTYEAR + 1
    ANC_INDEX     = 0
    DELIV_INDEX   = 1

    #   ========================================================================
    #                 Grab the data needed for Quality calculations
    #   ========================================================================

    Data = [{}, {}]

    AnchorYear = -1
    
    if not Readiness_DB == None:   

        AnchorYear = Readiness_DB['anchorYear']

        for key, record in Readiness_DB.items():
            if type(record) == dict and 'mstID' in record and str(record['mstID']) in IVInfo:            
                mstID = str(record['mstID'])
                currID = IVInfo[mstID]['currID']
                groupMstID = IVInfo[mstID]['groupMstID']
                
                index = -1
                if   groupMstID == CS_GroupMstID_Pregnancy   : index = ANC_INDEX
                elif groupMstID == CS_GroupMstID_Childbirth  : index = DELIV_INDEX
                
                if index > -1:
                    Data[index][key] = {
                        'currID'  : currID,
                        'mstID'   : mstID,
                        'Anchor'  : record['anchor'],
                        'Gap'     : np.zeros(NUM_DB_YEARS).tolist(),
                        'Quality' : np.zeros(NUM_DB_YEARS).tolist(),
                        'Ratio'   : np.zeros(NUM_DB_YEARS).tolist()
                    }

    temp = {BLOOD_TAG : None, BP_TAG : None, URINE_TAG : None, CSECT_TAG : None}

    if not CSection_Cov_DB == None:    
        for key, record in CSection_Cov_DB.items():
            for key2, value in record.items():            
                if temp[key2] == None:
                    temp[key2] = {
                        'values'  : np.zeros(NUM_DB_YEARS).tolist(),
                        'Gap'     : np.zeros(NUM_DB_YEARS).tolist(),
                        'Quality' : np.zeros(NUM_DB_YEARS).tolist(),
                        'Ratio'   : np.zeros(NUM_DB_YEARS).tolist()
                    }

                t = 0
                for year in GBRange(DB_FIRSTYEAR, DB_FINALYEAR):
                    if year == int(key):
                        temp[key2]['values'][t] = value
                    t += 1    
    
    ancblood = temp[BLOOD_TAG]
    ancbp    = temp[BP_TAG]
    ancurine = temp[URINE_TAG]
    csectcov = temp[CSECT_TAG]

    #   ======================================================================
    #                             Calculate Quality
    #   ======================================================================

    #   We have to use all years in the database because the anchoring year
    #   may fall outside of the projection years the anchoring year is the
    #   for which their is data for the quality elements of ANC and Delivery
    #   services identification of year that will serve as the anchor for the
    #   quality associated data items year by year calculation of average
    #   tracer values AnchorYear is the year for which there is default
    #   quality data ancblood, etc. are the tracers whose evolution will set
    #   the pace at which quality improves going forward in time and declines
    #   going back ward in time. We use an average for their evolution

    if ancblood != None and ancbp != None and ancurine != None and csectcov != None:
        
        AnchorYearIdx = -1
        
        AnchorTracer = [-1] * 2
        FirstYearIdx = [-1] * 2
        FinalYearIdx = [-1] * 2
        
        Tracer       = [[0] * NUM_DB_YEARS] * 2
        GapTracer    = [[0] * NUM_DB_YEARS] * 2
        DeltaTracer  = [[0] * NUM_DB_YEARS] * 2

        t = 0
        for year in GBRange(DB_FIRSTYEAR, DB_FINALYEAR):
            if year == AnchorYear:
                AnchorYearIdx = t

            # ancblood, ancbp and ancurine are in the Readiness Trend database.
            # There can be many years of data for these items.
            if ((ancblood['values'][t] > 0) and (ancbp['values'][t] > 0) and (ancurine['values'][t] > 0)):
                value = (ancblood['values'][t] + ancbp['values'][t] + ancurine['values'][t]) / 3
                Tracer[ANC_INDEX][t] = value
                if FirstYearIdx[ANC_INDEX] < 0: 
                    FirstYearIdx[ANC_INDEX] = t
                
                FinalYearIdx[ANC_INDEX] = t

            # CSection is in the Readiness Trend database.
            if ((csectcov['values'][t] > 0) and (Tracer[ANC_INDEX][t] > 0)):
                #bill was here 2022 02
                value = (ancblood['values'][t] + ancbp['values'][t] + ancurine['values'][t] + csectcov['values'][t] * 100) / 4
                Tracer[DELIV_INDEX][t] = value
                if FirstYearIdx[DELIV_INDEX] < 0: 
                    FirstYearIdx[DELIV_INDEX] = t
                
                FinalYearIdx[DELIV_INDEX] = t
            
            t += 1       
    
        for i in GBRange(ANC_INDEX, DELIV_INDEX):
        
        #   ========================================================================
        #                          Interpolate Tracer Data
        #   ========================================================================
        
            if (FirstYearIdx[i] > -1) and (FinalYearIdx[i] > -1):
                BeginInterpolate = FirstYearIdx[i]
                EndInterpolate = FirstYearIdx[i] + 1
                while (BeginInterpolate != FinalYearIdx[i]):
                    if (Tracer[i][EndInterpolate] > 0) and (Tracer[i][EndInterpolate - 1] == 0):
                        for t in GBRange(BeginInterpolate + 1, EndInterpolate - 1):
                            Tracer[i][t] = Tracer[i][BeginInterpolate] + (Tracer[i][EndInterpolate] - Tracer[i][BeginInterpolate]) * (t - BeginInterpolate) / (EndInterpolate - BeginInterpolate)
                        BeginInterpolate = EndInterpolate
                    
                    if (Tracer[i][EndInterpolate] > 0) and (Tracer[i][EndInterpolate - 1] > 0):
                        BeginInterpolate = EndInterpolate
                    EndInterpolate = EndInterpolate + 1
            
        #   Flatlining forward and backward
            t = 0
            for year in GBRange(DB_FIRSTYEAR, DB_FINALYEAR):
                if (FirstYearIdx[i] > -1) and (FinalYearIdx[i] > -1):
                    if t < FirstYearIdx[i]: Tracer[i][t] = Tracer[i][FirstYearIdx[i]]
                    if t > FinalYearIdx[i]: Tracer[i][t] = Tracer[i][FinalYearIdx[i]]            
                t += 1       
            
            #   Identifying the tracers and the quality associated data items for
            #   the anchoring year.
            if AnchorYearIdx > -1:
                if (FirstYearIdx[i] > -1) and (FinalYearIdx[i] > -1):
                    AnchorTracer[i] = Tracer[i][AnchorYearIdx]
            
        #   Defining the gaps between the tracers and the default anchors. Either
        #   0 or 100 depending on whether the year is before or after the anchor
        #   year.
            if AnchorYearIdx > -1:
                t = 0
                for year in GBRange(DB_FIRSTYEAR, DB_FINALYEAR):
                    if (FirstYearIdx[i] > -1) and (FinalYearIdx[i] > -1):
                        GapTracer[i][t] = AnchorTracer[i]

                        if Tracer[i][t] >= Tracer[i][AnchorYearIdx]:
                            GapTracer[i][t] = 100 - AnchorTracer[i]

                        if Tracer[i][t] > 0:
                            DeltaTracer[i][t] = Tracer[i][t] - Tracer[i][AnchorYearIdx]

                        for key, record in Data[i].items():
                            record['Gap'][t] = record['Anchor']
                            if Tracer[i][t] >= Tracer[i][AnchorYearIdx]:
                                record['Gap'][t] = 100 - record['Anchor']

                            record['Ratio'][t] = 0
                            if GapTracer[i][t] > 0:
                                record['Ratio'][t] = record['Gap'][t] / GapTracer[i][t]

                            if Tracer[i][t] > 0:
                                record['Quality'][t] = record['Anchor'] + record['Ratio'][t] * DeltaTracer[i][t]
                    t += 1 

        #   ======================================================================
        #                    Set the calculated Quality into LiST
        #   ======================================================================

            if (FirstYearIdx[i] > -1) and (FinalYearIdx[i] > -1):
                for key, record in Data[i].items():
                    for t in GBRange(0, YearRec['ProjFinalYearIdx']):
                        dataYearIdx = 0

                        if YearRec['ProjFirstYear'] + t > DB_FIRSTYEAR:
                            dataYearIdx = YearRec['ProjFirstYear'] + t - DB_FIRSTYEAR

                        if dataYearIdx > NUM_DB_YEARS - 1:
                            dataYearIdx = NUM_DB_YEARS - 1

                        Coverage['quality'][record['currID']][t] = record['Quality'][dataYearIdx]

                        
                        UtilMstID = -1
                        if   IVInfo[record['mstID']]['QualityMode'] == CS_ANC1Mode: UtilMstID = CS_IV_ANC1_MST_ID
                        elif IVInfo[record['mstID']]['QualityMode'] == CS_ANC4Mode: UtilMstID = CS_IV_ANC4_MST_ID
                        elif IVInfo[record['mstID']]['QualityMode'] == CS_FascDelivMode: UtilMstID = CS_IV_HealthFacDelivery_MST_ID

                        if UtilMstID in IVInfo:
                            Utilization = Coverage['value'][IVInfo[UtilMstID]['currID']][t] / 100
                            value = ((record['Quality'][dataYearIdx] / 100) * Utilization) * 100
                            Coverage['value'][record['currID']][t] = value

    #   For now, KMC Quality will be zero even though there is readiness data
    #   in the database.  (Ticket #590, 6.14.22)
        for t in GBRange(0, YearRec['ProjFinalYearIdx']):
            Coverage['quality'][IVInfo[CS_IV_KangarooMothCare_MST_ID]['currID']][t] = 0
            Coverage['value'][IVInfo[CS_IV_KangarooMothCare_MST_ID]['currID']][t] = 0

    return Coverage

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                           Z Scores                                                       /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def CSGetAgeWeight(a):
    if   a == CS_0t1months   : return (1 - 0) / 60
    elif a == CS_1t6months   : return (6 - 1) / 60
    elif a == CS_6t12months  : return (12 - 6) / 60
    elif a == CS_12t24months : return (24 - 12) / 60
    elif a == CS_24t60months : return (60 - 24) / 60
    else                     : return 1

# Formulae are from "Stunting and Wasting Indicators 7aug2015.xlsx", cells D8 to D11.
def CSZScoreCalc(curYrSingle, firstYrSingle, firstYrGT1SD, firstYr1t2SD, firstYr2t3SD, firstYrGT3SD, 
                 valueGT1SD, value1t2SD, value2t3SD, valueGT3SD):

    if curYrSingle > firstYrSingle:
        temp = max(curYrSingle, 2.275)

        if curYrSingle > 2.275: 
            temp2 = 0
        else: 
            temp2 = 2.275 - curYrSingle

        temp3 = (firstYrSingle - temp) / (firstYrSingle - 2.275)

        valueGT1SD = firstYrGT1SD - (temp3 * (firstYrGT1SD - 84.1345))
        value1t2SD = firstYr1t2SD - (temp3 * (firstYr1t2SD - 13.5905)) + temp2
        value2t3SD = firstYr2t3SD - (temp3 * (firstYr2t3SD - 2.14)) - (temp2 / 2.275) * 2.14
        valueGT3SD = firstYrGT3SD - (temp3 * (firstYrGT3SD  - 0.135)) - (temp2 / 2.275) * 0.135
    else:
        temp = (curYrSingle - firstYrSingle) / (100 - firstYrSingle)

        valueGT1SD = firstYrGT1SD + temp * (0.13498 - firstYrGT1SD)
        value1t2SD = firstYr1t2SD + temp * (2.14 - firstYr1t2SD)
        value2t3SD = firstYr2t3SD + temp * (13.59046 - firstYr2t3SD)
        valueGT3SD = firstYrGT3SD + temp * (84.13453 - firstYrGT3SD)

def CSConvertIndicatorToZScores(FirstYrIdx, FinalYrIdx, distr, singleDistr):
    if FirstYrIdx >= len(singleDistr):
        exit

    firstYrSingle  = singleDistr[FirstYrIdx]
    
    for t in GBRange(FirstYrIdx, FinalYrIdx):
        curYrSingle = singleDistr[t]
        
        for a in GBRange(CS_0t1months, CS_24t60months):
            firstYrGT1SD = distr[CS_GT1STD][a][FirstYrIdx]
            firstYr1t2SD = distr[CS_1t2STD][a][FirstYrIdx]
            firstYr2t3SD = distr[CS_2t3STD][a][FirstYrIdx]
            firstYrGT3SD = distr[CS_GT3STD][a][FirstYrIdx]

            valueGT1SD = 0
            value1t2SD = 0
            value2t3SD = 0
            valueGT3SD = 0
            
            CSZScoreCalc(curYrSingle, firstYrSingle, firstYrGT1SD, firstYr1t2SD,
                firstYr2t3SD, firstYrGT3SD, valueGT1SD, value1t2SD, value2t3SD,
                valueGT3SD)

            distr[CS_GT1STD][a][t] = valueGT1SD
            distr[CS_1t2STD][a][t] = value1t2SD
            distr[CS_2t3STD][a][t] = value2t3SD
            distr[CS_GT3STD][a][t] = valueGT3SD

def CSConvertZScoresToIndicator(FirstYrIdx, FinalYrIdx, distr, singleDistr):
    for t in GBRange(FirstYrIdx, FinalYrIdx):
        singleDistr[t] = 0.0

        for a in GBRange(CS_0t1months, CS_24t60months):
            singleDistr[t] = singleDistr[t] + (distr[CS_2t3STD][a][t] + distr[CS_GT3STD][a][t]) * CSGetAgeWeight(a)

def CSUpdateDistributions(FirstYrIdx, FinalYrIdx, UseDetailedDistr, distr, singleDistr):
    # Calculate stunting to fill proper ModVars. If single stunting
    # is selected in the radio groups, then calculate detailed values. If detailed
    # stunting is selected in the radio groups, then calculate single values
    if UseDetailedDistr: 
        CSConvertZScoresToIndicator(FirstYrIdx, FinalYrIdx, distr, singleDistr)
    else: 
        CSConvertIndicatorToZScores(FirstYrIdx, FinalYrIdx, distr, singleDistr)

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def getCurrID(IVInfo, mstID) : 
    if mstID in IVInfo:
        return IVInfo[mstID]['currID']
    return None

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def StuntWasteDistr_RegionalInit(RegionData_DB, final_year_idx, tag):
    result = Value_1DSource_Init(np.zeros((CS_GT3STD+1, CS_24t60months+1, final_year_idx+1)).tolist())
    
    sourceData = RegionData_DB[tag]

    def fillAgeData(t, a, key):
        result['value'][CS_GT1STD][a][t] = sourceData[key]['>-1 Z']
        result['value'][CS_1t2STD][a][t] = sourceData[key]['-2 to -1Z']
        result['value'][CS_2t3STD][a][t] = sourceData[key]['-3 to -2 Z']
        result['value'][CS_GT3STD][a][t] = sourceData[key]['<-3 Z']

    for t in GBRange(0, final_year_idx): 
        fillAgeData(t, CS_0t1months, '0-1  month')
        fillAgeData(t, CS_1t6months, '1-5 months')
        fillAgeData(t, CS_6t12months, '6-11 months')
        fillAgeData(t, CS_12t24months, '12-23 months')
        fillAgeData(t, CS_24t60months, '24-59 months')

    Fill_Source_DBC(result, sourceData, '0')   

    return result

def BFPrev_RegionalInit(RegionData_DB, final_year_idx):
    result = Value_1DSource_Init(np.zeros((CS_NotBF+1, CS_24t60months+1, final_year_idx+1)).tolist())
       
    sourceData = RegionData_DB[TG_BreastFeeding]
    
    def fillAgeData(t, bf, a, key, key2):
        if key2 in sourceData[key]:
            result['value'][bf][a][t] = sourceData[key][key2]
    
    def fillBFData(t, bf, keySet):
        for key in keySet:
            fillAgeData(t, bf, CS_0t1months, key, 'less than 1')
            fillAgeData(t, bf, CS_1t6months, key, '1 to 5')
            fillAgeData(t, bf, CS_6t12months, key, '6 to 11')
            fillAgeData(t, bf, CS_12t24months, key, '12 to 23')

    for t in GBRange(0, final_year_idx):
        fillBFData(t, CS_ExclusiveBF, ['Exclusive breastfeeding'])
        fillBFData(t, CS_PredominantBF, ['Predominant breastfeeding'])
        fillBFData(t, CS_PartialBF, ['Partial breastfeeding', 'Any breastfeeding'])
        fillBFData(t, CS_NotBF, ['Not breastfeeding'])   
         
    Fill_Source_DBC(result, sourceData, '0')      
    
    return result

def SetRegionalValues(CSModvars, RegionData_DB, final_year_idx):
    hasStunting = False
    hasWasting = False
    hasBreastFeed = False

    for sd in GBRange(CS_GT1STD, CS_GT3STD):
        for age in GBRange(CS_AgeSummary, CS_24t60months):
            for t in GBRange(0, final_year_idx):
                if not CSModvars[CS_StuntDistrTag]['value'][sd][age][t] == 0:
                    hasStunting = True

    for sd in GBRange(CS_GT1STD, CS_GT3STD):
        for age in GBRange(CS_AgeSummary, CS_24t60months):
            for t in GBRange(0, final_year_idx):
                if not CSModvars[CS_WasteDistrTag]['value'][sd][age][t] == 0:
                    hasWasting = True

    for bf in GBRange(CS_ExclusiveBF, CS_NotBF):
        for age in GBRange(CS_AgeSummary, CS_24t60months):
            for t in GBRange(0, final_year_idx):
                if not CSModvars[CS_BFPrevTag]['value'][bf][age][t] == 0:
                    hasBreastFeed = True    

    if not hasStunting: CSModvars[CS_StuntDistrTag] = StuntWasteDistr_RegionalInit(RegionData_DB, final_year_idx, TG_StuntingDist)
    if not hasWasting: CSModvars[CS_WasteDistrTag] = StuntWasteDistr_RegionalInit(RegionData_DB, final_year_idx, TG_WastingDist)
    if not hasBreastFeed: CSModvars[CS_BFPrevTag] = BFPrev_RegionalInit(RegionData_DB, final_year_idx)

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def CSCalcCoverageRelationships(IVInfo, CalcArray, CalcSet, ReadingDatabase, scalingMethod = CS_SG_ScaleTargPerc):
    
    def sumValues(IVInfo, CalcArray, destMstID, sourceMstIDSet):    
        destCurrID = getCurrID(IVInfo, destMstID)
        if not destCurrID == None:
            CalcArray[destCurrID] = 0
            for mstID in sourceMstIDSet: 
                sourceCurrID = getCurrID(IVInfo, mstID)
                if not sourceCurrID == None:
                    CalcArray[destCurrID] += CalcArray[sourceCurrID]
    
    if CS_Pentavalent_CCFI in CalcSet:
        CS_IV_PentaVacc = getCurrID(IVInfo, CS_IV_PentaVacc_MST_ID)
        if not CS_IV_PentaVacc == None:
            
            CS_IV_DPT_Vacc = getCurrID(IVInfo, CS_IV_DPT_Vacc_MST_ID)
            if not CS_IV_DPT_Vacc == None: CalcArray[CS_IV_DPT_Vacc] = CalcArray[CS_IV_PentaVacc]
            
            CS_IV_HibVacc = getCurrID(IVInfo, CS_IV_HibVacc_MST_ID)
            if not CS_IV_HibVacc == None: CalcArray[CS_IV_HibVacc]  = CalcArray[CS_IV_PentaVacc]
            
            CS_IV_HepBVacc = getCurrID(IVInfo, CS_IV_HepBVacc_MST_ID)
            if not CS_IV_HepBVacc == None: CalcArray[CS_IV_HepBVacc] = CalcArray[CS_IV_PentaVacc]  
    
    if ReadingDatabase:
        CS_IV_InjectAntibNeonSeps = getCurrID(IVInfo, CS_IV_InjectAntibNeonSeps_MST_ID)
        CS_IV_ParenteralAdminOfAntibiotics = getCurrID(IVInfo, CS_IV_ParenteralAdminOfAntibiotics_MST_ID)   
        if not CS_IV_InjectAntibNeonSeps == None and not CS_IV_ParenteralAdminOfAntibiotics == None:
            CalcArray[CS_IV_InjectAntibNeonSeps] = CalcArray[CS_IV_ParenteralAdminOfAntibiotics]

        CS_IV_VitATrMeas = getCurrID(IVInfo, CS_IV_VitATrMeas_MST_ID)
        CS_IV_VitASupp = getCurrID(IVInfo, CS_IV_VitASupp_MST_ID)
        if not CS_IV_VitATrMeas == None and not CS_IV_VitASupp == None:
            CalcArray[CS_IV_VitATrMeas] = CalcArray[CS_IV_VitASupp]
    
    sumValues(IVInfo, CalcArray, CS_IV_CaseMgmtPrematBabies_MST_ID, [CS_IV_KangarooMothCare_MST_ID, CS_IV_FullSuppCarePremat_MST_ID, CS_IV_iKMC_MST_ID])
    sumValues(IVInfo, CalcArray, CS_IV_CaseMgmtNeonSepsPneu_MST_ID, [CS_IV_OralAntibNeonSeps_MST_ID, CS_IV_InjectAntibNeonSeps_MST_ID, CS_IV_FullSuppCareNeonSepsPneu_MST_ID])
    sumValues(IVInfo, CalcArray, CS_IV_MicronSupp_MST_ID, [CS_IV_MultiMicronSuppPreg_MST_ID, CS_IV_IronSuppPreg_MST_ID])
   
#  If scaling by percentage points instead of scaling to a target percentage 
#  (which the user may do, for example, in LiST SG), do some further checks. 
    if scalingMethod == CS_SG_ScalePercPts:
        CS_IV_MicronSupp = getCurrID(IVInfo, CS_IV_MicronSupp_MST_ID)
        CS_IV_CaseMgmtPrematBabies = getCurrID(IVInfo, CS_IV_CaseMgmtPrematBabies_MST_ID)
        CS_IV_CaseMgmtNeonSepsPneu = getCurrID(IVInfo, CS_IV_CaseMgmtNeonSepsPneu_MST_ID)
   
        if not CS_IV_MicronSupp == None:
            if CalcArray[CS_IV_MicronSupp] > 100.0: CalcArray[CS_IV_MicronSupp] = 100.0
            if CalcArray[CS_IV_MicronSupp] < -100.0: CalcArray[CS_IV_MicronSupp] = -100.0
        
        if not CS_IV_CaseMgmtPrematBabies == None:
            if CalcArray[CS_IV_CaseMgmtPrematBabies] > 100.0: CalcArray[CS_IV_CaseMgmtPrematBabies] = 100.0
            if CalcArray[CS_IV_CaseMgmtPrematBabies] < -100.0: CalcArray[CS_IV_CaseMgmtPrematBabies] = -100.0
        
        if not CS_IV_CaseMgmtNeonSepsPneu == None:
            if CalcArray[CS_IV_CaseMgmtNeonSepsPneu] > 100.0: CalcArray[CS_IV_CaseMgmtNeonSepsPneu] = 100.0
            if CalcArray[CS_IV_CaseMgmtNeonSepsPneu] < -100.0: CalcArray[CS_IV_CaseMgmtNeonSepsPneu] = -100.0

def CSRecalcDataForDataByCountry(CSModvars, final_year_idx):
    numIntervs = CSModvars[CS_NumIntervsTag]['value']
    
    # Recalc data for data by country
    for t in GBRange(0, final_year_idx):  
        OneDimCoverage = [0] * (numIntervs + 1)
        
        for i in GBRange(0, numIntervs):  
            OneDimCoverage[i] = CSModvars[CS_CoverageTag]['value'][i][t]
        
        CSCalcCoverageRelationships(CSModvars[CS_IVInfoTag], OneDimCoverage, CSModvars[CS_CalcCovFromInputsTag], True)
        
        for i in GBRange(0, numIntervs):  
            CSModvars[CS_CoverageTag]['value'][i][t] = OneDimCoverage[i]

def ModifyCoverage(CSModvars, final_year_idx):
    # Recalc data for data by country 
    CSRecalcDataForDataByCountry(CSModvars, final_year_idx)
  
    # Set BF improvement to exclusive breastfeeding value from health status  
    CS_IV_ChangesInBF = getCurrID(CSModvars[CS_IVInfoTag], str(CS_MstBFPromotion))
    if not CS_IV_ChangesInBF == None:         
        for t in GBRange(0, final_year_idx):  
            CSModvars[CS_CoverageTag]['value'][CS_IV_ChangesInBF][t] = CSModvars[CS_BFPrevTag]['value'][CS_ExclusiveBF][CS_1t6months][t]

    CSUpdateDistributions(0, final_year_idx, True, CSModvars[CS_StuntDistrTag]['value'], CSModvars[CS_SingleStuntDistrTag]['value'])
    CSUpdateDistributions(0, final_year_idx, True, CSModvars[CS_WasteDistrTag]['value'], CSModvars[CS_SingleWasteDistrTag]['value'])

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def CS_Linkage_HSNutriDefiOnChange(CSModvars):
    # Set affected fractions that rely on the HealthStatus variable.
    for a in GBRange(CS_AgeSummary, CS_24t60months):
        # Vitamin A
        value = CSModvars[CS_NutrDeficTag]['value'][CS_VitADefic]/100
        CSModvars[CS_DiarrIncReductionTag]['value'][CS_Default][CS_VitAPTwoDoses_DiarInterv][CS_AffecFract][a] = value
        CSModvars[CS_DiarrIncReductionTag]['value'][CS_Data][CS_VitAPTwoDoses_DiarInterv][CS_AffecFract][a] = value

        # Zinc
        value = CSModvars[CS_NutrDeficTag]['value'][CS_ZincDefic]/100
        CSModvars[CS_DiarrIncReductionTag]['value'][CS_Default][CS_ZincP_DiarInterv][CS_AffecFract][a] = value
        CSModvars[CS_DiarrIncReductionTag]['value'][CS_Data][CS_ZincP_DiarInterv][CS_AffecFract][a] = value

        # Zinc
        value = CSModvars[CS_NutrDeficTag]['value'][CS_ZincDefic]/100
        CSModvars[CS_PneumIncReductionTag]['value'][CS_Default][CS_ZincP_PneumInterv][CS_AffecFract][a] = value
        CSModvars[CS_PneumIncReductionTag]['value'][CS_Data][CS_ZincP_PneumInterv][CS_AffecFract][a] = value

        for d in GBRange(1, CS_MaxDeathCauses):
            # Vitamin A 
            CS_IV_VitASupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_VitASupp_MST_ID)
            if not CS_IV_VitASupp == None:
                value = CSModvars[CS_NutrDeficTag]['value'][CS_VitADefic]/100
                CSModvars[CS_ChildEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_VitASupp][d][a] = value
                CSModvars[CS_ChildEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_VitASupp][d][a] = value

            # Zinc
            CS_IV_ZincSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_ZincSupp_MST_ID)
            if not CS_IV_ZincSupp == None:            
                value = CSModvars[CS_NutrDeficTag]['value'][CS_ZincDefic]/100
                CSModvars[CS_ChildEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_ZincSupp][d][a] = value
                CSModvars[CS_ChildEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_ZincSupp][d][a] = value

def CS_Linkage_HSMatNutriDefiOnChange(CSModvars):
    # Set affected fractions that rely on the HealthStatus variable.
    VitADefic = CSModvars[CS_VitADefInPWTag]['value'] / 100
    ZincDefic = CSModvars[CS_NutrDeficTag]['value'][CS_ZincDefic] / 100
    CalcDefic = CSModvars[CS_NutrDeficTag]['value'][CS_CalcDefic] / 100
    FolDefic  = CSModvars[CS_NutrDeficTag]['value'][CS_FolDefic] / 100
    IronDefic = CSModvars[CS_NutrDeficTag]['value'][CS_IronDefic] / 100

    for d in GBRange(1, CS_Mat_MaxDeathCauses):
        CS_IV_FolicAcidSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_FolicAcidSupp_MST_ID)
        if not CS_IV_FolicAcidSupp == None:          
            CSModvars[CS_MatEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_FolicAcidSupp][d] = FolDefic
            CSModvars[CS_MatEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_FolicAcidSupp][d] = FolDefic

        CS_IV_ZincFort = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_ZincFort_MST_ID)
        if not CS_IV_ZincFort == None:          
            CSModvars[CS_MatEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_ZincFort][d] = ZincDefic
            CSModvars[CS_MatEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_ZincFort][d] = ZincDefic

        CS_IV_CalciumSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_CalciumSupp_MST_ID)
        if not CS_IV_CalciumSupp == None:          
            CSModvars[CS_MatEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_CalciumSupp][d] = CalcDefic
            CSModvars[CS_MatEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_CalciumSupp][d] = CalcDefic

        CS_IV_ZincSuppInPreg = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_ZincSuppInPreg_MST_ID)
        if not CS_IV_ZincSuppInPreg == None:          
            CSModvars[CS_MatEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_ZincSuppInPreg][d] = ZincDefic
            CSModvars[CS_MatEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_ZincSuppInPreg][d] = ZincDefic
    
    FolicAcidDeficPrev = CSModvars[CS_FolicAcidDeficPrevTag]['value']
    
    for d in GBRange(1, CS_SB_MaxCauses):
        CS_IV_CalciumSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_CalciumSupp_MST_ID)
        if not CS_IV_CalciumSupp == None:         
            CSModvars[CS_SBEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_CalciumSupp][d] = CalcDefic
            CSModvars[CS_SBEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_CalciumSupp][d] = CalcDefic

        CS_IV_FolicAcidSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_FolicAcidSupp_MST_ID)
        if not CS_IV_FolicAcidSupp == None:          
            CSModvars[CS_SBEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_FolicAcidSupp][d] = FolDefic * FolicAcidDeficPrev
            CSModvars[CS_SBEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_FolicAcidSupp][d] = FolDefic * FolicAcidDeficPrev

    for d in GBRange(1, CS_MaxDeathCauses):
        value = FolDefic
        if d == CS_NNCongen:
            value = FolDefic * CSModvars[CS_CongenitalDueToNTDsAFTag]['value']
        
        for a in GBRange(CS_AgeSummary, CS_24t60months):
            CS_IV_FolicAcidSupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_FolicAcidSupp_MST_ID)
            if not CS_IV_FolicAcidSupp == None:       
                CSModvars[CS_ChildEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_FolicAcidSupp][d][a] = value
                CSModvars[CS_ChildEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_FolicAcidSupp][d][a] = value

            CS_IV_NNVitASupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_NNVitASupp_MST_ID)
            if not CS_IV_NNVitASupp == None:        
                CSModvars[CS_ChildEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_NNVitASupp][d][a] = VitADefic
                CSModvars[CS_ChildEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_NNVitASupp][d][a] = VitADefic

    for term in GBRange(CS_PreTermSGA, CS_TermAGA):
        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_CalciumSupp][term] = CalcDefic
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_CalciumSupp][term] = CalcDefic

        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_FolicAcidFort][term] = FolDefic
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_FolicAcidFort][term] = FolDefic

        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_ZincFort][term] = ZincDefic
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_ZincFort][term] = ZincDefic

        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_ZincSuppInPreg][term] = ZincDefic
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_ZincSuppInPreg][term] = ZincDefic

    for n in GBRange(CS_Preg, CS_NonPreg):
        CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Default][n][CS_IronFolatePW_MatAnemia][CS_AffecFract] = IronDefic
        CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Data][n][CS_IronFolatePW_MatAnemia][CS_AffecFract] = IronDefic

        CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Default][n][CS_IronFortNPW_MatAnemia][CS_AffecFract] = IronDefic
        CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Data][n][CS_IronFortNPW_MatAnemia][CS_AffecFract] = IronDefic

def CS_Linkage_HSFalciparumIPTpSheetOnChange(CSModvars):
    value = CSModvars[CS_PercExposedFalciparumTag]['value'] / 100

    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Data][CS_Preg][CS_IPTP_MatAnemia][CS_AffecFract] = value
    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Default][CS_Preg][CS_IPTP_MatAnemia][CS_AffecFract] = value

    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Data][CS_Preg][CS_ITN_MatAnemia][CS_AffecFract] = value
    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Default][CS_Preg][CS_ITN_MatAnemia][CS_AffecFract] = value

    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Data][CS_NonPreg][CS_ITN_MatAnemia][CS_AffecFract] = value
    CSModvars[CS_ImpactOnMatAnemiaTag]['value'][CS_Default][CS_NonPreg][CS_ITN_MatAnemia][CS_AffecFract] = value

def CS_Linkage_HSPerCapitaIncOnChange(CSModvars):
    value = CSModvars[CS_FoodInsecureTag]['value'][CSModvars[CS_CovBYTag]['value']]
    if value == -1:
        value = CSModvars[CS_PerCapitaIncTag]['value'][CSModvars[CS_CovBYTag]['value']]

    value /= 100

    CS_IV_BalEnergySupp = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_BalEnergySupp_MST_ID)
    if not CS_IV_BalEnergySupp == None:        
        for d in GBRange(1, CS_Mat_MaxDeathCauses):
            CSModvars[CS_MatEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_BalEnergySupp][d] = value
            CSModvars[CS_MatEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_BalEnergySupp][d] = value
        
        for d in GBRange(1, CS_SB_MaxCauses):
            CSModvars[CS_SBEffTag]['value'][CS_Default][CS_AffecFract][CS_IV_BalEnergySupp][d] = value
            CSModvars[CS_SBEffTag]['value'][CS_Data][CS_AffecFract][CS_IV_BalEnergySupp][d] = value
    
    for term in GBRange(CS_PreTermSGA, CS_TermAGA):
        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_BalEnergy][term] = value
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_BalEnergy][term] = value

def CS_Linkage_HSPercWom15t49LowBMIOnChange(CSModvars):
    # Set balanced energy affected fraction and stillbirth balanced energy
    # affected fraction = % of women aged 15-49 with low BMI.
    value = CSModvars[CS_PercWom15t49LowBMITag]['value']
    for term in GBRange(CS_PreTermSGA, CS_TermAGA):        
            CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_MultMicroLowBMI][term] = value
            CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_MultMicroLowBMI][term] = value

            CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_MultMicroNormBMI][term] = 1-value
            CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_MultMicroNormBMI][term] = 1-value

def CS_Linkage_HSSyphilisAmongWRAOnChange(CSModvars):
    # Set balanced energy affected fraction and stillbirth balanced energy
    # affected fraction = % of women aged 15-49 with low BMI.
    value = CSModvars[CS_SyphilisAmongWRATag]['value']
    for term in GBRange(CS_PreTermSGA, CS_TermAGA):        
            CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_SyphilisTreat][term] = value
            CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_SyphilisTreat][term] = value

def CS_Linkage_HSPathogensDiarrOnChange(CSModvars, defaultFlag):
    for age in GBRange(CS_0t1months, CS_24t60months):  
        # Incidence
        # Severe Diarrhea Rotavirus
        CSModvars[CS_SevDiarrIncReductionTag]['value'][defaultFlag][
            CS_RotavirusVacc_SevDiarInterv][CS_AffecFract][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_Sevr][CS_RotaPathDiar][age]
        
        # All Diarrhea Rotavirus
        CSModvars[CS_DiarrIncReductionTag]['value'][defaultFlag][
            CS_RotavirusVacc_DiarInterv][CS_AffecFract][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_All][CS_RotaPathDiar][age]
        
        # All Diarrhea Vaccine B
        CSModvars[CS_DiarrIncReductionTag]['value'][defaultFlag][
            CS_DiarVaccPathB_DiarInterv][CS_AffecFract][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_All][CS_PathBDiar][age]
        
        # All Diarrhea Vaccine C
        CSModvars[CS_DiarrIncReductionTag]['value'][defaultFlag][
            CS_DiarVaccPathC_DiarInterv][CS_AffecFract][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_All][CS_PathCDiar][age]
        
        # Detailed vaccine efficacy
        for dose in GBRange(1, CS_MaxVaccDose): 
            # Rota
            CSModvars[CS_DetVaccEffTag]['value'][defaultFlag][CS_AffecFract][
                CS_Rota_Det][CS_Diarrhea][dose][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_Fatal][CS_RotaPathDiar][age]
            
            # Vacc B
            CSModvars[CS_DetVaccEffTag]['value'][defaultFlag][CS_AffecFract][
                CS_VaccB_Det][CS_Diarrhea][dose][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_Fatal][CS_PathBDiar][age]
            
            # Vacc C
            CSModvars[CS_DetVaccEffTag]['value'][defaultFlag][CS_AffecFract][
                CS_VaccC_Det][CS_Diarrhea][dose][age] = CSModvars[CS_DiarrPathTag]['value'][
                CS_Distr_Fatal][CS_PathCDiar][age]
            
        # Standard vaccine efficacy
        # Rota
        CS_IV_RotavirusVacc = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_RotavirusVacc_MST_ID)
        if not CS_IV_RotavirusVacc == None:        
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_RotavirusVacc][CS_Diarrhea][age] = CSModvars[CS_DiarrPathTag]['value'][
                    CS_Distr_Fatal][CS_RotaPathDiar][age]
        
        # Vacc B
        CS_IV_DiarVaccPathB = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_DiarVaccPathB_MST_ID)
        if not CS_IV_DiarVaccPathB == None:        
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_DiarVaccPathB][CS_Diarrhea][age] = CSModvars[CS_DiarrPathTag]['value'][
                    CS_Distr_Fatal][CS_PathBDiar][age]

        # Vacc C
        CS_IV_DiarVaccPathC = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_DiarVaccPathC_MST_ID)
        if not CS_IV_DiarVaccPathC == None:        
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_DiarVaccPathC][CS_Diarrhea][age] = CSModvars[CS_DiarrPathTag]['value'][
                    CS_Distr_Fatal][CS_PathCDiar][age]

def CS_Linkage_HSPathogensPneumOnChange(CSModvars, defaultFlag):
    for age in GBRange(CS_0t1months, CS_24t60months):
        # Incidence
        # HiB
        CSModvars[CS_PneumIncReductionTag]['value'][
            defaultFlag][CS_HibVacc_PneumInterv][CS_AffecFract][age] = CSModvars[CS_PneumPathTag]['value'][
                CS_Distr_Sevr][CS_HibPathPneumVT][age]

        # PCV
        CSModvars[CS_PneumIncReductionTag]['value'][
            defaultFlag][CS_PneumVacc_PneumInterv][CS_AffecFract][age] = CSModvars[CS_PneumPathTag]['value'][
                CS_Distr_Sevr][CS_PneumocPathPneumVT][age]

        # Detailed vaccine efficacy
        for dose in GBRange(1, CS_MaxVaccDose):
            # HiB
            CSModvars[CS_DetVaccEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_HiB_Det][CS_Pneumonia][dose][age] = CSModvars[CS_PneumPathTag]['value'][
                    CS_Distr_Fatal][CS_HibPathPneumVT][age]

            # PCV
            CSModvars[CS_DetVaccEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_PCV_Det][CS_Pneumonia][dose][age] = CSModvars[CS_PneumPathTag]['value'][
                    CS_Distr_Fatal][CS_PneumocPathPneumVT][age]

        # Standard vaccine efficacy
        # HiB
        CS_IV_HibVacc = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_HibVacc_MST_ID)
        if not CS_IV_HibVacc == None:        
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_HibVacc][CS_Pneumonia][age] = CSModvars[CS_PneumPathTag]['value'][
                    CS_Distr_Fatal][CS_HibPathPneumVT][age]
        
        # PCV
        CS_IV_PneuVacc = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_PneuVacc_MST_ID)
        if not CS_IV_PneuVacc == None:        
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_PneuVacc][CS_Pneumonia][age] = CSModvars[CS_PneumPathTag]['value'][
                    CS_Distr_Fatal][CS_PneumocPathPneumVT][age]

def CS_Linkage_HSPathogensMeninOnChange(CSModvars, defaultFlag):
    for age in GBRange(CS_0t1months, CS_24t60months):
        # Incidence
        # HiB
        CSModvars[CS_MeninIncReductionTag]['value'][
            defaultFlag][CS_HibVacc_MeningInterv][CS_AffecFract][age] = CSModvars[CS_MeninPathTag]['value'][
                CS_Distr_Sevr][CS_HibPathMeninVT][age]

        # PCV
        CSModvars[CS_MeninIncReductionTag]['value'][
            defaultFlag][CS_PneumVacc_MeningInterv][CS_AffecFract][age] = CSModvars[CS_MeninPathTag]['value'][
                CS_Distr_Sevr][CS_PneumocPathMeninVT][age]
        
        # Meningococcal - A
        CSModvars[CS_MeninIncReductionTag]['value'][
            defaultFlag][CS_MeningA_MeningInterv][CS_AffecFract][age] = CSModvars[CS_MeninPathTag]['value'][
                CS_Distr_Sevr][CS_MeningitAPathMeninVT][age]

        # Detailed vaccine efficacy
        for dose in GBRange(1, CS_MaxVaccDose):
            # HiB
            CSModvars[CS_DetVaccEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_HiB_Det][CS_Meningitis][dose][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_HibPathMeninVT][age]
            
            # PCV
            CSModvars[CS_DetVaccEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_PCV_Det][CS_Meningitis][dose][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_PneumocPathMeninVT][age]
            
            # Meningococcal A
            CSModvars[CS_DetVaccEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_MenA_Det][CS_Meningitis][dose][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_MeningitAPathMeninVT][age]

        # Standard vaccine efficacy
        # HiB
        CS_IV_HibVacc = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_HibVacc_MST_ID)
        if not CS_IV_HibVacc == None:              
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_HibVacc][CS_Meningitis][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_HibPathMeninVT][age]
        
        # PCV
        CS_IV_PneuVacc = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_PneuVacc_MST_ID)
        if not CS_IV_PneuVacc == None:              
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_PneuVacc][CS_Meningitis][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_PneumocPathMeninVT][age]
        
        # Meningococcal A
        CS_IV_MeningA = getCurrID(CSModvars[CS_IVInfoTag], CS_IV_MeningA_MST_ID)
        if not CS_IV_MeningA == None:              
            CSModvars[CS_ChildEffTag]['value'][
                defaultFlag][CS_AffecFract][CS_IV_MeningA][CS_Meningitis][age] = CSModvars[CS_MeninPathTag]['value'][
                    CS_Distr_Fatal][CS_MeningitAPathMeninVT][age]

def CS_Linkage_EffIntervForBOOnChange(CSModvars):
    #  IPTp malaria and ITN will be calculated later. Zero out data for now.
    #  Affected fraction for IPT and ITN is %births that are first or second order
    #  times the % of women exposed to Falciparum (See CSProj for details).
    value = 0
    for term in GBRange(CS_PreTermSGA, CS_TermAGA):
        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_IPTp][term] = value
        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_IPTp][term] = value

        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_AspirinLowDose][term] = value
        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_AspirinLowDose][term] = value

        CSModvars[CS_IntervForBOTag]['value'][CS_Data][CS_AffecFract][CS_Progesterone][term] = value
        CSModvars[CS_IntervForBOTag]['value'][CS_Default][CS_AffecFract][CS_Progesterone][term] = value

def CS_Linkage_AFforKMConBreastfeedingOnChange(CSModvars, defaultFlag):
    # Set the Affected Fraction for KMC on Breastfeeding to 0.58 * percent of births that
    # are premature, which is a country-specific value.
    value = 0.58 * (CSModvars[CS_StatusAtBirthTag]['value'][CS_PreTermSGA] + CSModvars[CS_StatusAtBirthTag]['value'][CS_PreTermAGA])
    
    for a in GBRange(CS_0t1months, CS_24t60months):
        CSModvars[CS_KMConBFTag]['value'][defaultFlag][CS_AffecFract][a] = value

def ModifyEffectivenessFromHealthStatus(CSModvars):    
    CS_Linkage_HSNutriDefiOnChange(CSModvars)
    CS_Linkage_HSMatNutriDefiOnChange(CSModvars)
    CS_Linkage_HSFalciparumIPTpSheetOnChange(CSModvars)
    CS_Linkage_HSPerCapitaIncOnChange(CSModvars)
    CS_Linkage_HSPercWom15t49LowBMIOnChange(CSModvars)
    CS_Linkage_HSSyphilisAmongWRAOnChange(CSModvars)
    CS_Linkage_HSPathogensDiarrOnChange(CSModvars, CS_Default)
    CS_Linkage_HSPathogensPneumOnChange(CSModvars, CS_Default)
    CS_Linkage_HSPathogensMeninOnChange(CSModvars, CS_Default)
    CS_Linkage_HSPathogensDiarrOnChange(CSModvars, CS_Data)
    CS_Linkage_HSPathogensPneumOnChange(CSModvars, CS_Data)
    CS_Linkage_HSPathogensMeninOnChange(CSModvars, CS_Data)
    CS_Linkage_EffIntervForBOOnChange(CSModvars)
    CS_Linkage_AFforKMConBreastfeedingOnChange(CSModvars, CS_Default)
    CS_Linkage_AFforKMConBreastfeedingOnChange(CSModvars, CS_Data)

# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# /////                                                                                                          /////
# /////                                                                                                          /////
# /////                                                                                                          /////
# ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

def setBaseYrValueAtYr(arrPtr, t):
    arrPtr[t] = arrPtr[0]

def FlatlineCoverage(CSModvars, final_year_idx):
    for t in GBRange(0, final_year_idx):
        for i in GBRange(0, len(CSModvars[CS_CoverageTag]['value']) - 1):
            setBaseYrValueAtYr(CSModvars[CS_CoverageTag]['value'][i], t)
                
        for bf in GBRange(0, CS_NotBF):
            for a in GBRange(0, CS_24t60months):
                setBaseYrValueAtYr(CSModvars[CS_BFPrevTag]['value'][bf][a], t)
        
        for a in GBRange(0, CS_24t60months):
            setBaseYrValueAtYr(CSModvars[CS_EarlyInitBFPrevTag]['value'][a], t)
        
        for vt in GBRange(0, CS_LastVacc_Det):
            for dose in GBRange(0, CS_MaxVaccDose):
                setBaseYrValueAtYr(CSModvars[CS_RoutDetVaccCovTag]['value'][vt][dose], t)
        
        setBaseYrValueAtYr(CSModvars[CS_PentaDetVaccCovTag]['value'], t)
        
        for sd in GBRange(0, CS_GT3STD):
            for a in GBRange(0, CS_24t60months): 
                setBaseYrValueAtYr(CSModvars[CS_StuntDistrTag]['value'][sd][a], t)
                setBaseYrValueAtYr(CSModvars[CS_WasteDistrTag]['value'][sd][a], t)
    
    for t in GBRange(CS_DetVacc1YrsRetro, CS_DetVaccRetroOffset):
        for vt in GBRange(0, CS_LastVacc_Det):
            for dose in GBRange(0, CS_MaxVaccDose):
                CSModvars[CS_RoutDetVaccCovTag]['retro'][vt][dose][t] = CSModvars[CS_RoutDetVaccCovTag]['retro'][vt][dose][CS_DetVacc1YrsRetro]
        CSModvars[CS_PentaDetVaccCovTag]['retro'][t] = CSModvars[CS_PentaDetVaccCovTag]['retro'][CS_DetVacc1YrsRetro]

def GetStandardCoverage(CSModvars, mstID, t):
    mstID = int(mstID)
    IVInfo = CSModvars[CS_IVInfoTag]

    result = 0

    if str(mstID) in IVInfo:
        if 'currID' in IVInfo[str(mstID)]:
            currID = IVInfo[str(mstID)]['currID']
            vt = CSGetVaccCurrID(mstID)
            if not vt == 0:
                dose = CSGetDetVaccEquivDose(vt)
                result = CSModvars[CS_RoutDetVaccCovTag]['value'][vt][dose][t]
            elif mstID == IV_MstPentaVacc:
                result = CSModvars[CS_PentaDetVaccCovTag]['value'][t]
            else:
                result = CSModvars[CS_CoverageTag]['value'][currID][t]
    
    return result

def SetStandardCoverage(CSModvars, mstID, t, value):
    mstID = int(mstID)
    IVInfo = CSModvars[CS_IVInfoTag]

    if str(mstID) in IVInfo:
        if 'currID' in IVInfo[str(mstID)]:
            currID = IVInfo[str(mstID)]['currID']
            vt = CSGetVaccCurrID(mstID)
            if not vt == 0:
                dose = CSGetDetVaccEquivDose(vt)
                CSModvars[CS_RoutDetVaccCovTag]['value'][vt][dose][t] = value
            elif mstID == IV_MstPentaVacc:
                CSModvars[CS_PentaDetVaccCovTag]['value'][t] = value
            else:
                CSModvars[CS_CoverageTag]['value'][currID][t] = value

def SetNonStandardCoverage(CSModvars, mstID, t, value):
    mstID = int(mstID)
    
    var1, var2 = getVars(mstID)

    if   mstID in BF_Set    : CSModvars[CS_BFPrevTag]['value'][var1][var2][t] = value
    elif mstID in Stunt_Set : CSModvars[CS_StuntDistrTag]['value'][var1][var2][t] = value
    elif mstID in Waste_Set : CSModvars[CS_WasteDistrTag]['value'][var1][var2][t] = value
    
    elif mstID == CS_MstEarlyInitBF : 
        for a in GBRange(CS_AgeSummary, CS_24t60months):     
            CSModvars[CS_EarlyInitBFPrevTag]['value'][a][t] = value

def UpdateCoverageFromQuality(CSModvars, final_year_idx):
    IVInfo = CSModvars[CS_IVInfoTag]

    for key, record in IVInfo.items():
        if 'currID' in IVInfo[key]:
            currID = IVInfo[key]['currID']
            qualityMode = IVInfo[key]['QualityMode']
            
            utilMstID = -1
            if not qualityMode == CS_QualityInactive:
                if qualityMode == CS_ANC1Mode: utilMstID = CS_IV_ANC1_MST_ID
                if qualityMode == CS_ANC4Mode: utilMstID = CS_IV_ANC4_MST_ID
                if qualityMode == CS_FascDelivMode: utilMstID = CS_IV_HealthFacDelivery_MST_ID

                if utilMstID in IVInfo:
                    
                    utilCurrID = IVInfo[utilMstID]['currID']
                    
                    for t in GBRange(0, final_year_idx):
                        Utilization = CSModvars[CS_CoverageTag]['value'][utilCurrID][t]
                        Quality = CSModvars[CS_CoverageTag]['quality'][currID][t]
                        CSModvars[CS_CoverageTag]['value'][currID][t] = (Quality / 100) * Utilization
                    
                    if int(utilMstID) in CSModvars[CS_SubnatDefDataMstIDsetTag]['value']:
                        CSModvars[CS_SubnatDefDataMstIDsetTag]['value'].append(int(key))
                        if utilMstID in CSModvars[CS_SubnatDataSourceArrayTag]['value']:
                            CSModvars[CS_SubnatDataSourceArrayTag]['value'][key] = CSModvars[CS_SubnatDataSourceArrayTag]['value'][utilMstID]

def SetInterventionsActive(CSModvars, interventionsToExclude):
    '''
        Sets all interventions active except any in interventionsToExclude

            Parameters:
                CSModvars (dict): MV tag, MV value pairs of all MVs required to execute this function.  The following MVs are required:

                    CS_IVInfoTag
                    IC_CS_SelectedIVSetTag
                    CS_EnterBFImproveDataTag

                interventionsToExclude (list) : list of all interventions that should NOT be made active.
              
            Returns:
                Nothing.
    '''

    for key, record in CSModvars[CS_IVInfoTag].items():
        if key not in [CS_IV_ContraceptiveUse_MST_ID, 
                       CS_IV_ART_MST_ID, 
                       str(CS_MstBFPromotion)]:
            if "Coverage_ED" in record:
                if record["Coverage_ED"]: 
                    if (int(key) not in interventionsToExclude) and (int(key) not in CSModvars[CS_SelectedIVSetTag]):             
                        CSModvars[CS_SelectedIVSetTag].append(int(key))
    
    for mstID in GBRange(CS_FirstFPMstID, CS_LastFPMstID):
        if (mstID not in interventionsToExclude) and (mstID not in CSModvars[CS_SelectedIVSetTag]):
            CSModvars[CS_SelectedIVSetTag].append(mstID)
    
    if (CS_FirstAMMstID not in interventionsToExclude) and (CS_FirstAMMstID not in CSModvars[CS_SelectedIVSetTag]):
        CSModvars[CS_SelectedIVSetTag].append(CS_FirstAMMstID)  

    if (CS_MstPercOnART not in interventionsToExclude) and (CS_MstPercOnART not in CSModvars[CS_SelectedIVSetTag]):
        CSModvars[CS_SelectedIVSetTag].append(CS_MstPercOnART)

    # Breastfeeding promotion needs to be active for IHT on projection creation.
    if (CS_MstBFPromotion not in interventionsToExclude) and (CS_MstBFPromotion not in CSModvars[CS_SelectedIVSetTag]):
        CSModvars[CS_SelectedIVSetTag].append(CS_MstBFPromotion)
    # CSModvars[CS_EnterBFImproveDataTag] = CS_MstBFPromotionMode


    


