######################## FUNCIONES BÁSICAS DE CÁLCULO DE LOS DIV ########################
# Cálculos de los DIV
# Método americano:

calculaDIV_ModeloAmericano_HR <- function(temperaturas, HR, matrizDIV, umbralHR = 85, dataSamplingTime = 30)
{
	# temperaturas: un vector numérico con los valores de temperaturas (en ºC), pueden estar medidos en cualquier formato temporal
	# HR: vector numérico con los valores de humedar relativa (en tanto por ciento 0 a 100), en el mismo formato temporal que "temperaturas"
	# matrizDIV: matriz de DIV según modelo americano
	# dataSamplingTime = valor numérico que indica la granularidad de los datos, en minutos, si la estacion mide en horarios= 60, semihorarios = 30, quinceminutales = 15
	# umbralHR: valor numérico, indica el valor de HR por encima del cual se considera que hay agua libre en la atmósfera

	# Totalizar horas humectacion y redondearlas a un entero
	horasHumec <- round(sum(ifelse(HR > umbralHR, dataSamplingTime/60, 0)), digits = 0)
	# Calcular media temperaturas en horas en las que hay humectación
	tempMediaHumec <- round(mean(temperaturas[which(HR >  umbralHR)]), digits = 1)

	# Establecer qué valor de la matrizDIV corresponde a esa humectacion y a esa temperatura
	escalaFarenheit <- c(seq(from = 60, to = 94, by = 1), 95)
	escalaCelsius <- (escalaFarenheit-32) * 5/9

	if(tempMediaHumec < 15.2 || tempMediaHumec > 36 || horasHumec == 0)
	{
		DIV = 0
	}else
	{
		columnaTemperatura <- which.min(abs(escalaCelsius - tempMediaHumec))
		filaHumectacion <- (24 - horasHumec + 1)

		DIV = matrizDIV[filaHumectacion, columnaTemperatura]
	}

	return(list(DIV_HR = DIV,
				temperatura_HR = tempMediaHumec,
				horasHumectacion_HR = horasHumec))
}

calculaDIV_ModeloAmericano_HumectUmbral <- function(temperaturas, humectaciones, matrizDIV, valorHumectacionMinimo, dataSamplingTime = NULL)
{
	# temperaturas: un vector numérico con los valores de temperaturas (en ºC), pueden estar medidos en cualquier formato temporal
	# humectaciones: vector numérico con los valores de humectación (en minutos o porcentaje, ver "unidadesHumectacion"), en el mismo formato temporal que "temperaturas"
	# matrizDIV: matriz de DIV según modelo americano
	# valorHumectacionMinimo: Puede ser un tiempo, un porcentaje, etc, que indique qué valor ha de superarse para considerarse que el período de tiempo es húmedo
	# dataSamplingTime: no es necesario, pero si no se da se considera que el tiempo de medida es horario. Si se usa es de forma análoga a la función calculaDIV_ModeloAmericano_HR()

	# Totalizar horas humectacion y redondearlas a un entero
	if(is.null(dataSamplingTime))
	{
		horasHumec <- round(sum(humectaciones[humectaciones > valorHumectacionMinimo]/60), digits = 0)
	}else
	{
		horasHumec <- round(sum(ifelse(humectaciones > valorHumectacionMinimo, dataSamplingTime/60, 0)), digits = 0)
	}
	# Calcular media temperaturas en horas humectación
	tempMediaHumec <- round(mean(temperaturas[which(humectaciones > valorHumectacionMinimo)]), digits = 1)

	# Establecer qué valor de la matrizDIV corresponde a esa humectacion y a esa temperatura

	escalaFarenheit <- c(seq(from = 60, to = 94, by = 1), 95)
	escalaCelsius <- (escalaFarenheit-32) * 5/9

	if(tempMediaHumec < 15.28 || tempMediaHumec > 35.28 || horasHumec == 0)
	{ # por encima de estos valores o con cero humectacion no hay riesgo
		DIV = 0
	}else
	{
		columnaTemperatura <- which.min(abs(escalaCelsius - tempMediaHumec))
		filaHumectacion <- (24 - horasHumec + 1)

		DIV = matrizDIV[filaHumectacion, columnaTemperatura]
	}

	return(list(DIV_HumectUmbral = DIV,
				temperatura_HumectUmbral = tempMediaHumec,
				horasHumectacion_HumectUmbral = horasHumec))
}

# Método Horario, modelo Rioja:
CalculaDIV_Horario_ModeloRioja <- function(temperaturas, humectaciones, vectorDIVHorario, valorUmbralMinimo, dataSamplingTime, na.rm = TRUE)
{
	# temperaturas: un vector numérico con los valores de temperaturas (en ºC), su período de tiempo viene dado por dataSamplingTime ejemplo: dataSamplingTime = 60 ==> 1 hora => datos horarios
	# humectaciones: un vector numérico con los valores de humectación (en minutos) ó humedad relativa (en tanto por ciento 0-100), idem temperaturas para dataSamplingTime
	# valorUmbralMinimo: Puede ser un tiempo p.ej: valorUmbralMinimo = 5; o un porcentaje de HR, p. ej. valorUmbralMinimo = 85, que indique qué valor ha de superarse para considerarse que el período de tiempo es húmedo
	# dataSamplingTime: valor numérico que indica la granularidad de los datos, en minutos, la tabla de DIV usada es horaria, si el dataSampling time es diferente se hace una regla de tres
	# vectorDIVHorario: vector de DIV según modelo Rioja

	df.TempHumect <- data.frame(temperaturas = temperaturas, humectaciones = humectaciones, check.rows = FALSE)

	# Obtener qué horas han tenido humectación por encima del umbral
	df.TempHumect$hayHumectacion <- df.TempHumect$humectaciones > valorUmbralMinimo

	# Establecer qué valor del vector de DIV corresponde a esa humectacion y a esa temperatura

	# Generamos escala de temperaturas de acuerdo a intervalos a artículo Sheng and Teng (1989)
	escalaFarenheit <- c(seq(from = 60, to = 94, by = 1), 95)
	escalaCelsius <- (escalaFarenheit-32) * 5/9

	df.TempHumect$DIVHorario <- 0
	for(fila in 1:nrow(df.TempHumect))
	{
		if(df.TempHumect$temperaturas[fila] > escalaCelsius[1] &&
			df.TempHumect$temperaturas[fila] < (last(escalaCelsius) + 0.5) &&
			df.TempHumect$hayHumectacion[fila])
		{
			df.TempHumect$DIVHorario[fila] <- vectorDIVHorario$DIVHorario[which.min(abs(escalaCelsius - df.TempHumect$temperaturas[fila]))] * dataSamplingTime/60 #dataSamplingTime/60 corrección a horas
		}
	}

	if(any(is.na(temperaturas)) | any(is.na(humectaciones)))
	{
		if(!na.rm) stop("Algún NA en vector temperaturas o humectaciones, na.rm = TRUE")
		warning(paste("Algún NA en vector temperaturas o humectaciones, DIV puede ser no valido en posiciones",
				which(is.na(temperaturas)), "vector temperaturas, o", which(is.na(humectaciones)), "vector humectaciones"))
	}

	return(list(DIV_Horario_Acumulado = sum(df.TempHumect$DIVHorario, na.rm = TRUE),
				DIV_Horario = df.TempHumect$DIVHorario,
				 df.TempHumect = df.TempHumect))
}
# # TEST
## Distintas temperaturas en los límites del vectorDIVHorario
# CalculaDIV_Horario_ModeloRioja(temperaturas = c(15, 16.5, 16, 36, 16, 0), humectaciones = c(30, 30, 0, 30, 30, 30),
# 						vectorDIVHorario, valorUmbralMinimo = 5, dataSamplingTime = 60)
## Error, vectores temperaturas, humectaciones no igual longitud; temperaturas más largo
# CalculaDIV_Horario_ModeloRioja(temperaturas = c(15, 16.5, 16, 36, 16, 0, 26), humectaciones = c(30, 30, 0, 30, 30, 30),
# 						vectorDIVHorario, valorUmbralMinimo = 5, dataSamplingTime = 60)
## Actuación ante vectores con NA.Error, vectores temperaturas, humectaciones incluyen algún NA
## Temperaturas
# CalculaDIV_Horario_ModeloRioja(temperaturas = c(15, 16.5, NA, 36, 16, 0, 26), humectaciones = c(30, 30, 0, 0, 30, 30, 30),
# 						vectorDIVHorario, valorUmbralMinimo = 5, dataSamplingTime = 60)
## Humectaciones
# CalculaDIV_Horario_ModeloRioja(temperaturas = c(15, 16.5, 16, 36, 16, 0, 26), humectaciones = c(30, 30, 0, NA, 30, 30, 30),
# 						vectorDIVHorario, valorUmbralMinimo = 5, dataSamplingTime = 60)

######################## FUNCIONES AUXILIARES PARA GENERAR OBJETOS CON LOS RESULTADOS CALCULADOS USANDO DISTINTOS MÉTODOS Y MÉTRICAS ########################
calculaDIVAcum <- function(DIV){ # Se usa en ambos modelos (Americano y Rioja), para sumar el DIV diario de ayer y hoy
	res <- rep(NA, length(DIV)) # <- c()
	res[1] = DIV [1]

	for(posicion in 2:length(DIV))
	{
		res[posicion] = DIV [posicion] + DIV [posicion - 1]
	}
	return(res)
}

# Función para obtener un objeto (data.frame) con todos los resultados según el modelo americano
calculaRiesgoDIV_MODELO_AMERICANO <- function(datosClimaticos, diasCalculo, estacion, matrizDIV, horaInicio, offsetHR, dataSamplingTime, valorHumectacionMinimo)
{
	# horaInicio = "10", hora solar a la que se inician los cálculos
	# offsetHR = 0 por defecto se calcula el modelo con umbrales de HR de 80 a 92.5%. offsetHR permite modifificar los umbrales, por ejemplo offset = - 5 haría los cálculosa 75, 80 y 85% de umbral

	# Generar una data.frame igual a la que vamos a construir después en el for (Inicializar variable)
	DIV <- data.frame(fecha = as.POSIXct(character()),
					estacion = numeric(),
					DIV_HumectUmbral = numeric(), T_HumectUmbral = numeric(), HorasHumectacion_HumectUmbral = numeric(),
					DIV_HR80 = numeric(), T_HR80 = numeric(), HorasHumectacion_HR80 = numeric(),
					DIV_HR82.5 = numeric(), T_HR82.5 = numeric(), HorasHumectacion_HR82.5 = numeric(),
					DIV_HR85 = numeric(), T_HR85 = numeric(), HorasHumectacion_HR85 = numeric(),
					DIV_HR87.5 = numeric(), T_HR87.5 = numeric(), HorasHumectacion_HR87.5 = numeric(),
					DIV_HR90 = numeric(), T_HR90 = numeric(), HorasHumectacion_HR90 = numeric(),
					DIV_HR92.5 = numeric(), T_HR92.5 = numeric(), HorasHumectacion_HR92.5 = numeric()
					)

	DIV.names <- names(DIV)

	# Calcular el DIV para cada día de los solicitados
	for (dia in diasCalculo)
	{
		dia <- as.POSIXct(dia, origin = "1970-01-01")
		datos <- datosClimaticos[datosClimaticos$fecha > dia & datosClimaticos$fecha <= dia + 3600*24,] # extraer datos para el día de cálculo

		if(nrow(datos) < (1440/dataSamplingTime - 288/dataSamplingTime)) next # dia Incompleto, se acepta la pérdida del 20% de datos

		res <- data.frame(fecha = dia + 3600*24, estacion = estacion,
					calculaDIV_ModeloAmericano_HumectUmbral(datos$temperatura, datos$humectacion, matrizDIV, valorHumectacionMinimo, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 80 - offsetHR, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 82.5 - offsetHR, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 85 - offsetHR, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 87.5 - offsetHR, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 90 - offsetHR, dataSamplingTime),
					calculaDIV_ModeloAmericano_HR(datos$temperatura, datos$HR, matrizDIV, umbralHR = 92.5 - offsetHR, dataSamplingTime)
					) # calcular DIV

		DIV <- rbind(DIV, res) # unir
	}

	names(DIV) <- DIV.names

	# Insertar el DIV Acumulado
	DIV <- data.frame(fecha = DIV$fecha,
						estacion = DIV$estacion,
						T_HumectUmbral = DIV$T_HumectUmbral, HorasHumectacion_HumectUmbral = DIV$HorasHumectacion_HumectUmbral,
						DIV_HumectUmbral =  DIV$DIV_HumectUmbral,  DIV_HumectUmbralAcum = calculaDIVAcum(DIV$DIV_HumectUmbral),
						T_HR80 = DIV$T_HR80, HorasHumectacion_HR80 = DIV$HorasHumectacion_HR80,
						DIV_HR80 = DIV$DIV_HR80, DIV_HR80Acum = calculaDIVAcum(DIV$DIV_HR80),
						T_HR82.5 = DIV$T_HR82.5, HorasHumectacion_HR82.5 = DIV$HorasHumectacion_HR82.5,
						DIV_HR82.5 = DIV$DIV_HR82.5, DIV_HR82.5Acum = calculaDIVAcum(DIV$DIV_HR82.5),
						T_HR85 = DIV$T_HR85, HorasHumectacion_HR85 = DIV$HorasHumectacion_HR85,
						DIV_HR85 = DIV$DIV_HR85, DIV_HR85Acum = calculaDIVAcum(DIV$DIV_HR85),
						T_HR87.5 = DIV$T_HR87.5, HorasHumectacion_HR87.5 = DIV$HorasHumectacion_HR87.5,
						DIV_HR87.5 = DIV$DIV_HR87.5, DIV_HR87.5Acum = calculaDIVAcum(DIV$DIV_HR87.5),
						T_HR90 = DIV$T_HR90, HorasHumectacion_HR90 = DIV$HorasHumectacion_HR90,
						DIV_HR90 = DIV$DIV_HR90, DIV_HR90Acum = calculaDIVAcum(DIV$DIV_HR90),
						T_HR92.5 = DIV$T_HR92.5, HorasHumectacion_HR92.5 = DIV$HorasHumectacion_HR92.5,
						DIV_HR92.5 = DIV$DIV_HR92.5, DIV_HR92.5Acum = calculaDIVAcum(DIV$DIV_HR92.5)
						)

	return(DIV)
}

# Función para obtener un objeto (dos data.frames) con los resultados según el modelo Horario, un objeto permite comparar el resultado con el modelo americano
# el otro es el valor del DIV calculado para cada hora
calculaRiesgoDIV_MODELO_RIOJA  <- function(datosClimaticos, diasCalculo, estacion, vectorDIVHorario, offsetHR, dataSamplingTime, valorHumectacionMinimo)
{
	# GENERA DOS OBJETOS:
	# DIV, que es una data.frame con valores DIARIOS, es un remedo del DIV Americano, aunque no tiene mucho sentido, es sólo por hacer equiparables los conceptos, se calcula a horaInicio (que viene prefijado en diasCalculo)
	# DIV_Horario, que es una data.frame con valores HORARIOS, y tiene más sentido que lo anterior y permite integrar los cálculos del DIV para cada hora posteriormente

	# Generar data.frames iguales a las que vamos a construir después en el for
	DIV <- data.frame(fecha = as.POSIXct(character()),
					estacion = numeric(),
					DIV_HumectUmbral = numeric(),
					DIV_HR80 = numeric(),
					DIV_HR82.5 = numeric(),
					DIV_HR85 = numeric(),
					DIV_HR87.5 = numeric(),
					DIV_HR90 = numeric(),
					DIV_HR92.5 = numeric()
					)

	DIV.names <- names(DIV)

	DIV_Horario <- data.frame(DIV_Horario_HumectUmbral = numeric(),
								DIV_Horario_HR80 = numeric(),
								DIV_Horario_HR82.5 = numeric(),
								DIV_Horario_HR85 = numeric(),
								DIV_Horario_HR87.5 = numeric(),
								DIV_Horario_HR90 = numeric(),
								DIV_Horario_HR92.5 = numeric()
								)

	DIV_Horario.names <- names(DIV_Horario)

	for(dia in diasCalculo)  #[dias > first(datosClimaticos$fecha)]){
	{
		dia <- as.POSIXct(dia, origin = "1970-01-01")
		datos <- datosClimaticos[datosClimaticos$fecha > dia & datosClimaticos$fecha <= dia + 3600*24,] # extraer datos para el día de cálculo	(dia es un posixct con la hora de inicio del modelo incluida)

		if(nrow(datos) == 0) next

		DIV_HumectUmbral = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$humectacion, vectorDIVHorario, valorUmbralMinimo = valorHumectacionMinimo, dataSamplingTime)
		DIV_HR80 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 80 - offsetHR, dataSamplingTime)
		DIV_HR82.5 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 82.5 - offsetHR, dataSamplingTime)
		DIV_HR85 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 85 - offsetHR, dataSamplingTime)
		DIV_HR87.5 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 87.5 - offsetHR, dataSamplingTime)
		DIV_HR90 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 90 - offsetHR, dataSamplingTime)
		DIV_HR92.5 = CalculaDIV_Horario_ModeloRioja(datos$temperatura, datos$HR, vectorDIVHorario, valorUmbralMinimo = 92.5 - offsetHR, dataSamplingTime)

		res <- data.frame(fecha = dia + 3600*24, estacion = estacion,
					DIV_HumectUmbral$DIV_Horario_Acumulado,
					DIV_HR80$DIV_Horario_Acumulado,
					DIV_HR82.5$DIV_Horario_Acumulado,
					DIV_HR85$DIV_Horario_Acumulado,
					DIV_HR87.5$DIV_Horario_Acumulado,
					DIV_HR90$DIV_Horario_Acumulado,
					DIV_HR92.5$DIV_Horario_Acumulado
				)

		DIV <- rbind(DIV, res)

		res2 <- data.frame(DIV_HumectUmbral$DIV_Horario,
							DIV_HR80$DIV_Horario,
							DIV_HR82.5$DIV_Horario,
							DIV_HR85$DIV_Horario,
							DIV_HR87.5$DIV_Horario,
							DIV_HR90$DIV_Horario,
							DIV_HR92.5$DIV_Horario
						)

		DIV_Horario <- rbind(DIV_Horario, res2)
	}

	names(DIV) <- DIV.names
	names(DIV_Horario) <- DIV_Horario.names

	# Insertar el DIV Acumulado
	DIV <- data.frame(fecha = DIV$fecha,
						estacion = DIV$estacion,
						DIV_HumectUmbral =  DIV$DIV_HumectUmbral,  DIV_HumectUmbralAcum = calculaDIVAcum(DIV$DIV_HumectUmbral),
						DIV_HR80 = DIV$DIV_HR80, DIV_HR80Acum = calculaDIVAcum(DIV$DIV_HR80),
						DIV_HR82.5 = DIV$DIV_HR82.5, DIV_HR82.5Acum = calculaDIVAcum(DIV$DIV_HR82.5),
						DIV_HR85 = DIV$DIV_HR85, DIV_HR85Acum = calculaDIVAcum(DIV$DIV_HR85),
						DIV_HR87.5 = DIV$DIV_HR87.5, DIV_HR87.5Acum = calculaDIVAcum(DIV$DIV_HR87.5),
						DIV_HR90 = DIV$DIV_HR90, DIV_HR90Acum = calculaDIVAcum(DIV$DIV_HR90),
						DIV_HR92.5 = DIV$DIV_HR92.5, DIV_HR92.5Acum = calculaDIVAcum(DIV$DIV_HR92.5)
						)

	return(list(DIV = DIV, DIV_Horario = DIV_Horario))
}

# Función que acumula los valores de DIV para un número de registros = numDatos
# IMPORTANTE: Los valores de temperaturas, humectación y umbral humectación NO SE USAN
# se pasan sólo por si acaso en el futuro se implementan reseteos en el modelo de acumulación
calculaDIVAcumHorario <- function(DIV, temperatura, humectacion, umbralHumectacion,  offsetHR = 0, numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime)
{
	DIVAcumHorario <- rep(NA, length(DIV))
	numHorasDIVesNulo <- rep(NA, length(DIV))

	numDatos = numDatos * 60/dataSamplingTime # Corrección para considerar el número de registros en cálculos no horarios

	for(posicion in 1:length(DIV))
	{
		if(DIV[posicion] == 0){# Para cada registro si el DIV es cero se acumulan las horas donde DIV == 0
			numHorasDIVesNulo[posicion] <- dataSamplingTime/60 + if(posicion > 1) numHorasDIVesNulo[posicion - 1] else 0
		}else{ # cuando es DIV != 0 se resetea la cuenta
			numHorasDIVesNulo[posicion] = 0
		}

		if(posicion >= numDatos){# Superado el número de datos mínimo se hace el cálculo del DIV
			origen = posicion - numDatos
			extracto <- data.frame(temperatura = temperatura,
									humectacion = humectacion,
									numHorasDIVesNulo = numHorasDIVesNulo,
									DIV = DIV)

			extracto <- extracto[origen:posicion, ]

			if(!is.na(numHorasReseteo)){ # este parámetro puede no estar definido
				# Comprobación por temperaturas extremas #!!!
					#if(any(extracto$temperatura) > umbralMaximo) etc
				# Comprobación por humectaciones bajas extremas #!!!
				# Comprobación por número de horas DIV > 0
				if(any(extracto$numHorasDIVesNulo == numHorasReseteo)){ # Si no hay reseteo la fórmula es como la de antes
					posicionReseteo <- last(which(extracto$numHorasDIVesNulo == nummHorasReseteo)) # Last() por si hay más de un reseteo
					extracto <- extracto[posicionReseteo:nrow(extracto),] # Detrás del reseteo se pueden acumular más horas secas pero solo se acumula DIV de ahí en adelante
				}
			}
			DIVAcumHorario[posicion] <- sum(extracto$DIV, na.rm = TRUE)
		}
	}

	return(DIVAcumHorario)
}

# Función que devuelve un vector de valores lógicos TRUE/FALSE que indican si la lluvia acumulada se considera efectiva o no.
esLluviaEfectiva <- function(PAcum)
{
	lluviaEfectiva <- logical() # Inicializar variable

	lluviaEfectivaHoraria = 1 * 60 / dataSamplingTime  # Si llueve más de un litro cada hora = 2 litros a la hora, la lluvia se considera efectiva
	lluviaEfectivaPorAcumulacion = 0.2 * 60 / dataSamplingTime # Si llueve en algo en una hora y también la siguiente, aunque sea poca cantidad, se considera efectiva
	lluviaEfectivaPorAcumulacion2HorasPrevias = 0.4  # Si llueve en esta hora y ha llovido más de 0.4 en las dos horas anteriores, se considera efectiva

	for(posicion in 1:length(PAcum))
	{
		lluviaEfectiva[posicion] = FALSE

		if(!is.na(PAcum[posicion]) & posicion < length(PAcum)) # si no es NA | En la última posición no se hace el cálculo
		{
			if(PAcum[posicion] >= lluviaEfectivaHoraria)
			{# Si llueve más de un litro cada media hora = 2 litros a la hora, la lluvia se considera efectiva
				lluviaEfectiva[posicion] = TRUE
				next
			}
			if(PAcum[posicion] > lluviaEfectivaPorAcumulacion & sum(PAcum[posicion + 1] > lluviaEfectivaPorAcumulacion))
			{# Si llueve más de un litro cada media hora = 2 litros a la hora, la lluvia se considera efectiva
				lluviaEfectiva[posicion] = TRUE
				next
			}
			if(posicion > 2) # No se puede ejecutar este análisis en las dos primeras posiciones del vector de lluvias
			{# Si llueve en esta hora y ha llovido más de 0.4 en las dos horas anteriores, se considera efectiva
				if( PAcum[posicion] > 0 & (PAcum[posicion - 1] +  PAcum[posicion - 2]) > 0.4)
				{
					lluviaEfectiva[posicion] = TRUE
					next
				}
			}
		}
	}

	return(lluviaEfectiva)
}

# Función que devuelve un vector de valores lógicos TRUE/FALSE que indican si la temperatura registrada se considera efectiva o no.
hayTemperaturaEfectiva <- function(temperatura, umbralTempMinima, umbralTempMaxima)
{
	hayTemperaturaEfectiva <- ifelse(temperatura >= umbralTempMinima & temperatura < umbralTempMaxima, TRUE, FALSE)
	return(hayTemperaturaEfectiva)
}

# Función que devuelve un vector de valores lógicos TRUE/FALSE que indican si la temperatura registrada está entre 10 y 13ºC
estaTemperaturaEntre10y13Grados <- function(temperatura)
{
	hayTemperaturaEfectiva <- ifelse(temperatura >= 10 & temperatura < 13 , TRUE, FALSE)
	return(hayTemperaturaEfectiva)
}

# Función para obtener un objeto (data.frame) con los resultados de las fechas que cumplen infección para ello se consideran los siguientes parámetros:
# umbralTempMinima ==> mínima temperatura necesaria para que se considere a la espora activa para infectar (13 ºC según bibliografía)
# umbralTempMaxima ==> máxima temperatura que la espora permite para mantener su poder infetivo (35 ºC según bibliografía)
# horasAcumuladasParaAparicionInfeccion  => horas necesarias para que la germinación sea satisfactoria y la infección se produzca
calculaMomentosInfeccion <-function(fecha, temperatura, humectacion, umbralHumectacion, umbralTempMinima = 13, umbralTempMaxima = 35, horasAcumuladasParaAparicionInfeccion = 8){
	res <- data.frame(fecha, temperatura, humectacion, stringsAsFactors = FALSE)

	res$hayHumectacion = ifelse(humectacion > umbralHumectacion, TRUE, FALSE)
	res$hayTemperaturaOptima = ifelse(temperatura >= umbralTempMinima & temperatura <= umbralTempMaxima, TRUE, FALSE)

	res$horasAcumuladas[1] = if(res$hayHumectacion[1] & res$hayTemperaturaOptima[1]) 1 else 0
	for(posicion in 2:nrow(res)){ # Si se dan condiciones de Humectación y temperatura en esta variable almaceno, para cada registro, el número de horas de condiciones apropiadas
		res$horasAcumuladas[posicion] = if(res$hayHumectacion[posicion] & res$hayTemperaturaOptima[posicion]) 1 + res$horasAcumuladas[posicion-1] else 0
	}

	fechasInfeccion = res$fecha[which(res$horasAcumuladas >= horasAcumuladasParaAparicionInfeccion)]

	calculaDiasHastaInfeccion <- function(tempMediaPosterior){ # esta funcion ofrece valores de 1 para T = 30 y 20 para T = 10, de forma  lineal
		if(tempMediaPosterior > 30) tempMediaPosterior = 30
		if(tempMediaPosterior < 10) tempMediaPosterior = NA
		tempMediaPosterior <- round(tempMediaPosterior, 0)
		diasHastaInfeccion = 30 - tempMediaPosterior + 1
		return(diasHastaInfeccion)
	}
	#calculaDiasHastaInfeccion(32); calculaDiasHastaInfeccion(30); calculaDiasHastaInfeccion(20); calculaDiasHastaInfeccion(10); calculaDiasHastaInfeccion(9)

	tempMediaPosterior = numeric()
	diasHastaInfeccion = integer()
	for(fecha in fechasInfeccion){
		temperaturasPosterioresInfeccion <- res$temperatura[res$fecha > fecha]

		if(length(temperaturasPosterioresInfeccion) > 20*24) temperaturasPosterioresInfeccion = temperaturasPosterioresInfeccion[1:20*24] # Eliminamos más allá de 20 días de datos
		tempMediaPosterior = c(tempMediaPosterior, mean(temperaturasPosterioresInfeccion))
		diasHastaInfeccion = if(length(temperaturasPosterioresInfeccion) > 0) calculaDiasHastaInfeccion(last(tempMediaPosterior)) else NA
	}

	return(data.frame(fechasInfeccion, tempMediaPosterior, diasHastaInfeccion))
}

######################## FUNCION PRINCIPAL REALIZA TODO EL CONTROL DEL PROCESO DE CÁLCULO DE VALORES DIV Y RIESGO ########################
###
### LLAMA A LAS DISTINTAS FUNCIONES AUXILIARES Y GENERA OBJETO DE RESPUESTA CON TODA LA INFORMACIÓN OBTENIDA
###
###########################################################################################################################################
calculaRiesgoDIV_V20 <- function(estacion, fechaInicio, fechaFin,
									matrizDIV, vectorDIVHorario,
									horaInicio = "10", offsetHR = 0,
									sensorHumect = "THumecta1",
									origenDato = "SWClima",
									nombreEstacion = NA,
									...)
{

# horaInicio = "10", hora solar a la que se inician los cálculos
# offsetHR = 0 por defecto se calcula el modelo con umbrales de HR de 85 y 90. offsetHR permite modifificar los umbrales, por ejemplo offset = - 5 haría los cálculosa 75, 80 y 85% de umbral
# sensorHumect = "THumecta1", solo aplicable a datos tipo "SWClima"; es previsible que se elimine, en la actualidad se extraen los dos sensores de humectacion del SIAR, el que se especifica queda marcado como el sensor 1 en el proceso, es un error lógico
# origenDato = "SWClima", "CESENS", etc

	# EXTRAER DATOS CLIMÁTICOS
	res <- extraeDatosClima(estacion, origenDato, fechaInicio, fechaFin)

	res <- fromJSON(res)

	attach(res)

	# Formatear variables
	datosClimaticos <- as.data.frame(datosClimaticos)
	datosClimaticos$fecha <- as.POSIXct(datosClimaticos$fecha, origin = "1970-01-01")

	if(!is.na(nombreEstacion)) nombreEstacionCalculada = nombreEstacion # se da prioridad a lo que traiga esta variable, CESENS no tiene una buena trazabilidad de los nombres ligados a los códigos de estaciones

	# Calcular el DIV para cada día entre fechaInicio y fechaFin
	# Obtener secuencia de los días a calcular
	# por convenio en modelos nunca empezamos el día en las 00:00 sino en una hora que agrícolamente tenga sentido, generalmente entre las 8 y las 10 UTC (Hora solar en España)
	diasCalculo <- seq.POSIXt(from = as.POSIXct(strftime(paste0(fechaInicio, " ", horaInicio, ":00"), tz = "UTC")),
						to = as.POSIXct(strftime(paste0(fechaFin, " ", horaInicio, ":00"), tz = "UTC")),
						by = "day")

	diasCalculo <- diasCalculo[-length(diasCalculo)] # 	quitamos el último día

	diasCalculo <- diasCalculo[diasCalculo > first(datosClimaticos$fecha)] # y también quitamos los días donde no haya datos procedentes de la estación

	DIV_AMERICANO <- calculaRiesgoDIV_MODELO_AMERICANO(datosClimaticos, diasCalculo, estacion, matrizDIV, horaInicio, offsetHR, dataSamplingTime, valorHumectacionMinimo)

	DIV_HORARIO <- calculaRiesgoDIV_MODELO_RIOJA(datosClimaticos, diasCalculo, estacion, vectorDIVHorario, offsetHR, dataSamplingTime, valorHumectacionMinimo)

	DIV <- DIV_HORARIO$DIV
	DIV_Horario <- DIV_HORARIO$DIV_Horario

	datosClimaticos$lluviaEfectiva <- esLluviaEfectiva(datosClimaticos$PAcum)
	datosClimaticos$temperaturaEfectiva <- hayTemperaturaEfectiva(datosClimaticos$temperatura, umbralTempMinima = 13, umbralTempMaxima = 35)
	datosClimaticos$temperaturaEntre10y13 <- estaTemperaturaEntre10y13Grados(datosClimaticos$temperatura)

	# Recortar datos climáticos
	datosClimaticos <- datosClimaticos[datosClimaticos$fecha > DIV$fecha[1]-24*3600,]
	datosClimaticos <- datosClimaticos[datosClimaticos$fecha <= last(DIV$fecha),]

	DIV_Horario <- data.frame(fecha = datosClimaticos$fecha,
							estacion = rep(estacion, length(datosClimaticos$fecha)),
							#####################
							DIV_Horario_HumectUmbral = DIV_Horario$DIV_Horario_HumectUmbral,
							DIV_HumectUmbral.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HumectUmbral, datosClimaticos$temperatura,
																			datosClimaticos$humectacion, umbralHumectacion = valorHumectacionMinimo,
																			offsetHR = offsetHR, numDatos = 2 * 24,
																			numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HumectUmbral.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HumectUmbral, datosClimaticos$temperatura,
																			datosClimaticos$humectacion, umbralHumectacion = valorHumectacionMinimo,
																			offsetHR = offsetHR, numDatos = 7 * 24,
																			numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HumectUmbral.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HumectUmbral, datosClimaticos$temperatura,
																			datosClimaticos$humectacion, umbralHumectacion = valorHumectacionMinimo,
																			offsetHR = offsetHR, numDatos = 14 * 24,
																			numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HumectUmbral.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HumectUmbral, datosClimaticos$temperatura,
																			datosClimaticos$humectacion, umbralHumectacion = valorHumectacionMinimo,
																			offsetHR = offsetHR, numDatos = 21 * 24,
																			numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HumectUmbral.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HumectUmbral, datosClimaticos$temperatura,
																			datosClimaticos$humectacion, umbralHumectacion = valorHumectacionMinimo,
																			offsetHR = offsetHR, numDatos = 28 * 24,
																			numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR80 = DIV_Horario$DIV_Horario_HR80,
							DIV_HR80.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR80, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 80, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR80.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR80, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 80, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR80.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR80, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 80, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR80.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR80, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 80, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR80.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR80, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 80, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR82.5 = DIV_Horario$DIV_Horario_HR82.5,
							DIV_HR82.5.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR82.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 82.5, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR82.5.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR82.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 82.5, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR82.5.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR82.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 82.5, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR82.5.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR82.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 82.5, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR82.5.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR82.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 82.5, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR85 = DIV_Horario$DIV_Horario_HR85,
							DIV_HR85.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR85, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 85, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR85.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR85, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 85, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR85.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR85, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 85, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR85.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR85, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 85, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR85.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR85, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 85, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR87. = DIV_Horario$DIV_Horario_HR87.5,
							DIV_HR87.5.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR87.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 87.5, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR87.5.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR87.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 87.5, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR87.5.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR87.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 87.5, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR87.5.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR87.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 87.5, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR87.5.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR87.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 87.5, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR90 = DIV_Horario$DIV_Horario_HR90,
							DIV_HR90.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR90, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 90, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR90.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR90, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 90, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR90.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR90, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 90, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR90.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR90, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 90, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR90.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR90, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 90, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							#####################
							DIV_Horario_HR92.5 = DIV_Horario$DIV_Horario_HR92.5,
							DIV_HR92.5.2dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR92.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 92.5, offsetHR = offsetHR,
																	numDatos = 2 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR92.5.7dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR92.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 92.5, offsetHR = offsetHR,
																	numDatos = 7 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR92.5.14dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR92.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 92.5, offsetHR = offsetHR,
																	numDatos = 14 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR92.5.21dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR92.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 92.5, offsetHR = offsetHR,
																	numDatos = 21 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime),
							DIV_HR92.5.28dias = calculaDIVAcumHorario(DIV_Horario$DIV_Horario_HR92.5, datosClimaticos$temperatura,
																	datosClimaticos$HR, umbralHumectacion = 92.5, offsetHR = offsetHR,
																	numDatos = 28 * 24, numHorasReseteo = NA, dataSamplingTime = dataSamplingTime)
							)

	fechasInfeccionHR95.8horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura,
								datosClimaticos$HR, umbralHumectacion = 95, horasAcumuladasParaAparicionInfeccion = 8)
	fechasInfeccionHR95.6horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura,
								datosClimaticos$HR, umbralHumectacion = 95, horasAcumuladasParaAparicionInfeccion = 6)
	fechasInfeccionHR90.8horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura,
								datosClimaticos$HR, umbralHumectacion = 90, horasAcumuladasParaAparicionInfeccion = 8)
	fechasInfeccionHR90.6horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura,
								datosClimaticos$HR, umbralHumectacion = 90, horasAcumuladasParaAparicionInfeccion = 6)

	fechasInfeccionHumectacion.8horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura , datosClimaticos$humectacion, umbralHumectacion = 50)
	fechasInfeccionHumectacion.6horas <- calculaMomentosInfeccion(datosClimaticos$fecha, datosClimaticos$temperatura , datosClimaticos$humectacion, umbralHumectacion = 50, horasAcumuladasParaAparicionInfeccion = 6)

	variablesConfiguracion <- data.frame(valorDIV = 1, nombre = 'Sin Riesgo',  color = "khaki", stringsAsFactors = FALSE)
	variablesConfiguracion <- rbind(variablesConfiguracion, list(valorDIV = 2, nombre = 'Inicio Riesgo',  color =  "greenyellow"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = 3, nombre = 'Riesgo Leve',  color = "yellowgreen"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = 4, nombre = 'Riesgo Medio',  color = "orange"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = 5, nombre = 'Riesgo Moderado',  color = "sienna1"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = 6, nombre = 'Riesgo Alto',  color = "tomato1"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = NA, nombre = 'Riesgo muy Alto',  color = "red1"))
	variablesConfiguracion <- rbind(variablesConfiguracion,	list(valorDIV = 7, nombre = 'Infección segura',  color = "red3"))

	row.names(variablesConfiguracion) = c('DIV.SinRiesgo', 'DIV1.InicioRiesgo', 'DIV.RiesgoLeve', 'DIV.RiesgoMedio',
										'DIV.RiesgoModerado', 'DIV.RiesgoAlto', 'DIV.RiesgoMuyAlto', 'DIV.InfeccionSegura')

	# Generar objeto respuesta
	objetoRespuesta = list(datosClimaticos = datosClimaticos[datosClimaticos$fecha > diasCalculo[1] & datosClimaticos$fecha <= last(diasCalculo) + 3600*24,],
				DIV = DIV,
				DIV_AMERICANO = DIV_AMERICANO,
				DIV_Horario = DIV_Horario,
				fechasInfeccion = list(HR95.8horas = fechasInfeccionHR95.8horas, HR95.6horas = fechasInfeccionHR95.6horas,
									HR90.8horas = 	fechasInfeccionHR90.8horas, HR90.6horas = 	fechasInfeccionHR90.6horas,
									humectacion.8horas = fechasInfeccionHumectacion.8horas, humectacion.6horas = fechasInfeccionHumectacion.6horas),
				variablesConfiguracion = variablesConfiguracion,
				fechaUltimoDato = tail(DIV_Horario$fecha, 1),
				origenDato = origenDato,
				estacion = estacion,
				nombreEstacion = nombreEstacionCalculada)

	detach(res)

	return(objetoRespuesta)
}

# Test
#
# estacion = 505
# fechaInicio = as.POSIXct("2018-06-01", tz = "UTC")
# fechaFin =  as.POSIXct("2018-10-01", tz = "UTC") #Sys.time()
 # matrizDIV <- read.csv("Matriz_DIV.csv", row.names = 1)
 # vectorDIVHorario <- read.csv("Vector_DIVHorario.csv")
# DIV505_2018 <- calculaRiesgoDIV_V20(estacion, fechaInicio, fechaFin, matrizDIV, vectorDIVHorario)
 # head(DIV505_2018[,c(1,19, 21)])
  # tail(DIV505_2018[,c(1,19, 21)])
  # summary(DIV505_2018[,c(1,19, 21)])


#' Calculate statistics for a BayesODM model
#'
#' This function calculates statistics for a BayesODM model.
#' @param mcmc MCMC object from \code{trainBayesODM}.
#' @param focusModel SpatialPolygonsDataFrame with BayesODM model output.
#' @param rhat Maximum value of rhat by which to conclude a model parameter is sufficiently converged.
#' @param ... Arguments to pass to \code{rhatStats}.
#' @return
#' @examples
#' @export
bayesODMStats <- function(mcmc, focusModel, rhat = 1.1, ...) {

	meta <- attr(focusModel, 'meta')

	# input stats
	detects <- focusModel@data[ , meta$detect]
	efforts <- focusModel@data[ , meta$effort]

	# output stats
	totalSamples <- (meta$niter - meta$nburnin) / meta$thin
	totalParams <- nrow(mcmc$summary$all.chains)
	proportConverged <- sum(mcmc$summary$all.chains[ , 'rhat'] < rhat, na.rm=TRUE) / totalParams

	stats <- data.frame(

		totalCounties = nrow(focusModel),
		occupiedCounties = sum(detects > 0, na.rm=TRUE),
		countiesWithNonZeroEffort = sum(efforts > 0, na.rm=TRUE),
		meanDetectsToEfforts = mean(detects / efforts, na.rm=TRUE),
		maxDetections = max(detects, na.rm=TRUE),
		maxEfforts = max(efforts, na.rm=TRUE),

		meanPsi = mean(focusModel@data$psi, na.rm=TRUE),
		meanPsi95CI = mean(focusModel@data$psi95CI, na.rm=TRUE),
		meanP = mean(focusModel@data$p, na.rm=TRUE),
		meanP95CI = mean(focusModel@data$p95CI, na.rm=TRUE),

		niter=meta$niter,
		nburnin=meta$nburnin,
		nchains=meta$nchains,
		thin=meta$thin,
		totalSamples=totalSamples,

		proportConverged = proportConverged,
		rhat = rhat,
		totalParams = totalParams

	)

	stats

}


#' mph2knots
#'
#' Conversion speed from mile per hour in knots.
#'
#' @param numeric  mph Speed in mile per hour.
#' @return
#'
#'
#' @author  Istituto di Biometeorologia Firenze Italy  Alfonso Crisci \email{a.crisci@@ibimet.cnr.it}
#' @keywords  mph2knots
#'
#' @export
#'
#'
#'
#'
mph2knots<-function(mph)
{
  return(mph * 0.868976);
}


MARSSkemcheck <- function(MLEobj) {
  # This checks that the model can be handled by the MARSSkem algorithm
  # Most of this is implementing the restrictions in Summary of Requirements for Degenerate Models in derivation
  MODELobj <- MLEobj$marss
  fixed <- MODELobj$fixed
  free <- MODELobj$free
  par.dims <- attr(MODELobj, "model.dims")
  m <- par.dims[["x"]][1]
  n <- par.dims[["y"]][1]
  TT <- par.dims[["data"]][2]
  pseudolim <- 1E-8
  ok <- TRUE
  msg <- NULL

  # If TT=2 then kf will break
  if (TT <= 2) {
    msg <- c(msg, "The number of time steps is <=2.\nMore than 2 data points are needed to estimate parameters.\n")
    ok <- FALSE
  }

  ############ Check that if fixed, B is within the unit circle
  for (t in 1:max(dim(free$B)[3], dim(fixed$B)[3])) {
    ifixed <- min(t, dim(fixed$B)[3])
    if (is.fixed(free$B[, , ifixed, drop = FALSE])) { # works on 3D matrices
      # parmat needs MODELobj and par list
      if (is.null(MLEobj$par$B)) tmpparB <- MLEobj$start$B else tmpparB <- MLEobj$par$B
      tmp.MLEobj <- list(marss = MODELobj, par = list(B = tmpparB)) # B is fixed but par might have cols from other times
      parB <- parmat(tmp.MLEobj, "B", t = t)$B
      if (!all(abs(Re(eigen(parB, only.values = TRUE)$values)) <= 1)) {
        msg <- c(msg, " All the eigenvalues of B must be within the unit circle: all(abs(Re(eigen(fixed$B)$values))<=1)\n")
        ok <- FALSE
      }
    }
  } # end for over time to check B

  ############ Check that if R has 0s, then the corresponding row of A and Z are fixed
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  el <- "R"
  # extracts the r=0 rows (or cols)
  diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
  Tmax <- 0
  for (par.test in c("R", "Z", "A")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }

  if (is.null(MLEobj[["par"]])) MLEobj$par <- MLEobj$start
  # Run with MARSSkfss because it has more internal checks and error messages
  MLEobj$kf <- MARSSkfss(MLEobj)
  if (!MLEobj$kf$ok) {
    return(list(ok = FALSE, msg = c(MLEobj$kf$errors, msg)))
  }
  MLEobj$Ey <- MARSShatyt(MLEobj)
  msg.tmp <- NULL
  for (i in 1:TT) {
    ifixed <- min(i, dim(fixed[[el]])[3])
    ifree <- min(i, dim(free[[el]])[3])
    zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , ifixed, drop = FALSE] == 0, 1, all) # fixed rows and 0
    if (any(zero.diags)) {
      II0 <- makediag(as.numeric(zero.diags))
      if (i <= Tmax) {
        for (par.test in c("Z", "A")) {
          II <- diag(1, par.dims[[par.test]][2])
          ifree.par <- min(i, dim(free[[par.test]])[3])
          dpart <- sub3D(free[[par.test]], t = ifree.par)
          par.not.fixed <- any(((II %x% II0) %*% dpart) != 0)

          if (par.not.fixed) {
            if (par.test == "Z") msg <- c(msg, paste("t=", i, ": For method=kem (EM), if an element of the diagonal of R is 0, the corresponding row of Z must be fixed. See MARSSinfo('AZR0').\n", sep = ""))
            if (par.test == "A") msg <- c(msg, paste("t=", i, ": For method=kem (EM), if an element of the diagonal of R is 0, the corresponding row of both A and D must be fixed. See MARSSinfo('AZR0').\n", sep = ""))
          }
        } # for par.test
      } # if par needs to be tested; don't need to do over all TT just up to Tmax

      Z <- parmat(MLEobj, "Z", t = i)$Z
      A <- parmat(MLEobj, "A", t = i)$A
      Z.R0 <- Z[zero.diags, , drop = FALSE]
      A.R0 <- A[zero.diags, , drop = FALSE]
      y.R0 <- MLEobj$Ey$ytT[zero.diags, i, drop = FALSE]
      y.resid <- y.R0 - Z.R0 %*% MLEobj$kf$xtT[, i, drop = FALSE] - A.R0
      if (any(y.resid > pseudolim)) {
        msg.tmp <- c(msg.tmp, paste(" Z, A, and y for the R=0 rows at time=", i, " do not agree. For the R=0 rows, E(y) must equal Z * E(x) + A.\n", sep = ""))
      }
    } # any zero.diags
  } # end for loop over time
  if (!is.null(msg)) {
    msg <- c(msg, msg.tmp[1:min(10, length(msg.tmp))]) # error msgs could be really long, only print first 10
    ok <- FALSE
  }

  ############ Check that II_q^{0} is time constant
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  Tmax <- 0
  for (par.test in c("Q")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }
  II0 <- list()
  for (i in 1:Tmax) {
    for (el in c("Q")) {
      ifixed <- min(i, dim(fixed[[el]])[3])
      ifree <- min(i, dim(free[[el]])[3])
      diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
      zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , ifixed, drop = FALSE] == 0, 1, all) # fixed rows
      II0Q <- makediag(as.numeric(zero.diags))
      II0Q <- unname(II0[[el]])
    }
    if (i == 1) {
      II0Q.1 <- II0Q
    } else {
      if (!isTRUE(all.equal(II0Q, II0Q.1))) {
        msg <- c(msg, paste("t=", i, ": The placement of 0 variances in Q must be time constant.\n", sep = ""))
        ok <- FALSE
      }
    }
  } # end for loop over time

  ############ Check that if R and Q both have 0s, then the U and Bs are appropriately fixed
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  Tmax <- 0
  for (par.test in c("R", "Q", "U", "B", "Z")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }
  II0 <- list()
  for (i in 1:Tmax) {
    for (el in c("R", "Q")) {
      ifixed <- min(i, dim(fixed[[el]])[3])
      ifree <- min(i, dim(free[[el]])[3])
      diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
      zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , ifixed, drop = FALSE] == 0, 1, all) # fixed rows
      II0[[el]] <- makediag(as.numeric(zero.diags))
    }
    for (el in c("B", "U")) {
      ifree <- min(i, dim(free[[el]])[3])
      # I don't know what par$Z will be.  I want to create a par$Z where there is a non-zero value for any potentially non-zero Z's
      tmp.MLEobj <- list(marss = MODELobj, par = list(Z = matrix(1, dim(free$Z)[2], 1)))
      tmp.MLEobj$marss$fixed$Z[tmp.MLEobj$marss$fixed$Z != 0] <- 1
      tmp.MLEobj$marss$free$Z[tmp.MLEobj$marss$free$Z != 0] <- 1
      parZ <- parmat(tmp.MLEobj, "Z", t = i)$Z
      II <- diag(1, par.dims[[el]][2])

      dpart <- sub3D(free[[el]], t = ifree)
      par.not.fixed <- any(((II %x% (II0$R %*% parZ %*% II0$Q)) %*% dpart) != 0)

      if (par.not.fixed) {
        msg <- c(msg, paste("t=", i, ": For method=kem (EM), if an element of the diagonal of R & Q is 0, the corresponding row of ", par.test, " must be fixed.\n", sep = ""))
        ok <- FALSE
      }
    } # for par.test
  } # end for loop over time

  ############ Check that B^{0} is fixed
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  Tmax <- 0
  for (par.test in c("B")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }
  II0 <- list()
  for (i in 1:Tmax) {
    for (el in c("Q")) {
      ifixed <- min(i, dim(fixed[[el]])[3])
      ifree <- min(i, dim(free[[el]])[3])
      diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
      zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , ifixed, drop = FALSE] == 0, 1, all) # fixed rows
      II0[[el]] <- makediag(as.numeric(zero.diags))
    }
    for (el in c("B")) {
      ifree <- min(i, dim(free[[el]])[3])
      II <- diag(1, par.dims[[el]][2])
      dpart <- sub3D(free[[el]], t = ifree)
      par.not.fixed <- any(((II %x% II0$Q) %*% dpart) != 0)

      if (par.not.fixed) {
        msg <- c(msg, paste("t=", i, ": If an element of the diagonal of Q is 0, the corresponding row and col of ", par.test, " must be fixed.\n", sep = ""))
        ok <- FALSE
      }
    } # for par.test
  } # end for loop over time

  ############ Check that if u^{0} or xi^{0} are estimated, B adjacency matrix is time invariant
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  Tmax <- 0
  for (par.test in c("U")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }
  II0 <- list()
  el <- "Q"
  ifixed <- min(i, dim(fixed[[el]])[3])
  ifree <- min(i, dim(free[[el]])[3])
  diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
  zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , 1, drop = FALSE] == 0, 1, all) # fixed rows
  II0[[el]] <- makediag(as.numeric(zero.diags))

  dpart <- sub3D(free$x0, t = 1)
  test.adj <- any((II0$Q %*% dpart) != 0)
  for (i in 1:Tmax) {
    dpart <- sub3D(free$U, t = 1)
    test.adj <- test.adj & any((II0$Q %*% dpart) != 0) # II0$Q required to be time constant above
  }

  if (test.adj) { # means x0^{0} or u^{0} being estimated
    Tmax <- 0
    for (par.test in c("U", "B")) {
      Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
    }
    for (i in 1:Tmax) {
      el <- "B"
      ifree <- min(i, dim(free[[el]])[3])
      # I don't know what par$B will be.  I want to create a par$B where there is a non-zero value for any potentially non-zero B's
      tmp.MLEobj <- list(marss = MODELobj, par = list(B = matrix(1, dim(free$B)[2], 1)))
      tmp.MLEobj$marss$fixed[[el]][tmp.MLEobj$marss$fixed[[el]] != 0] <- 1
      tmp.MLEobj$marss$free[[el]][tmp.MLEobj$marss$free[[el]] != 0] <- 1
      adjB <- parmat(tmp.MLEobj, el, t = i)[[el]]
      adjB[adjB != 0] <- 1
      adjB <- unname(adjB)
      if (i == 1) {
        adjB.1 <- adjB
      } else {
        if (!isTRUE(all.equal(adjB, adjB.1))) {
          msg <- c(msg, paste("t=", i, ": If u^{0} or xi^{0} are estimated, the adjacency matrix specified by B must be time constant.\n", sep = ""))
          ok <- FALSE
        }
      }
    } # end for loop over time
  } # if the adj test needs to be done

  ############ Check that II_q^{d} and II_q^{is} are time constant
  # See Summary of Requirements for Degenenate Models in EMDerivations.pdf
  Tmax <- 0
  for (par.test in c("Q")) {
    Tmax <- max(Tmax, dim(fixed[[par.test]])[3], dim(free[[par.test]])[3])
  }
  IIz <- IIp <- OMGz <- OMGp <- IId <- IIis <- list()
  for (i in 1:Tmax) {
    el <- "Q"
    ifixed <- min(i, dim(fixed[[el]])[3])
    ifree <- min(i, dim(free[[el]])[3])
    diag.rows <- 1 + 0:(par.dims[[el]][1] - 1) * (par.dims[[el]][1] + 1)
    zero.diags <- is.fixed(free[[el]][diag.rows, , ifree, drop = FALSE], by.row = TRUE) & apply(fixed[[el]][diag.rows, , ifixed, drop = FALSE] == 0, 1, all) # fixed rows
    IIz[[el]] <- makediag(as.numeric(zero.diags))
    IIp[[el]] <- diag(1, par.dims[[el]][1]) - IIz[[el]]
    OMGz[[el]] <- diag(1, par.dims[[el]][1])[diag(IIz[[el]]) == 1, , drop = FALSE]
    OMGp[[el]] <- diag(1, par.dims[[el]][1])[diag(IIp[[el]]) == 1, , drop = FALSE]

    # I don't know what par$B will be.  I want to create a par$B where there is a non-zero value for any potentially non-zero B's
    tmp.MLEobj <- list(marss = MODELobj, par = list(B = matrix(1, dim(free$B)[2], 1)))
    tmp.MLEobj$marss$fixed$B[tmp.MLEobj$marss$fixed$B != 0] <- 1
    tmp.MLEobj$marss$free$B[tmp.MLEobj$marss$free$B != 0] <- 1
    Adj.mat <- parmat(tmp.MLEobj, "B", t = i)$B
    Adj.mat[Adj.mat != 0] <- 1
    Adj.mat <- unname(Adj.mat)
    Adj.mat.pow.m <- matrix.power(Adj.mat, m) # to find all the linkages

    Q.0.rows.of.Adj.mat <- OMGz[[el]] %*% Adj.mat.pow.m
    # These are the columns corresponding to the directly stochastic bit: Q.0.rows.of.Adj.mat%*%t(OMGp$Q)
    if (dim(OMGp[[el]])[1] != 0) {
      tmp <- Q.0.rows.of.Adj.mat
      # which rows of the + columns are all zero
      if (dim(Q.0.rows.of.Adj.mat)[1] != 0) tmp <- apply(Q.0.rows.of.Adj.mat %*% t(OMGp[[el]]) == 0, 1, all)
      tmp <- t(OMGz[[el]]) %*% matrix(as.numeric(tmp), ncol = 1) # expand back outl 1s where the deterministic x's are
      IId[[el]] <- makediag(tmp)
      IId[[el]] <- unname(IId[[el]])
      IIis[[el]] <- diag(1, m) - IId[[el]] - IIp[[el]]
      IIis[[el]] <- unname(IIis[[el]])
    } else { # Q all 0
      IId[[el]] <- diag(1, par.dims[[el]][1])
      IIis[[el]] <- diag(0, par.dims[[el]][1])
    }
    if (i == 1) {
      IId.1 <- IId[[el]]
      IIis.1 <- IIis[[el]]
    } else {
      if (!isTRUE(all.equal(IId[[el]], IId.1))) {
        msg <- c(msg, paste("t=", i, ": The location of the deterministic x's must be time constant.\n", sep = ""))
        ok <- FALSE
      }
      if (!isTRUE(all.equal(IIis[[el]], IIis.1))) {
        msg <- c(msg, paste("t=", i, ": The location of the indirectly stochastic x's must be time constant.\n", sep = ""))
        ok <- FALSE
      }
    }
  } # end for loop over time

  return(list(ok = ok, msg = msg))
}


  # ----------------------------------------------------------------------------------------------
  # predict the maturity of crabs based upon determined data
  # ----------------------------------------------------------------------------------------------

  predictmaturity = function(x, method="DFA") {
    # codes

    # sex codes
    male = 0
    female = 1
    sex.unknown = 2

    # maturity codes
    immature = 0
    mature = 1
    mat.unknown = 2

    # basic biological limits
      x$mat[ which( x$cw<50 ) ]  = immature
      x$mat[ which( x$cw>150 ) ] = mature

    # males: order is important .. last is most imperative
      x$mat[ which( x$sex == male & x$shell %in% c(4,5)) ] = mature
      x$mat[ which( x$sex == male & x$cw < 50) ] = immature
      x$mat[ which( x$sex == male & x$cw > 150) ] = mature
      x$mat[ which( x$sex == male & x$chela < 5) ] = immature
      x$mat[ which( x$sex == male & x$chela > 33) ] = mature

    # female: sequence is important .. last is most imperative
      x$mat[ which( x$sex == female & x$shell %in% c(4,5)) ] = mature
      x$mat[ which( x$sex == female & x$cw < 30) ] = immature
      x$mat[ which( x$sex == female & x$cw > 80) ] = mature
      x$mat[ which( x$sex == female & x$abdomen < 20) ] = immature
      x$mat[ which( x$sex == female & x$abdomen > 50) ] = mature
      x$mat[ which( x$sex == female & x$gonad %in% c(1,2,3) )] = immature
      x$mat[ which( x$sex == female & x$eggPr == 0 )] = immature
      x$mat[ which( x$sex == female & x$eggPr %in% c(1:4) )] = mature # berried females
      x$mat[ which( x$sex == female & x$eggcol %in% c(1,2,3,4) )] = mature

    if (method=="DFA") {
      # males
      test.male = -25.32404 * log(x$cw) + 19.775707 * log(x$chela) + 56.649941
      x$mat[ which(x$sex==male & test.male <= 0 ) ] = immature
      x$mat[ which(x$sex==male & test.male > 0 ) ] = mature
    # females
      test.female = 16.422757 * log(x$cw) - 14.756163 * log(x$abdomen) - 14.898721
      x$mat[ which(x$sex==female  & test.female > 0 ) ] = immature
      x$mat[ which(x$sex==female  & test.female <= 0) ] = mature
    }

    if (method=="logistic.regression") {
      require(mgcv)

      ##TODO- need to add a final step for animals without a SC. Maybe revert to DFA method for these only?

      x$rs = NA
      x$mat.predicted = NA

      WW = which( x$mat %in% c(immature, mature) )
      MM = which( x$sex==male )
      FF = which( x$sex==female )
      SC = which( x$shell %in% 1:5 )
      cleandata_mm = which( x$cw > 0 & x$chela > 0 )
      cleandata_ff = which( x$cw > 0 & x$abdomen > 0 )

      x$mat = as.factor(x$mat)
      x$sex = as.factor(x$sex)
      #      x$shell = as.factor(x$shell)

      imm = intersect( intersect( intersect( MM, WW ), SC ), cleandata_mm )
      iff = intersect( intersect( intersect( FF, WW ), SC ), cleandata_ff )

      modm1 = glm( mat ~ log(cw) + log(chela) + shell, data=x[imm,], family=binomial(link="logit") )
      modf1 = glm( mat ~ log(cw) + log(abdomen) + shell, data=x[iff,], family=binomial(link="logit") )

      x$rs[imm]  = residuals( modm1 )
      x$rs[iff]  = residuals( modf1 )

      # reject too large a residual variability
      rs3 =  which( abs(x$rs) > 1.96 )  # reject too large a residual variability -- log-scaled ... residual =1 is large
      # these have potentially problematic data inputs in either cw and/or chela. etc
      # assume CW is OK and set all other data in the main dataset to uncertain/missing
      x$chela[rs3] = NA
      x$abdomen[rs3] = NA

      # update data vectors removing "bad" data and redo model
      cleandata_mm = which( x$cw > 0 & x$chela > 0 )
      cleandata_ff = which( x$cw > 0 & x$abdomen > 0 )
      imm = intersect( intersect( intersect( MM, WW ), SC ), cleandata_mm )
      iff = intersect( intersect( intersect( FF, WW ), SC ), cleandata_ff )

      modm1 = glm( mat ~ log(cw) + log(chela) + shell, data=x[imm,], family=binomial(link="logit") )
      modf1 = glm( mat ~ log(cw) + log(abdomen) + shell, data=x[iff,], family=binomial(link="logit") )

      # require( boot )  # inv.logit
      # plot( modm1, trans=inv.logit,  rug=T, jit=T, scale=0 )
      # plot( modf1, trans=inv.logit,  rug=T, jit=T, scale=0 )

      # update data vectors removing "bad" data and redo model
      cleandata_mm = which( x$cw > 0 & x$chela > 0 )
      cleandata_ff = which( x$cw > 0 & x$abdomen > 0 )
      imm = intersect( intersect( intersect( MM, WW ), SC ), cleandata_mm )
      iff = intersect( intersect( intersect( FF, WW ), SC ), cleandata_ff )

      modm1 = glm( mat ~ log(cw) + log(chela) + shell, data=x[imm,], family=binomial(link="logit") )
      modf1 = glm( mat ~ log(cw) + log(abdomen) + shell, data=x[iff,], family=binomial(link="logit") )

      # identify inds without maturity indications
      WW = which( !(x$mat %in% c(immature, mature) )  )
      imm = intersect( intersect( intersect( MM, WW ), SC ), cleandata_mm )
      iff = intersect( intersect( intersect( FF, WW ), SC ), cleandata_ff )

      x$mat.predicted[imm] = ifelse( predict( modm1, x[imm,], type="response" ) < 0.5, immature, mature )
      x$mat.predicted[iff] = ifelse( predict( modf1, x[iff,], type="response" ) < 0.5, immature, mature )

      x$mat[imm] = x$mat.predicted[imm]
      x$mat[iff] = x$mat.predicted[iff]

#animals without shell did not get assigned mat through models to this point so...
      cleandata_mm = which( x$cw > 0 & x$chela > 0 )
      cleandata_ff = which( x$cw > 0 & x$abdomen > 0 )
      NoC= which( is.na(x$shell)) #individuals w/o shell condition
      WW = which( !(x$mat %in% c(immature, mature) )  ) #individuals w/o maturity indications

      imm = intersect( intersect( intersect( MM, WW ), NoC ), cleandata_mm )
      iff = intersect( intersect( intersect( FF, WW ), NoC ), cleandata_ff )

      modm1 = glm( mat ~ log(cw) + log(chela), data=x[imm,], family=binomial(link="logit") )
      modf1 = glm( mat ~ log(cw) + log(abdomen) , data=x[iff,], family=binomial(link="logit") )

      x$mat.predicted[imm] = ifelse( predict( modm1, x[imm,], type="response" ) < 0.5, immature, mature )
      x$mat.predicted[iff] = ifelse( predict( modf1, x[iff,], type="response" ) < 0.5, immature, mature )

      x$mat[imm] = x$mat.predicted[imm]
      x$mat[iff] = x$mat.predicted[iff]

      x$rs = NULL
      x$mat.predicted = NULL

    }

    return(x)
  }


#' Mean pairwise distance
#'
#' @param comm Community matrix giving sites (rows) by species (columns). Column names should match the
#'                   names in the phylogeny
#' @param phylogeny Phylogeny in the format used by \code{\link{ape}}; if missing dis is required
#' @param dis Species distance matrix; if missing will be extracted from the phylogeny
#' @param dis.transform Function (e.g., sqrt) to transform distance before computing mpd
#' @details Computes mean pairwise distance (MPD) for all site pairs (including within and between site MPD)
#' @return Matrix of pairwise distances; diagonal is witin site (alpha) MPD, other values are pairwise distances (beta MPD)
#' @useDynLib mbm c_mpd
#' @export
mpd <- function(comm, phylogeny, dis, dis.transform)
{
	if(missing(dis))
	{
		if(missing(phylogeny)) stop("Either phylogeny or dis matrix must be specified")
		dis <- cophenetic(phylogeny)
	} else {
		# do some error checking
		if((nrow(dis) != ncol(dis)) | any((colnames(dis) != rownames(dis))))
			stop("dis must be square with identical row and column names")
	}

	# make sure all species in comm and in dis match
	taxa <- sort(intersect(colnames(comm), rownames(dis)))
	if(length(taxa) < ncol(comm))
		warning(ncol(comm) - length(taxa), " taxa were dropped from comm because they were not in dis")
	if(length(taxa) < nrow(dis))
		warning(nrow(dis) - length(taxa), " taxa were dropped from dis because they were not in comm")
	dis <- dis[taxa, taxa]
	comm <- comm[,taxa]

	if(! missing(dis.transform))
		dis <- dis.transform(dis)

	spMat <- which(comm > 0, arr.ind = TRUE)
	spMat <- spMat[order(spMat[,1]),]
	sites = spMat[,1] - 1	# subtract one from indices because C is 0 indexed
	species = spMat[,2] - 1
	cDist <- numeric(nrow(comm)^2)
	res <- .C("c_mpd", species=as.integer(species), sites=as.integer(sites), N=as.integer(length(sites)),
		dist=as.double(dis), K=as.integer(nrow(dis)), dest=as.double(cDist), S=as.integer(nrow(comm)))
	matrix(res$dest, nrow=nrow(comm), ncol=nrow(comm), dimnames=list(rownames(comm), rownames(comm)))
}


#################
### SH functions
#################

#' Wrapper function for limitless regression discontinuity testing and estimation
#'
#' In a regression discontinuity design, this function tests for an effect using \code{testSH},
#' and inverts the test to estimate a confidence interval and Hodges-Lehmann point estimate.
#' The test is the test associated with the Z coefficient after fitting
#' a model from the robustbase suite that uses Z and R as independent variables.
#' Covariance adjustment is via robust logistic regression if
#' ytilde is binary, robust linear regression otherwise. In the latter case,
#' since we're using `robustbase::lmrob` with the `MM` method, the SE used
#' in the test is of the Huber-White type, allowing for heteroskedasticity
#' and accounting for  propagation of errors from the preliminary fitting
#' done by the robust regression routine.
#'
#' This is the method recommended in version 2 of Limitless
#' Regression Discontinuity
#'
#' @param dat Data set with columns \code{Z}, \code{R}, an outcome variable, and (optionally)
#' an indicator for treatment received, in the case of partial compliance.
#' @param BW An optional bandwidth \code{b>0}. The test uses data for subjects with |R|<b. When
#' \code{BW} is omitted, the function uses sequential covariate balance tests to choose a
#' data-driven bandwidth.
#' @param outcome A string specifying the name of the outcome variable
#' @param Dvar An optional string specifying the name of the treatment-received variable in the
#' case of partial compliance.
#' @param alpha The level of the confidence interval
#' @param rhs A string specifying the \eqn{\mu_\beta(\cdot)} model relating the outcome to
#' \code{R}. Defaults to \code{"R+Z"}
#'
#' @return a list consisting of
#' \describe{
#'  \item{p.value} The p-value testing no effect
#'  \item{CI} a vector of confidence limits and the HL treatment effect
#'  \item{BW} the RDD bandwidth
#'  \item{bal.pval} a p-value testing for covariate balance
#'  \item{n} the number of subjects in the window of analysis
#'  \item{W} the range of R values in the window of analysis
#' }
#' @import robustbase
#' @export

sh <- function(dat,BW,outcome,Dvar='probation_year1',alpha=0.05,rhs=NULL,...){
    if(missing(BW)) BW <- bwMult(dat,...)
    if(!is.null(Dvar)) dat$D <- dat[[Dvar]]
    bal.pval <- balMult(dat,BW,method='sh',reduced.covars=FALSE,rhs=rhs)
    p.value <- testSH(dat=dat,BW=BW,outcome=outcome,rhs=rhs)
    est <- HLsh(dat=dat,BW=BW,outcome=outcome,rhs=rhs)
    CI <- CIsh(dat=dat,BW=BW,outcome=outcome,est=est,alpha=alpha,rhs=rhs)
    list(p.value=p.value,CI=CI,BW=BW,bal.pval=bal.pval,W=c(min(abs(dat$R)),BW),n=sum(abs(dat$R)<BW))
}

#' Univariate covariance-adjusted balance test, using Huber-White SE
#'
#' The test is the test associated with the Z coefficient after fitting
#' a linear or logistic-linear model with Z and R as independent variables.
#' The Huber-White SE is the one provided by the sandwich package.
#' #'
#'
#' @param dat Data set with columns \code{Z}, \code{R} and a covariate
#' @param BW The bandwidth within which to test balance.
#' @param varb The character name of the variable to test balance on.
#' @param rhs  A string specifying the \eqn{\mu_\beta(\cdot)} model relating the outcome to
#' \code{R}. Defaults to \code{"R+Z"}
#'
#' @return scalar, the p-value associated w/ coefficient on Z
#' @import sandwich
#' @export
balOneSH <- function(dat,BW,varb,rhs=NULL){

    if(sum(dat$R<0 & -BW<dat$R)<2 || sum(dat$R>0 & dat$R<BW)<2) return(NA)
    dat <- dat[abs(dat$R)<BW,]
    dat$ytilde <- dat[[varb]]

    form <- if(is.null(rhs)) as.formula('ytilde~Z+R') else
                   as.formula(paste0('ytilde',rhs))

    if(length(unique(dat$ytilde))>2)
        mod <- lm(form,dat=dat)
    else mod <- glm(form,dat=dat,family=binomial)
    vcv <- sandwich::sandwich(mod)
    Z.pos.c <- pmatch("Z", names(coef(mod)))
    Z.pos.v <- pmatch("Z", colnames(vcv))
    tstat <- coef(mod)[Z.pos.c]/sqrt(vcv[Z.pos.v, Z.pos.v])
    2 * pt(-abs(tstat), df=mod$df.residual, lower.tail=T)
}

#' Robust balance test following robust covariate adjustment
#'
#' The test is the test associated with the Z coefficient after fitting
#' a model from the robustbase suite that uses Z and R as independent variables.
#' Covariance adjustment is via robust logistic regression if
#' ytilde is binary, robust linear regression otherwise. In the latter case,
#' since we're using `robustbase::lmrob` with the `MM` method, the SE used
#' in the test is of the Huber-White type, allowing for heteroskedasticity
#' and accounting for  propagation of errors from the preliminary fitting
#' done by the robust regression routine.
#'
#' This is the method recommended in version 2 of Limitless
#' Regression Discontinuity
#'
#' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
#' @param BW A bandwidth \code{b>0}. The test uses data for subjects with |R|<b
#' @param tau A hypothetical treatment effect. Defaults to 0
#' @param outcome A string specifying the name of the outcome variable
#' @param return.coef If TRUE return the Z-coefficient from the model; if FALSE return
#' the p-value
#' @param rhs A string specifying the \eqn{\mu_\beta(\cdot)} model relating the outcome to
#' \code{R}. Defaults to \code{"R+Z"}
#'
#' @return scalar, if return.coef the coefficient on Z; otherwise the p-value associated
#' w/ coefficient on Z
#' @import robustbase
#' @export
testSH <- function(dat,BW,tau=0,outcome='Y',return.coef=FALSE,rhs=NULL){
    if(sum(dat$R<0 & -BW<dat$R)<2 || sum(dat$R>0 & dat$R<BW)<2) return(NA)
    dat <- dat[abs(dat$R)<BW,]

    if('D'%in%names(dat)) dat$ytilde <- dat[[outcome]]-tau*dat$D
    else dat$ytilde <- dat[[outcome]]-tau*dat$Z

    ctl <- if (exists('lmrob_seed')) {
        lmrob.control(seed=lmrob_seed, k.max=500, maxit.scale=500)
    } else lmrob.control(          k.max=500, maxit.scale=500)

    form <- if(is.null(rhs)) as.formula('ytilde~Z+R') else
                   as.formula(paste0('ytilde',rhs))

    mod <- lmrob(form,data=dat,method='MM',control=ctl)

    newcov <- try(LRDsandwich(mod))
    mod$cov <- if (inherits(newcov, "try-error")) NA else newcov
    Z.pos = pmatch("Z", names(coef(mod)))
    if(return.coef) return(coef(mod)[Z.pos])
    coef(summary(mod))[Z.pos,4]

}

#' Hodges-Lehmann estimate of a treatment effect for limitless regression discontinuity
#'
#' Inverts the test in \code{testSH()} to find the Hodges-Lehmann point estimate
#'
#'
#' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
#' @param BW A bandwidth \code{b>0}. The test uses data for subjects with |R|<b
#' @param outcome A string specifying the name of the outcome variable
#' @param rhs A string specifying the \eqn{\mu_\beta(\cdot)} model relating the outcome to
#' \code{R}. Defaults to \code{"R+Z"}
#'
#' @return scalar, the Hodges-Lehmann estimate
#' @import robustbase
#' @export

HLsh <- function(dat,BW,outcome='Y',rhs=NULL){

    shortFunction <- function(tau)
        testSH(dat=dat,BW=BW,tau=tau,outcome=outcome,return.coef=TRUE,rhs=rhs)
    out <- try(uniroot(shortFunction,interval=c(-2,2),extendInt='yes')$root)
    ifelse(inherits(out, 'try-error'),NA,out)
}

#' Confidence Interval and Hodges-Lehmann estimate of a treatment effect for limitless
#' regression discontinuity
#'
#' Inverts the test in \code{testSH()} to find the Confidence Interval and Hodges-Lehmann
#' point estimate
#'
#'
#' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
#' @param BW A bandwidth \code{b>0}. The test uses data for subjects with |R|<b
#' @param outcome A string specifying the name of the outcome variable
#' @param est A point estimate generated by \code{HLsh}. If not provided it will be estimated
#' automatically.
#' @param alpha the level of the confidence interval.
#' @param rhs A string specifying the \eqn{\mu_\beta(\cdot)} model relating the outcome to \code{R}. Defaults to \code{"R+Z"}
#'
#' @return a vector including
#' \describe{
#'  \item{CI1} and
#'  \item{CI2}, the lower and upper bounds of the confidence interval;
#'  \item{est}, the HL point estimate
#' }
#' @import robustbase
#' @export
CIsh <- function(dat,BW,outcome='Y',est,alpha=0.05,rhs=NULL){

    shortFunction <- function(tau) testSH(dat=dat,BW=BW,tau=tau,outcome=outcome,rhs=rhs)-alpha
    if(missing(est)) est <- HLsh(dat=dat,BW=BW,outcome=outcome)
    CI1 <- uniroot(shortFunction,interval=c(-2,est),extendInt='upX')$root
    CI2 <- uniroot(shortFunction,interval=c(est,2),extendInt='downX')$root
    c(CI1=CI1,CI2=CI2,est=est)
}

###############
### IK method
#############

#' Wrapper for Imbens-Kalyanaraman style RDD analysis
#'
#' Uses OLS to estimate the RDD Local Average Treatment Effect (LATE) as in
#' Imbens, G. and Kalyanaraman, K. (2012). Uses a "rectangular" kernal.
#' Tests balance on a set of pre-treatment covariates using \code{balMult} and either the
#' provided bandwidth or the bandwidth calculated with the IK procedure.
#'
#'
#' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
#' @param BW (Optional) A bandwidth \code{b>0}. If not provided it will be estimated from the
#' data
#' @param outcome A string specifying the name of the outcome variable
#'
#' @return a list consisting of
#' \describe{
#'  \item{p.value} The p-value testing no effect
#'  \item{CI} a vector of confidence limits and the HL treatment effect
#'  \item{BW} the RDD bandwidth
#'  \item{bal.pval} a p-value testing for covariate balance
#'  \item{n} the number of subjects in the window of analysis
#'  \item{W} the range of R values in the window of analysis
#' }
#' @import rdd
#' @export

ik <- function(dat,BW=NULL,outcome){
    mod <- ikTest(dat,BW,varb=outcome,justP=FALSE)
    BW <- mod$bw[1]
    bal.pval=balMult(dat=dat,BW=BW,method='sh',reduced.covars=FALSE)
    list(p.value=mod$p[1],CI=c(mod$ci[1,],est=mod$est[1]),bal.pval=bal.pval,
         W=c(min(abs(dat$R)),BW),n=sum(abs(dat$R)<BW))
}

#' Imbens-Kalyanaraman style RDD analysis
#'
#' Uses OLS to estimate the RDD Local Average Treatment Effect (LATE) as in
#' Imbens, G. and Kalyanaraman, K. (2012). Uses a "rectangular" kernal.
#' Tests balance on a set of pre-treatment covariates using \code{balMult} and either the
#' provided bandwidth or the bandwidth calculated with the IK procedure.
#'
#'
#' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
#' @param BW (Optional) A bandwidth \code{b>0}. If not provided it will be
#' estimated from the data
#' @param varb A string specifying the name of the outcome or covariate variable
#' @param rhs Ignored
#' @param justP if TRUE only returns the p-value
#'
#' @return if justP=TRUE, returns the p-value for an effect. if justP=FALSE,
#' returns the fitted RDD model (an object of class "RD").
#' @import rdd
#' @export

ikTest <- function(dat,BW=NULL,varb,rhs=NULL,justP=TRUE,cutpoint=-0.005){
    if(!missing(varb)) dat$Y <- -dat[[varb]]
    if(missing(BW) | is.null(BW))
        mod <- try(RDestimate(Y~R,kernel='rectangular',
                              data=dat,cutpoint=cutpoint))
    else  mod <- try(RDestimate(Y~R,kernel='rectangular',
                                data=dat,bw=BW,cutpoint=cutpoint))
    if(class(mod)=='try-error') return(rep(NA,ifelse(justP,1,5)))
    if(justP) return(mod$p[1])
    mod
}

ikMultBal <- function(dat,BW,xvars,int=FALSE){
    require(systemfit)
    xeq <- if(int) lapply(xvars,function(xx) as.formula(paste0(xx,'~R+Z+R:Z'))) else
    lapply(xvars,function(xx) as.formula(paste0(xx,'~R+Z')))
    names(xeq) <- gsub('_','',xvars)

    sur <- systemfit(xeq,data=subset(dat,abs(R)<BW))

    rest <- paste(names(xeq),'Z',sep='_')
    rest <- paste(rest,'=0')

    linearHypothesis(sur,rest,test='Chisq')$Pr[2]
}

###############
### CCT method
#############

#' @export

cct <- function(dat,BW=NULL,outcome){
    mod <- cctTest(dat,BW,varb=outcome,justP=FALSE)
    BW <- mod$bws['h','left']
    bal.pval=balMult(dat=dat,BW=BW,method='sh',reduced.covars=FALSE)
    list(p.value=mod$pv['Robust',1],CI=c(mod$ci['Robust',],est=mod$coef['Robust','Coeff']),bal.pval=bal.pval,
         W=c(min(abs(dat$R)),BW),n=sum(mod$Nh))
}

#' @export

cctTest <- function(dat,BW=NULL,varb,rhs=NULL,justP=TRUE){
    if(!missing(varb)) dat$Y <- -dat[[varb]]
    if(missing(BW) | is.null(BW))
        mod <- try(with(dat,rdrobust(Y,R,-0.005,kernel='uniform')))
    else  mod <- try(with(dat,rdrobust(Y,R,-0.005,kernel='uniform',h=BW)))
    if(class(mod)=='try-error') return(rep(NA,ifelse(justP,1,5)))
    if(justP) return(mod$pv['Robust',1])
    mod
}

cctMultBal <- function(dat,BW,xvars,int=FALSE){
    balPs <- lapply(xvars,function(xx) rdrobust(xx,dat$R,-0.005,h=BW))
    names(xeq) <- gsub('_','',xvars)

    sur <- systemfit(xeq,data=subset(dat,abs(R)<BW))

    rest <- paste(names(xeq),'Z',sep='_')
    rest <- paste(rest,'=0')

    linearHypothesis(sur,rest,test='Chisq')$Pr[2]
}

################
### CFT Method
##############
##' RDD analysis method modeled on Cattaneo et al 2014, J Causal Inference
##'
##'
##' @title CFT analysis method
##' @param dat Data set with columns \code{Z}, \code{R}, and an outcome variable
##' @param BW (Optional) A bandwidth \code{b>0}. If not provided it will be estimated from the data
##' @param outcome A string specifying the name of the outcome or covariate variable
##' @param alpha 1-confidence interval level
##' @param rhs Ignored
##' @return list of
##' \describe{
##'  \item{p.value} of Fisher-style no effect hypothesis
##'  \item{CI} vector of confidence limits, estimate
##'  \item{bal.pval} balance p-value
##'  \item{n} number of observations inselected window
##'  \item{W}  window
##' }
##' @author lrd author 1
##' @export
cft <- function(dat,BW,outcome,alpha=0.05,rhs=NULL){
    if(missing(BW) |is.null(BW))
        BW <- bwMult(dat,balMult.control=list(method='cft',reduced.covars=FALSE))

    if(sum(dat$R<0 & -BW<dat$R)<2 || sum(dat$R>0 & dat$R<BW)<2) return(NA)

    bal.pval=balMult(dat=dat,BW=BW,method='cft',reduced.covars=FALSE)

    estAll <- cftTest(dat=dat,BW=BW,varb=outcome,alpha=alpha,justP=FALSE)

    list(estAll$p.value,CI=c(estAll$conf.int,est=estAll$estimate),bal.pval=bal.pval,
         n=sum(abs(dat$R)<BW),W=c(min(abs(dat$R)),BW))
}

cftTest <- function(dat,BW,varb,alpha=0.05,rhs=NULL,justP=TRUE){
    if(sum(dat$R<0 & -BW<dat$R)<2 || sum(dat$R>0 & dat$R<BW)<2) return(NA)
    dat <- dat[abs(dat$R)<BW,]

    if(!missing(varb)) dat$Y <- dat[[varb]]
    if(length(unique(dat$Y))==2){
        wilcox <- with(dat,fisher.test(Y,Z,conf.int=!justP,conf.level=1-alpha))
    }
    else wilcox <- with(dat,wilcox.test(Y[Z==1],Y[Z==0],conf.int=!justP,conf.level=1-alpha))
    if(justP) return(wilcox$p.value)
    return(wilcox[c('p.value','conf.int','estimate')])
}

##############################
### data-driven bandwidths
############################

balMult <- function(dat,BW,method='sh',reduced.covars=TRUE,rhs=NULL,bonferonni=TRUE){
    ps <- NULL
    balTest <- switch(method, "sh"=balOneSH,"cft"=cftTest,"ik"=ikTest)

    xvars <- c('lhsgrade_pct','totcredits_year1','male','loc_campus1','loc_campus2',
               'bpl_north_america','english')
    xvars <- if (reduced.covars) c(xvars, 'age') else c(xvars, 'age_at_entry')

    if(method=='ik') return(ikMultBal(dat,BW,xvars))

    for(varb in xvars){
        ps <- c(ps,balTest(dat=dat,BW=BW,varb=varb,rhs=rhs))

    }
    if(bonferonni) ps <- ps*length(ps)
    min(1,min(ps))
}

#' Choose a bandwidth using either Limitless or Permutations method with one covariate, or the IK method
#'
#' @param dat a data.frame. must contain variables `R` (the running variable) and `Z`
#' (treatment) and, if method='ik', `Y` (outcome)
#' @param covname a character string, the name of the covariate to test balance on
#' (unless method='ik')
#' @param method a character string. either 'sh' for Limitless, 'ik' for IK, or 'cft' for
#' Permutations
#'
#' @return bandwidth choice (scalar)
#' @export
bw <- function(dat,covname='x',method='sh',alphax=0.15,plt=FALSE){

    if(method=='ik') return(IKbandwidth(dat$R,dat$Y,kernel='rectangular'))
    else test <- switch(method,"sh"= balOneSH, "cft"=cftTest)
    short <- function(w) test(dat=dat,BW=w,varb=covname)# - alphax
    #uniroot(short,interval=c(min(max(dat$R),-min(dat$R))-0.1,min(max(dat$R),-min(dat$R))),
    #                  extendInt='downX')$root
    bws <- seq(0.02,min(max(dat$R),-min(dat$R)),0.01)
    ps <- vapply(bws,short,1)
    if(plt) plot(ps)
    bws[max(which(ps>alphax))]
}

#' Choose a bandwidth using one of several methods
#'
#' Method options include Limitless, a permutation-testing method, or the IK method
#'
#' @param dat a data.frame. must contain variables `R` (the running variable) and `Z`
#' (treatment) and, if method='ik', `Y` (outcome) along with these covariates: 'lhsgrade_pct',
#' 'totcredits_year1','male','loc_campus1','loc_campus2','bpl_north_america','english',
#' and 'age'
#' @param alpha the level of the balance test
#' @param newbal.control list of optional arguments to `newBal`
#' @param bsw numeric, a sequence of bandwidths to try (in increasing order)
#'
#' @return bandwidth choice (scalar)
#' @export
bwMult <- function(dat,alpha=0.15,balMult.control=list(method='sh',reduced.covars=FALSE), bws = seq(0,2,0.01)){
    stopifnot(is.list(balMult.control),
              !any(c('dat', 'BW') %in% names(balMult.control)),
              is.numeric(bws) && all(bws >=0),
              length(bws)==1 || !is.unsorted(bws))
    short <- function(w) {
        nbargs <- c(list(dat=dat, BW=w), balMult.control)
        try(do.call(balMult, nbargs))
    }

    p <- 0
    i <- length(bws)+1
    while(p<alpha){
        i <- i-1
        if(i==0) return(i)
        bw <- bws[i]
        pval <- short(bw)
        p <- ifelse(is.numeric(pval)&is.finite(pval),pval,0)
    }
    bws[i]
}

##############################
### Frandsen Manipulation Test
############################
frandsenK <- function(R,BW){
    if(!missing(BW)) R <- R[abs(R)<BW]
    rvals <- sort(unique(R))
    delta <- min(c(rvals,NA)-c(NA,rvals),na.rm=TRUE)
    delta <- delta/sd(R,na.rm=TRUE)

    delta^3*dnorm(delta/2)/(2*(pnorm(3*delta/2)-pnorm(delta/2)))
}

frandsenTest <- function(N0,Nplus,Nminus,k){
    m <- N0+Nplus+Nminus
    results <- data.frame(k=k,p=NA)
    for(k in results$k){
        results$p[results$k==k] <- 2*min(pbinom(N0,m,(1-k)/(3-k)),1-pbinom(N0,m,(1+k)/(3+k)))
    }
    results
}

#' Frandsen Manipulation Test
#'
#'
#'
#' @param R a discrete running variable
#' @param cutoff the value of the running variable at the boundary between treatment and control---in Frandsen (2016), the maximum (minimum) R at which subjects are assigned to treatment; alternatively, the minimum (maximum) R at which subjects are assigned to control.
#' @param BW an optional bandwidth, helpful for choosing a benchmark for k
#' @param k a vector of possible values of k, a tuning parameter for the test. The function automatically computes the benchmark value Frandsen (2016) suggests based on normal theory, and adds it to this list.
#'
#' @return a p-value
#'
#' @export
frandsen <- function(R,cutoff,BW,k=c(0,0.01,0.02,0.1)){
    if(missing(BW)) BW <- max(abs(R),na.rm=TRUE)
    k.bench <- frandsenK(R,BW)
    k <- sort(c(k,k.bench))

    N0 <- sum(R==cutoff,na.rm=TRUE)
    rplus <- min(R[R>cutoff],na.rm=TRUE)
    rminus <- max(R[R<cutoff],na.rm=TRUE)
    Nplus <- sum(R==rplus,na.rm=TRUE)
    Nminus <- sum(R==rminus,na.rm=TRUE)

    frandsenTest(N0,Nplus,Nminus,k)
}