library (gsubfn)
setwd('C:/Users/midingoy/Documents/crop2mlcatalog/SQ_Energy_Balance/src/r')
source('Netradiation.r')
source('Netradiationequivalentevaporation.r')
source('Priestlytaylor.r')
source('Conductance.r')
source('Diffusionlimitedevaporation.r')
source('Penman.r')
source('Ptsoil.r')
source('Soilevaporation.r')
source('Evapotranspiration.r')
source('Soilheatflux.r')
source('Potentialtranspiration.r')
source('Cropheatflux.r')
source('Canopytemperature.r')

model_energybalance <- function (minTair = 0.7,
         maxTair = 7.2,
         albedoCoefficient = 0.23,
         stefanBoltzman = 4.903e-09,
         elevation = 0.0,
         solarRadiation = 3.0,
         vaporPressure = 6.1,
         extraSolarRadiation = 11.7,
         lambdaV = 2.454,
         hslope = 0.584,
         psychrometricConstant = 0.66,
         Alpha = 1.5,
         vonKarman = 0.42,
         heightWeatherMeasurements = 2.0,
         zm = 0.13,
         d = 0.67,
         zh = 0.013,
         plantHeight = 0.0,
         wind = 124000.0,
         deficitOnTopLayers = 5341.0,
         soilDiffusionConstant = 4.2,
         VPDair = 2.19,
         rhoDensityAir = 1.225,
         specificHeatCapacityAir = 0.00101,
         tau = 0.9983,
         tauAlpha = 0.3,
         isWindVpDefined = 1){
    #'- Name: EnergyBalance -Version: 001, -Time step: 1
    #'- Description:
    #'            * Title: EnergyBalance
    #'            * Author: Pierre MARTRE
    #'            * Reference: Modelling energy balance in the wheat crop model SiriusQuality2: Evapotranspiration and canopy and soil temperature calculations
    #'            * Institution: INRA/LEPSE
    #'            * ExtendedDescription: see documentation at http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * ShortDescription: This component calculates the canopy temperature and energy balance
    #'- inputs:
    #'            * name: minTair
    #'                          ** description : minimum air temperature
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : -30
    #'                          ** max : 45
    #'                          ** default : 0.7
    #'                          ** unit : °C
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: maxTair
    #'                          ** description : maximum air Temperature
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : -30
    #'                          ** max : 45
    #'                          ** default : 7.2
    #'                          ** unit : °C
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: albedoCoefficient
    #'                          ** description : albedo Coefficient
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.23
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: stefanBoltzman
    #'                          ** description : stefan Boltzman constant
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 4.903E-09
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: elevation
    #'                          ** description : elevation
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0
    #'                          ** min : -500
    #'                          ** max : 10000
    #'                          ** unit : m
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: solarRadiation
    #'                          ** description : solar Radiation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 3
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : MJ m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: vaporPressure
    #'                          ** description : vapor Pressure
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 6.1
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : hPa
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: extraSolarRadiation
    #'                          ** description : extra Solar Radiation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 11.7
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : MJ m2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: lambdaV
    #'                          ** description : latent heat of vaporization of water
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 2.454
    #'                          ** min : 0
    #'                          ** max : 10
    #'                          ** unit : MJ kg-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: hslope
    #'                          ** description : the slope of saturated vapor pressure temperature curve at a given temperature
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.584
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : hPa °C-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: psychrometricConstant
    #'                          ** description : psychrometric constant
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.66
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: Alpha
    #'                          ** description : Priestley-Taylor evapotranspiration proportionality constant
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 1.5
    #'                          ** min : 0
    #'                          ** max : 100
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: vonKarman
    #'                          ** description : von Karman constant
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** default : 0.42
    #'                          ** unit : dimensionless
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'                          ** parametercategory : constant
    #'            * name: heightWeatherMeasurements
    #'                          ** description : reference height of wind and humidity measurements
    #'                          ** parametercategory : soil
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10
    #'                          ** default : 2
    #'                          ** unit : m
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: zm
    #'                          ** description : roughness length governing momentum transfer, FAO
    #'                          ** parametercategory : constant
    #'                          ** inputtype : parameter
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** default : 0.13
    #'                          ** unit : m
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: d
    #'                          ** description : corresponding to 2/3. This is multiplied to the crop heigth for calculating the zero plane displacement height, FAO
    #'                          ** inputtype : parameter
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.67
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit : dimensionless
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547rl
    #'            * name: zh
    #'                          ** description : roughness length governing transfer of heat and vapour, FAO
    #'                          ** parametercategory : constant
    #'                          ** inputtype : parameter
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** default : 0.013
    #'                          ** unit : m
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: plantHeight
    #'                          ** description : plant Height
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : mm
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'                          ** variablecategory : auxiliary
    #'            * name: wind
    #'                          ** description : wind
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 124000
    #'                          ** min : 0
    #'                          ** max : 1000000
    #'                          ** unit : m/d
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: deficitOnTopLayers
    #'                          ** description : deficit On TopLayers
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 5341
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: soilDiffusionConstant
    #'                          ** description : soil Diffusion Constant
    #'                          ** parametercategory : soil
    #'                          ** datatype : DOUBLE
    #'                          ** default : 4.2
    #'                          ** min : 0
    #'                          ** max : 10
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: VPDair
    #'                          ** description :  vapour pressure density
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** default : 2.19
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : hPa
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: rhoDensityAir
    #'                          ** description : Density of air
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 1.225
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: specificHeatCapacityAir
    #'                          ** description : Specific heat capacity of dry air
    #'                          ** parametercategory : constant
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.00101
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: tau
    #'                          ** description : plant cover factor
    #'                          ** parametercategory : species
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.9983
    #'                          ** min : 0
    #'                          ** max : 100
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: tauAlpha
    #'                          ** description : Fraction of the total net radiation exchanged at the soil surface when AlpaE = 1
    #'                          ** parametercategory : soil
    #'                          ** datatype : DOUBLE
    #'                          ** default : 0.3
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'            * name: isWindVpDefined
    #'                          ** description : if wind and vapour pressure are defined
    #'                          ** parametercategory : constant
    #'                          ** datatype : INT
    #'                          ** default : 1
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** unit :
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : parameter
    #'- outputs:
    #'            * name: netRadiation
    #'                          ** description :  net radiation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : MJ m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: netOutGoingLongWaveRadiation
    #'                          ** description : net OutGoing Long Wave Radiation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: netRadiationEquivalentEvaporation
    #'                          ** variablecategory : auxiliary
    #'                          ** description : net Radiation in Equivalent Evaporation
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: evapoTranspirationPriestlyTaylor
    #'                          ** description : evapoTranspiration of Priestly Taylor
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: diffusionLimitedEvaporation
    #'                          ** description : the evaporation from the diffusion limited soil
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: energyLimitedEvaporation
    #'                          ** description : energy Limited Evaporation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: conductance
    #'                          ** description : the boundary layer conductance
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : m/d
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: evapoTranspirationPenman
    #'                          ** description :  evapoTranspiration of Penman Monteith
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: soilEvaporation
    #'                          ** description : soil Evaporation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: evapoTranspiration
    #'                          ** description : evapoTranspiration
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : mm
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: potentialTranspiration
    #'                          ** description : potential Transpiration
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: soilHeatFlux
    #'                          ** description : soil Heat Flux
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: cropHeatFlux
    #'                          ** description :  crop Heat Flux
    #'                          ** variablecategory : rate
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: minCanopyTemperature
    #'                          ** description : minimal Canopy Temperature
    #'                          ** datatype : DOUBLE
    #'                          ** variablecategory : state
    #'                          ** min : -30
    #'                          ** max : 45
    #'                          ** unit : degC
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'            * name: maxCanopyTemperature
    #'                          ** description : maximal Canopy Temperature
    #'                          ** datatype : DOUBLE
    #'                          ** variablecategory : state
    #'                          ** min : -30
    #'                          ** max : 45
    #'                          ** unit : degC
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    list[netRadiation, netOutGoingLongWaveRadiation] <- model_netradiation(minTair, maxTair, albedoCoefficient, stefanBoltzman, elevation, solarRadiation, vaporPressure, extraSolarRadiation)
    conductance <- model_conductance(vonKarman, heightWeatherMeasurements, zm, zh, d, plantHeight, wind)
    diffusionLimitedEvaporation <- model_diffusionlimitedevaporation(deficitOnTopLayers, soilDiffusionConstant)
    netRadiationEquivalentEvaporation <- model_netradiationequivalentevaporation(lambdaV, netRadiation)
    evapoTranspirationPriestlyTaylor <- model_priestlytaylor(netRadiationEquivalentEvaporation, hslope, psychrometricConstant, Alpha)
    energyLimitedEvaporation <- model_ptsoil(evapoTranspirationPriestlyTaylor, Alpha, tau, tauAlpha)
    evapoTranspirationPenman <- model_penman(evapoTranspirationPriestlyTaylor, hslope, VPDair, psychrometricConstant, Alpha, lambdaV, rhoDensityAir, specificHeatCapacityAir, conductance)
    soilEvaporation <- model_soilevaporation(diffusionLimitedEvaporation, energyLimitedEvaporation)
    evapoTranspiration <- model_evapotranspiration(isWindVpDefined, evapoTranspirationPriestlyTaylor, evapoTranspirationPenman)
    soilHeatFlux <- model_soilheatflux(netRadiationEquivalentEvaporation, tau, soilEvaporation)
    potentialTranspiration <- model_potentialtranspiration(evapoTranspiration, tau)
    cropHeatFlux <- model_cropheatflux(netRadiationEquivalentEvaporation, soilHeatFlux, potentialTranspiration)
    list[minCanopyTemperature, maxCanopyTemperature] <- model_canopytemperature(minTair, maxTair, cropHeatFlux, conductance, lambdaV, rhoDensityAir, specificHeatCapacityAir)
    return (list ("netRadiation" = netRadiation,"netOutGoingLongWaveRadiation" = netOutGoingLongWaveRadiation,"netRadiationEquivalentEvaporation" = netRadiationEquivalentEvaporation,"evapoTranspirationPriestlyTaylor" = evapoTranspirationPriestlyTaylor,"diffusionLimitedEvaporation" = diffusionLimitedEvaporation,"energyLimitedEvaporation" = energyLimitedEvaporation,"conductance" = conductance,"evapoTranspirationPenman" = evapoTranspirationPenman,"soilEvaporation" = soilEvaporation,"evapoTranspiration" = evapoTranspiration,"potentialTranspiration" = potentialTranspiration,"soilHeatFlux" = soilHeatFlux,"cropHeatFlux" = cropHeatFlux,"minCanopyTemperature" = minCanopyTemperature,"maxCanopyTemperature" = maxCanopyTemperature))
}


## Funciones

# La creación de funciones es la principal utilidad de un lenguaje programado,
# aún en un lenguaje orientado a la estadística como R. Anteriormente nos
# ocupamos de distintos tipos de objeto que podemos encontrar en R. En la pre-
# sente unidad nos dedicaremos a la comprensión de la estructura y funcionamien-
# to de las funciones, así como a la creación y corrección de las mismas.

## ¿Para qué hacer funciones?

# El primer motivo que encontramos para escribir funciones es la practicidad.
# Esto es cierto en situaciones en las que se debe realizar un procedimiento
# que requiere la ejecución de comandos en un orden específico, o volver a eje-
# cutar una serie de comandos que nos quedó muy atrás en el historial y sabemos
# que los vamos a tener que utilizar nuevamente. En casos como estos, puede
# ahorrarnos tiempo y esfuerzo escribir una función que contenga estos pasos y
# que podamos utilizar una y otra vez. Un ejemplo claro de esto es la función
# 'sort', que ejecuta las instrucciones necesarias para ordenar un vector.

x <- sample(1:15)  # Si generamos un vector muestreando los valores de 1 a 15
sort (x)  # la ejecución de 'sort' nos devuelve este vector ordenado.

# Dentro de esta función están las instrucciones necesarias para realizar la
# tarea, y que se ejecutarán sin necesidad de hacerlo paso a paso.

sort

# Más adelante volveremos sobre la estructura de las funciones, su anatomía,
# para entender qué exactamente es cada uno de sus componentes.

# El segundo motivo, que se desprende del anterior, es la generalización.
# Muchas veces vamos a generar una función para atender a un caso particular,
# a un determinado set de datos o situación. Sin embargo, en la medida en que
# las funciones que escribimos nos resulten útiles, es importante poder pasar
# de su aplicabilidad a valores fijos a utilizar variables que podamos modificar
# en cada caso. Veamos un ejemplo, en la creación de una función que nos permita
# calcular el área de un triángulo.

# El problema planteado es el área de un triángulo que presenta 3 cm de base y
# 4 de altura. La forma más sencilla de estimar la misma es, directamente,

4 * 3 / 2

# Sin embargo, si necesitamos hallar el área de varios triángulos, podemos crear
# la función 'area', utilizando base y altura como variables.

area <- function(a, b) {
  a * b / 2
}

area (4, 3)    # Podemos usarla con las medidas de nuestro triángulo
area (12, 10)  # o de cualquier otro.

# En este caso, es muy sencillo entender que se pasó del caso particular de un
# par de valores de base y altura a una función que corre para cualquier par de
# valores. En este sentido, la abstracción es similar a lo que uno ve en las
# matemáticas, en el sentido de pasar de valores particulares a símbolos que
# pueden ser sustituidos en una ecuación por valores conocidos, logrando poder
# aplicar la función a cualquier situación similar.

# Otro ejemplo más ilustrativo puede ser el siguiente: teniendo un vector `x` de
# 15 elementos, podemos calcular su promedio de la siguiente manera:

x <- rpois(15, 4)
p <- sum(x) / 15
p

# Este código sólo sirve para `x` u otro vector con 15 elementos. Si queremos
# adaptar este código para calcular el promedio de cualquier vector, indepen-
# dientemente de su longitud, vamos a tener que sustituir este valor por la
# longitud de un vector genérico, empleando `length(x)`.

p <- sum(x) / length(x)

# En este caso, aún sin crear una función, hemos ganado en abstracción, ya que
# salimos del caso concreto en el que `x` tiene 15 elementos y ahora podemos
# utilizar esta línea de código para hacerlo para cualquier vector `x` posible.
# Pero aún dependemos de esta línea de comando, que podemos haber olvidado o
# puede haber quedado muy atrás en el historial y volver a encontrarla para
# ejecutarla puede ser un derroche de tiempo y esfuerzo. Sin mencionar que el
# vector tiene necesariamente que llamarse `x` para que funcione. En este caso
# puede ser más sencillo crear una función que haga la operación deseada.

f <- function(v) {
  p <- sum(v) / length(v)
  p
}

f(rpois(19, 23))  # Y sabemos que devuelve un resultado para cualquier vector

# Si bien puede parecer un derroche crear una función para una operación tan
# sencilla. Esto es cierto, pero también debe tenerse en cuenta que se pueden agregar nuevas
# capacidades y detalles a la función, consideremos la situación en que nuestro vector
# presenta NAs:

x <- rpois(20, 10)
x[c(3, 6, 7)] <- NA
f(x)

# En tal caso, deberemos agregar un comando para descartar dichos valores en la
# propia función, por ejemplo creando un objeto 'v' dentro de la misma que sea
# nuestro vector sin los NAs.

f <- function(v) {
  v <- v[!is.na(v)]
  p <- sum(v) / length(v)
  p
}

f(x)

# Como último detalle, no es necesario que las funciones representen una
# abstracción o generalización de una operación a partir de un caso concreto.
# Es posible crear funciones sin argumentos, cosas tan simples como:

g <- function() print("Hola mundo")
g()

# En la próxima lección exploraremos los componentes que hacen a las funciones,
# en una suerte de lección de anatomía de las mismas.


#' Calibrate a single station metabolism model
#' @param oxy Dissolved oxygen time series (g/L)
#' @param temp Water temperature time series (degrees); one per DO observation
#' @param light Light, (lux or W/m^2); one per DO observation
#' @param pressure Air pressure (hPa); single value or one per DO observation
#' @param depth Water depth, in meters
#' @param dt Time interval of observations (minutes)
#' @param nsamples The number of posterior samples to return
#' @param prior Priors for the function chosenl, see 'details'
#' @param ... Additional arguments to pass to the rstan
#'
#' @details Implements a one-station stream metaoblism model; much code from Fuß et al 2017.
#'
#' This model requires that the observations consist of a single 24-hour period. Start time
#' may be arbitrary, as long as an entire 24-hours is covered. No checking is done for this,
#' it is up to the user to ensure this is correct.
#'
#' For a prior, you may specify a named list, with each item a vector of parameters to use for
#' the prior. Currently, the following parameters are supported:
#' * lp1: the log of the inverse of the slope of the photosynthesis-irradiance curve; default
#' 		`c(0, 9)`.
#' * lp2: the log of the saturation term of the photosynthesis-irradiance curve; default
#' 		`c(0, 9)`.
#' * k: gas transfer velocity, default c(0, 10)
#' * gpp: daily gross primary productivity, default c(0, 10)
#' * er: daily in-situ ecosystem respiration, default c(0, 10) (note first item must be <= 0)
#'
#' Priors all use a half normal distribution.
#' @references Fuß, T. et al. (2017). Land use controls stream ecosystem metabolism by shifting
#' 		 dissolved organic matter and nutrient regimes. *Freshw Biol* **62**:582–599.
#' @return A fitted rstan model
#' @export
onestation <- function(oxy, temp, light, pressure, depth, dt,
	nsamples = 1000, prior = list(), ...) {

	# basic input validation
	if(length(oxy) != length(temp) | length(oxy) != length(light))
		stop("oxy, light, and temp must have the same length")
	if(length(pressure) == 1)
		pressure = rep(pressure, length(oxy))
	if(length(oxy) != length(pressure))
		stop("length(pressure) must be 1 or equal to length(oxy)")

	if(! "lp1" %in% prior)
		prior$lp1 <- c(0,9)
	if(! "lp2" %in% prior)
		prior$lp2 <- c(0,9)
	if(! "k" %in% prior)
		prior$k = c(0, 10)
	if(! "gpp" %in% prior)
		prior$gpp = c(0, 10)
	if(! "er" %in% prior)
		prior$er = c(0, 10)

	data = list(
		nDO = length(oxy),
		DO = oxy,
		temp = temp,
		light = light,
		pressure = pressure,
		depth = depth,
		delta_t = dt,
		lp1_pr = prior$lp1,
		lp2_pr = prior$lp2,
		k_pr = prior$k,
		gpp_pr = prior$gpp,
	 	er_pr = prior$er)

	file = system.file("stan/onestation.stan", package="NSmetabolism")
	modcode = rstan::stanc_builder(file = file)
	mod = rstan::stan_model(stanc_ret = modcode)
	calib = rstan::sampling(mod, data = data, iter = nsamples, ...)
	return(calib)
}


probitKDH<-function(minimal_data,num_cat,num_cont, returned_iters = 500, burn = 25, prior_var = 10)
{
	intvar = groupvar = one_way_var = two_way_var = cont_var = prior_var

  #numper of iterations the sampler will have to run
  iters=burn+returned_iters
  #create lm formula to fit frequentist model (and the model matrix)

  minimal_data[ ,2] = as.factor(minimal_data[ ,2])

  namer=paste0(names(minimal_data)[3:(3+num_cat-1)],sep="+",collapse = "")
  namer=substr(namer,1,nchar(namer)-1)
  formulaa=paste(names(minimal_data)[1],'~',names(minimal_data)[2],"+ (",namer,")^2")
  #handle continuous
  if(num_cont>0)
  {
    cont_addition=paste0(names(minimal_data)[(3+num_cat):length(names(minimal_data))],sep="+",collapse = "")
    cont_addition=substr(cont_addition,1,nchar(cont_addition)-1)
    formulaa=paste(formulaa,'+',cont_addition)
  }
  if(num_cat+num_cont<1)
  {
    formulaa=paste0(names(minimal_data)[1],'~',names(minimal_data)[2])
  }
    frequentist_model=glm(formula(formulaa),family=binomial(link="logit"),data=minimal_data)

  #extract the parameter estimates for the hospitals / subunits, hard code first to zero to take into account reference level
  ests=c(0,coefficients(frequentist_model)[which(substr(names(coefficients(frequentist_model)),1,6)==names(minimal_data)[2])])

  #same for covariance
  covv=vcov(frequentist_model)[1:(length(ests)),1:(length(ests))]
  covv[1,]=0
  covv[,1]=0

  #reparameterize as described in D-G paper
  xform_mat=diag(1,length(ests))-replicate(length(ests),table(minimal_data[,2]))/sum(table(minimal_data[,2]))
  new_ests=c(ests%*%xform_mat)
  new_cov_mat=t(xform_mat)%*%covv%*%xform_mat
  z_frequentist=new_ests/sqrt(diag(new_cov_mat))

  #bayesian model, first extract model matrix
  xx=model.matrix(frequentist_model)
  yy=as.numeric(as.character(eval(parse(text=paste0("minimal_data$",fun=names(minimal_data)[1])))))
  num_group_coef=length(table(minimal_data[,2]))-1

  num_2way=dim(xx)[2]-dim(model.matrix(glm(gsub("^2",'',formulaa,fixed=TRUE),family=binomial(link="logit"),data=minimal_data)))[2]
  num_1way=dim(xx)[2]-(1+num_group_coef+num_2way+num_cont)
  n=length(yy)
  p=dim(xx)[2]

  #create limits for the gibbs sampler truncated normals
  lower=rep(-Inf,n)*(-(yy-1))
  lower[is.nan(lower)]<-0
  upper=rep(Inf,n)*yy
  upper[is.nan(upper)]<-0

 #initialize storage and get initial values
  mcmc_betas=matrix(0,iters,p)
  mcmc_betas[1,]=coefficients(frequentist_model)
  colnames(mcmc_betas)=names(coefficients(frequentist_model))
  mcmc_latent=truncnorm::rtruncnorm(n,lower,upper,xx%*%mcmc_betas[1,])

  #priors, specified by macro
  priormean=rep(0,p)
  priorcov=c(rep(intvar,1),rep(groupvar,num_group_coef),rep(one_way_var,num_1way),rep(two_way_var,num_2way))
  if(num_cont>0)
  {
    priorcov=c(priorcov,rep(cont_var,num_cont))
  }

  mcmc_betas = probitFit(yy, xx, priorcov,
	iters = returned_iters + burn)[-(1:burn), ]

  # #pre calculate some stuff from data to make regression faster
  # XpX=crossprod(xx)
  # IR=backsolve(chol(XpX/1+diag(1/priorcov)),diag(p))
  # sigmasq=1

  # #MCMC: probit gibbs
  # for(i in 2:iters)
  # {
    # #first perform regression on latent normals
    # Xpy=crossprod(xx, as.matrix(mcmc_latent))
    # btilde=crossprod(t(IR))%*%(Xpy/sigmasq+diag(1/priorcov)%*%priormean)
    # mcmc_betas[i,] = t(replicate(1,as.vector(btilde))) + rnorm(p)%*%t(IR)
    # #then conditional on the regression coefficients, generate truncated normals
    # mcmc_latent=truncnorm::rtruncnorm(n,lower,upper,xx%*%mcmc_betas[i,])
  # }
  # #delete burn in section
  # mcmc_betas=mcmc_betas[(burn+1):iters,]
  #now reparameterize for d-g paper, but using the bayesian estimate of the covariance of the coefficients

  #estimate of covariance between the subunits, last row is all zero for effect coded category
  covv=cov(mcmc_betas)[1:(length(ests)),1:(length(ests))]
  covv[1,]=0
  covv[,1]=0

  #calculate new xform and covariance matrix to reparameterize
  xform_mat=diag(1,length(ests))-replicate(length(ests),table(minimal_data[,2]))/sum(table(minimal_data[,2]))
  new_cov_mat=t(xform_mat)%*%covv%*%xform_mat

  #extract the coefficients for the subunits
  tempp=(mcmc_betas[,1:length(ests)])
  #code first to zero for our dummy coded category
  tempp[,1]=0
  #uncertainty bands for our z scores plus a point estimate at the median
  est_matrix=tempp%*%xform_mat
  z_mat=est_matrix%*%diag(sqrt(1/(diag(new_cov_mat))))
  bayesian_z_ests=cbind(group_label=names(table(minimal_data[,2])),z_score=apply(z_mat, 2, quantile, probs = c(.5),  na.rm = TRUE))
  colnames(est_matrix)=paste0("group_",names(table(minimal_data[,2])))

  #create data to return
  returner=list(
  column_names=names(minimal_data)[1],
  mcmc_betas=mcmc_betas,
  z_matrix=z_mat,
  frequentist_model=frequentist_model,
  z_frequentist=z_frequentist,
  minimal_dataset=minimal_data,
  regression_formula=formulaa,
  bayesian_z_ests=bayesian_z_ests,
  group_labels=names(table(minimal_data[,2])),
  bayesian_ests=est_matrix,
  model_matrix = xx,
  yy = yy,
  priorcov = priorcov,
  Z= as.numeric(bayesian_z_ests[ ,2]),
  xform_mat = xform_mat
  )
  return(returner)
}


h1Proportion <- function (pv, method = "storey", lambda = 0.5)
{
    method = match.arg(method)
    pi1 = 1 - mean(pv > lambda, na.rm = TRUE)/(1 - lambda)
    if (pi1 < 0) {
        warning(paste("estimated pi1 =", round(pi1, digits = 4),
        "set to 0.0"))
        pi1 = 0
    }
    if (pi1 > 1) {
        warning(paste("estimated pi1 =", round(pi1, digits = 4),
        "set to 1.0"))
        pi1 = 1
    }
    return(pi1)
}


### fimd5.R
### R code from Van Buuren, S. (2012).
###		Flexible Imputation of Missing Data.
###		CRC/Chapman & Hall, Boca Raton, FL.
### (c) 2012 Stef van Buuren, www.multiple-imputation.com
### Version 1, 21mar2012
### Version 2, 4nov2015 tested with mice 2.23
### Tested with Mac OS X 10.7.3, R2.14-2, mice 2.12

if (packageVersion("mice")<'2.12') stop("This code requires mice 2.12.")

library("mice")
library("lattice")
library("gamlss")
library("AGD")

### set Trellis layout parameters
lhset <- trellis.par.get("layout.heights")
lhset$top.padding <- 0
lhset$bottom.padding <- 0
trellis.par.set("layout.heights", lhset)
lwset <- trellis.par.get("layout.widths")
lwset$left.padding <- 0
lwset$right.padding <- 0
trellis.par.set("layout.widths", lwset)

### Section 5.3.2 Predictors

imp <- mice(nhanes, print=FALSE)
imp$predictorMatrix
imp <- mice(cbind(nhanes, chl2=2*nhanes$chl), print=FALSE)
imp$loggedEvents

### Section 5.4.1 Ratio of two variables

imp1 <- mice(boys)
long <- complete(imp1, "long", inc=TRUE)
long$whr <- with(long, wgt/(hgt/100))
# imp2 <- long2mids(long)

### Just anothor varianble (JAV)
boys$whr <- boys$wgt/(boys$hgt/100)
imp.jav <- mice(boys, m=1, seed=32093, maxit=10)
imp.jav$loggedEvents

### Passive imputation
ini <- mice(boys, m=1, maxit=0)
meth <- ini$meth
meth["whr"] <- "~I(wgt/(hgt/100))"
meth["bmi"] <- "~I(wgt/(hgt/100)^2)"
pred <- ini$pred
pred[c("wgt","hgt","bmi"),"whr"] <- 0
pred[c("wgt","hgt","whr"),"bmi"] <- 0
pred
imp.pas <- mice(boys, m=1, meth=meth, pred=pred, seed=32093, maxit=10)

### passive imputation 2
pred[c("wgt","hgt","hc","reg"),"bmi"] <- 0
pred[c("gen","phb","tv"),c("hgt","wgt","hc")] <- 0
pred[,"whr"] <- 0
imp.pas2 <- mice(boys, m=1, meth=meth, pred=pred, seed=32093, maxit=10)

### Figure 5.1
c1 <- cbind(model="JAV",complete(imp.jav))
c2 <- cbind(model="passive",complete(imp.pas))
c3 <- cbind(model="passive 2",complete(imp.pas2))
cd <- rbind(c1,c2, c3)
trellis.par.set(mice.theme())
tp51 <- xyplot(whr~hgt|model,data=cd,layout=c(3,1),
       groups=rep(is.na(imp.jav$data$whr),3), pch=c(1,20), cex=c(0.4,1),
       xlab="Height (cm)", ylab="Weight/Height (kg/m)")
print(tp51)

### Section 5.4.3 Interaction terms
rm(boys)
expr <- expression((wgt-40)*(hc-50))
boys$wgt.hc <- with(boys, eval(expr))
ini <- mice(boys, max=0)
meth <- ini$meth
meth["wgt.hc"] <- paste("~I(",expr,")",sep="")
meth["bmi"] <- ""
pred <- ini$pred
pred[c("wgt","hc"),"wgt.hc"] <- 0
imp.int <- mice(boys, m=1, maxit=10, meth=meth, pred=pred, seed=62587)

### Figure 5.2
miss <- is.na(imp.int$data$wgt.hc)
tp52a <- xyplot(imp.int, wgt~wgt.hc, na.groups = miss,
       cex=c(0.8,1.2), pch=c(1,20),
       ylab="Weight (kg)", xlab="Interaction")
tp52b <- xyplot(imp.int, hc~wgt.hc, na.groups = miss,
       cex=c(0.8,1.2), pch=c(1,20),
       ylab="Head circumference (cm)", xlab="Interaction")
print(tp52a)
print(tp52b)

### Section 5.4.4 Conditional imputation

ini <- mice(airquality[,1:2],maxit=0)
post <- ini$post
post["Ozone"] <- "imp[[j]][,i] <- squeeze(imp[[j]][,i],c(1,200))"
### Note: ifdo() not yet implemented
### so for the moment use old form
## post["Ozone"] <- "ifdo(c(Ozone<1, Ozone>200), c(1, 200))"
imp <- mice(airquality[,1:2],method="norm.nob",m=1,maxit=1,seed=1,post=post)

### Figure 5.3

lwd <- 1.5
par(mfrow=c(1,2))
breaks <- seq(-20, 200, 10)
nudge <- 1
x <- matrix(c(breaks-nudge, breaks+nudge), ncol=2)
obs <- airquality[,"Ozone"]
mis  <- imp$imp$Ozone[,1]
fobs <- c(hist(obs, breaks, plot=FALSE)$counts, 0)
fmis <- c(hist(mis, breaks, plot=FALSE)$counts, 0)
y <- matrix(c(fobs, fmis), ncol=2)
matplot(x, y, type="s",
        col=c(mdc(4),mdc(5)), lwd=2, lty=1,
        xlim = c(0, 170), ylim = c(0,40), yaxs = "i",
        xlab="Ozone (ppb)",
        ylab="Frequency")
box()

tp <- xyplot(imp, Ozone~Solar.R, na.groups=ici(imp),
       ylab="Ozone (ppb)", xlab="Solar Radiation (lang)",
       cex = 0.75, lex=lwd,
       ylim = c(-20, 180), xlim = c(0,350))
print(tp, newpage = FALSE, position = c(0.48,0.08,1,0.92))

### Post-processing on the boys data

post <- mice(boys, m=1, maxit=0)$post
post["gen"] <- "imp[[j]][p$data$age[!r[,j]]<8,i] <- levels(boys$gen)[1]"
post["phb"] <- "imp[[j]][p$data$age[!r[,j]]<8,i] <- levels(boys$phb)[1]"
post["tv"]  <- "imp[[j]][p$data$age[!r[,j]]<8,i] <- 1"
free <- mice(boys, m=1, seed=85444, print=FALSE)
restricted <- mice(boys, m=1, post=post, seed=85444, print=FALSE)

### The following code using ifdo does not yet work
### Use the form given above
## post <- mice(boys, m=1, maxit=0)$post
## post["gen"] <- "ifdo(age<8, levels(gen)[1])"
## post["phb"] <- "ifdo(age<8, levels(phb)[1])"
## post["tv"] <- "ifdo(age<8, 1)"
## free <- mice(boys, m=1, seed=85444)
## restricted <- mice(boys, m=1, post=post, seed=85444)

### Figure 5.4

imp3 <- rbind(free, restricted)  # ignore warning
model <- rep(c("Free","Restricted"),each=nrow(boys))
tp54 <- xyplot(imp3, gen~age|model, pch=c(3,1), cex=c(3,1.5),
            ylab="Genital development", xlab="Age (years)")
print(tp54)

### Section 5.4.5 Compositional data

set.seed(43112)
n <- 400
Y1 <- sample(1:10, size=n, replace=TRUE)
Y2 <- sample(1:20, size=n, replace=TRUE)
Y3 <- 10 + 2 * Y1 + 0.6 * Y2 + sample(-10:10, size=n, replace=TRUE)
Y <- data.frame(Y1, Y2, Y3)
Y[1:100, 1:2] <- NA
md.pattern(Y)

Y123 <- Y1+Y2+Y3
Y12 <- Y123-Y[,3]
P1 <- Y[,1]/Y12
data <- data.frame(Y, Y123, Y12, P1)

ini <- mice(data, maxit=0, m=10, print=FALSE, seed=21772)
meth <- ini$meth
meth["Y1"] <- "~I(P1*Y12)"
meth["Y2"] <- "~I((1-P1)*Y12)"
meth["Y12"] <- "~I(Y123-Y3)"
pred <- ini$pred
pred["P1",] <- 0
pred[c("P1"),c("Y12","Y3")] <- 1
imp1 <- mice(data, meth=meth, pred=pred, m=10, print=FALSE)

round(summary(pool(with(imp1, lm(Y3~Y1+Y2))))[,1:2],2)

### improved solution for Figure 5.5b
ini <- mice(data, maxit=0, m=10, print=FALSE, seed=21772)
meth <- ini$meth
meth["Y1"] <- "~I(P1*Y12)"
meth["Y2"] <- "~I((1-P1)*Y12)"
meth["Y12"] <- "~I(Y123-Y3)"
pred <- ini$pred
pred["P1",] <- 0
pred[c("P1"),c("Y12","Y3")] <- 1
imp1 <- mice(data, meth=meth, pred=pred, m=1, print=FALSE)
pred["P1","Y3"] <- 0
imp2 <- mice(data, meth=meth, pred=pred, m=1, print=FALSE)

### Figure 5.5
imp <- rbind(imp1, imp2)  # ignore warning
model <- rep(c("Y12 and Y3","Y12 only"),each=n)
tp55 <- xyplot(imp, P1~Y12|model, pch=c(1,19), xlab="Y1 + Y2")
print(tp55)

### Section 5.5.1 Visit sequence

### Continue from the imp.int object calculated in section 5.4.3
imp.int$vis
vis <- c(2,3,5,10,6:9)
rm(boys)  # refresh boys data
expr <- expression((wgt-40)*(hc-50))
boys$wgt.hc <- with(boys, eval(expr))
imp.int2 <- mice(boys, m=1, max=1, vis=vis, meth=imp.int$meth, pred=imp.int$pred, seed=23390)

### monotone sequence
imp.int2 <- mice(boys, m=1, max=1, vis="monotone", meth=imp.int$meth, pred=imp.int$pred, seed=23390)

### Section 5.5.2 Convergence

### Figure 5.6 Default color version

imp <- mice(nhanes, seed=62006, maxit=20, print=FALSE)
tp56 <- plot(imp)
print(tp56)

### Figure 5.6 Book version

tp56b <- plot(imp, col=mdc(5), lty=1:5)
print(tp56b)

### Example of pathological convergence
rm(boys)
ini <- mice(boys,max=0,print=FALSE)
meth <- ini$meth
meth["bmi"] <- "~I(wgt/(hgt/100)^2)"
imp.bmi1 <- mice(boys, meth=meth, maxit=20, seed=60109)

### Figure 5.7
### Does not reproduce exactly, but the message is the same
tp57 <- plot(imp.bmi1,c("hgt","wgt","bmi"), col=mdc(5), lty=1:5)
print(tp57)

### Breaking cyclic imputations for hgt and wgt
pred <- ini$pred
pred[c("hgt","wgt"),"bmi"] <- 0
imp.bmi2 <- mice(boys, meth=meth, pred=pred, maxit=20, seed=60109)

### Figure 5.8
### Does not reproduce exactly, but the message is the same
tp58 <- plot(imp.bmi2,c("hgt","wgt","bmi"), col=mdc(5), lty=1:5)
print(tp58)

### Section 5.6.2 Diagnostic graphs

set.seed(24417)
### create 50% missing data in wgt
boys$wgt[sample(1:nrow(boys),nrow(boys)/2)] <- NA
meth <- c("","pmm","pmm","","pmm","polr","polr","pmm","polyreg")
imp <- mice(boys, m=1, meth=meth, seed=53882, print=FALSE)

### Simple scatterplot wgt-age (not given in book)
tp58b <- xyplot(imp, wgt~age|.imp)
print(tp58b)

### Fit separate regression models for wgt
### for observed (n=372) and imputed (n=376) data
cd <- complete(imp)[,-4]
isobs <- !is.na(boys$wgt)
cdobs <- cd[isobs,]
cdmis <- cd[!isobs,]
obs <- gamlss(wgt~age+hgt+hc+gen+phb+tv+reg, data=cdobs)
mis <- gamlss(wgt~age+hgt+hc+gen+phb+tv+reg, data=cdmis)

### Figure 5.9 worm plot
wp.twin(obs, mis, xvar=NULL, xvar.column=2, n.inter=9, col1=mdc(4), col2=mdc(5), ylim=0.9, cex=1, pch=1)

### Figure 5.10
imp <- mice(nhanes, seed=29981)
tp510 <- stripplot(imp, pch=c(1,20))
print(tp510)

### Figure 5.11
tp511 <- densityplot(imp)
print(tp511)

### Calculate propensity scores
fit <- with(imp, glm(ici(imp)~age+bmi+hyp+chl,family=binomial))
ps <- rep(rowMeans(sapply(fit$analyses, fitted.values)),imp$m)

### Figure 5.12
tp512 <- xyplot(imp, bmi~ps|.imp, pch=c(1,19),
       xlab="Probability that record is incomplete",
       ylab="BMI")
print(tp512)


#' Fit a mixture of factor analyzers with a specific number of components.
#'
#' description
#' @param x Matrix or vector of samples. For matrices, rows are samples and columns are variables.
#' @param num_factors Number of factors in the factor analyzers.
#' @param num_components Integer specifying the number of components.
#' @param epsilon For the EM algorithm, stop when the relative difference in log likelihood is less than this epsilon.
#' @param maxsteps Maximum number of steps to take in the EM algorithm. When the maximum number is reached, the current fit will be returned.
#' @export
fit.factor.mixture <- function(x, num_factors, num_components, epsilon = 1e-6, maxsteps = 1000, verbose = F)
{
  nvar <- ncol(x)
  nparam <- num_components * nvar + nvar + num_components * (num_factors * nvar - num_factors * (num_factors - 1) / 2) + num_components - 1
  if (nparam >= nrow(x)) {
    warning("More parameters than samples, consider lowering the number of factors or components")
    return(NULL)
  }

  result <- list()
  result$type <- "mfa"
  result$mfa_res <- NULL
  if (verbose) {
    try(result$mfa_res <- EMMIXmfa::mfa(x, num_components, num_factors, tol=epsilon,
                                        itmax=maxsteps, sigma_type = "unique", D_type = "unique",
                                        nrandom=5, nkmeans=5, conv_measure="ratio"), silent=F)
  } else {
    output <- capture.output(capture.output(result$mfa_res <- EMMIXmfa::mfa(x, num_components, num_factors, tol=epsilon,
                                                                            itmax=maxsteps, sigma_type = "unique", D_type = "unique",
                                                                            nrandom=5, nkmeans=5, conv_measure="ratio"), type="message"), type="output")
  }
  if (class(result$mfa_res)[1] != "emmix" || class(result$mfa_res)[2] != "mfa") {
    warning("Failed to fit MFA")
    return(NULL)
  }
  if (sum(class(result$mfa_res) == c("emmix", "mfa")) != 2) {
    warning("Factor mixture fitting failed")
    return(NULL)
  }

  result$num_components <- num_components
  result$num_factors <- num_factors
  result$weights <- result$mfa_res$pivec
  result$factor_loadings <- result$mfa_res$B
  result$factor_means <- result$mfa_res$mu
  result$covariances <- result$mfa_res$D
  result$log_likelihood <- result$mfa_res$logL
  result$BtBpD <- list()
  for (i in 1:num_components) {
    result$BtBpD[[i]] <- result$mfa_res$B[,,i] %*% t(result$mfa_res$B[,,i]) + result$mfa_res$D[,,i]
  }
  result$AIC <- 2 * nparam - 2 * result$log_likelihood
  result$BIC <- log(nrow(x)) * nparam - 2 * result$log_likelihood

  # Make covariance matrices symmetric (numerical issue?)
  # if (result$num_factors > 1) {
  #   for (i in 1:result$num_components) {
  #     for (j in 1:(result$num_factors-1)) {
  #       for (k in (j+1):result$num_factors) {
  #         cov <- mean(result$factor_covariances[i,j,k], result$factor_covariances[i,j,k])
  #         result$factor_covariances[i,j,k] <- cov
  #         result$factor_covariances[i,k,j] <- cov
  #       }
  #     }
  #   }
  # }

  return(structure(result, class = "mvd.density"))
}

#' Fit a transformed mixture of factor analyzers with a specific number of components.
#'
#' description
#' @param x Matrix or vector of samples. For matrices, rows are samples and columns are variables.
#' @param num_factors Number of factors in the factor analyzers.
#' @param num_components Integer specifying the number of components.
#' @param bounds Dx2 matrix specifying the lower and upper bound for each variable.
#' @param epsilon For the EM algorithm, stop when the relative difference in log likelihood is less than this epsilon.
#' @param maxsteps Maximum number of steps to take in the EM algorithm. When the maximum number is reached, the current fit will be returned.
#' @export
fit.transformed.factor.mixture <- function(x, num_factors, num_components, bounds, epsilon = 1e-6, maxsteps = 1000, verbose = F)
{
  result <- list()
  result$type <- "mfa.transformed"
  result$transform.bounds <- bounds
  transformed <- mvd.transform_to_unbounded(x, bounds)
  result$mfa <- fit.factor.mixture(transformed, num_factors, num_components, epsilon = epsilon, maxsteps = maxsteps, verbose = verbose)
  return(structure(result, class = "mvd.density"))
}

#' Calculate AIC of a mixture of factor analyzers across a range of number of components and factors.
#'
#' description
#' @param x Matrix or vector of samples. For matrices, rows are samples and columns are variables.
#' @param factors Vector specifying the number of factors to test
#' @param components Vector specifying the number of components to test
#' @param optimal.only If TRUE, directly return only the GMM with optimal number of components; otherwise return a structure with fits for all tested number of components/factors.
#' @param epsilon See fit.factor.mixture
#' @param maxsteps See fit.factor.mixture
#' @param verbose Display the fitting progress.
#' @export
factor.mixture.AIC <- function(x, factors = 1:5, components = 1:5, optimal.only = F, epsilon = 1e-6, maxsteps = 1000, verbose = F) {
  result <- list()
  result$factors <- factors
  result$components <- components
  result$AIC <- matrix(NA, length(factors), length(components))
  result$fits <- list()
  for (i in 1:length(factors)) {
    result$fits[[i]] <- list()

    for (j in 1:length(components)) {
      if (verbose) {
        cat("Fitting factors =", factors[i], " components =", components[j], "\n")
      }

      if (factors[i] < ncol(x)) {
        fit <- fit.factor.mixture(x, factors[i], components[j], epsilon = epsilon, maxsteps = maxsteps, verbose=verbose)
        result$fits[[i]][[j]] <- fit
        if(!is.null(fit)) {
          result$AIC[i,j] <- fit$AIC
        }
      } else {
        result$fits[[i]][[j]] <- NULL
      }
    }
  }
  if (optimal.only) {
    ixs <- which(result$AIC == min(result$AIC, na.rm=T), arr.ind = T)
    return(result$fits[[ixs[1]]][[ixs[2]]])
  } else {
    return(result)
  }
}

#' Calculate AIC of a transformed mixture of factor analyzers across a range of number of components and factors.
#'
#' description
#' @param x Matrix or vector of samples. For matrices, rows are samples and columns are variables.
#' @param factors Vector specifying the number of factors to test
#' @param components Vector specifying the number of components to test
#' @param optimal.only If TRUE, directly return only the GMM with optimal number of components; otherwise return a structure with fits for all tested number of components/factors.
#' @param epsilon See fit.factor.mixture
#' @param maxsteps See fit.factor.mixture
#' @param verbose Display the fitting progress.
#' @export
transformed.factor.mixture.AIC <- function(x, bounds, factors = 1:5, components = 1:5, optimal.only = F, epsilon = 1e-6, maxsteps = 1000, verbose = F) {
  result <- list()
  result$factors <- factors
  result$components <- components
  result$AIC <- matrix(NA, length(factors), length(components))
  result$fits <- list()
  for (i in 1:length(factors)) {
    result$fits[[i]] <- list()

    for (j in 1:length(components)) {
      if (verbose) {
        cat("Fitting factors =", factors[i], " components =", components[j], "\n")
      }

      if (factors[i] < ncol(x)) {
        fit <- fit.transformed.factor.mixture(x, factors[i], components[j], bounds, epsilon = epsilon, maxsteps = maxsteps, verbose=verbose)
        result$fits[[i]][[j]] <- fit
        if(!is.null(fit)) {
          result$AIC[i,j] <- fit$mfa$AIC
        }
      } else {
        result$fits[[i]][[j]] <- NULL
      }
    }
  }
  if (optimal.only) {
    ixs <- which(result$AIC == min(result$AIC, na.rm=T), arr.ind = T)
    return(result$fits[[ixs[1]]][[ixs[2]]])
  } else {
    return(result)
  }
}

#' Evaluate a mixture of factor analyzers
#'
#' description
#' @param fit An mvd.density object obtained from fit.factor.mixture
#' @param x Matrix or vector of samples at which to evaluate the mixture of factor analyzers. For matrices, rows are samples and columns are variables.
#' @param log Return log probability density
#' @export
evaluate.factor.mixture <- function(fit, x, log = F) {
  p <- rep(NA, nrow(x))
  for (i in 1:nrow(x)) {
    compp <- rep(NA, fit$num_components)
    for (j in 1:fit$num_components) {
      compp[j] <- mvtnorm::dmvnorm(x[i,], fit$factor_means[,j], fit$BtBpD[[j]], log=log)
    }
    if (log) {
      p[i] <- .logsum(compp + log(fit$weights))
    } else {
      p[i] <- sum(compp * fit$weights)
    }
  }
  return(p)
}


#' mmHg2mb
#'
#' Conversion mmHg2mb to millibar [hPa].
#'
#' @param numeric  mmHg millimeter of mercures.
#' @return
#'
#'
#' @author  Istituto di Biometeorologia Firenze Italy  Alfonso Crisci \email{a.crisci@@ibimet.cnr.it}
#' @keywords  mmHg2mb
#'
#' @export
#'
#'
#'
#'

mmHg2mb<-function(mmHg)
{
  return (mmHg * 1.33322);
}


#' kmh2knots
#'
#' Conversion from kilometer per hour to knot per second.
#'
#' @param numeric  kmh Speed in kilometer per hour.
#' @return
#'
#'
#' @author  Istituto di Biometeorologia Firenze Italy  Alfonso Crisci \email{a.crisci@@ibimet.cnr.it}
#' @keywords  kmh2knots
#'
#' @export
#'
#'
#'
#'

kmh2knots<-function(kmh)
{
 return(kmh * 0.539957);
}


########################### BayesKin ###########################
## Bayesian estimates of rigid body kinematics
## Andy Pohl
## UofC - Faculty of Kinesiology

## Written by: Andy Pohl
## UofC - Faculty of Kinesiology
## June-Dec 2020
## Revision 1: April 2021
################################################################

##################### RunMdl_TripleLink.r  ###################
## Performs analysis comparing Bayesian to Least squares approaches for
## 3 link kinematic chains.
################################################################
rm(list = ls())

WORKING_DIR = "" # Replace with the location of the BayesKin/src directory on the local PC.
SAVE_MDL = TRUE # assume save

INIT_TYPE = 'ls_est' # either 'true_vals', 'ls_est', 'random

setwd(WORKING_DIR)

source('library.r') # Retrieve function library
set.seed(1)  # Set seed for reproducibility

## 1) Specify parameters
n.links = 3               # Number of links.
seg.length = c(0.45, 0.35, 0.25)         # Length of segment specified in m. NB: Pataky pg 2.
r_true = c(0.07, 0.03)      # Origin location in m.
theta_true = c(-55, -110, -10)          # Rotation angle in deg. NB: specified to avoid 0/360 issue
# and mirroring solutions.
sigma_true = 1.5/1000       # Measurement noise in m. NB Noise specified as 1.5mm
# midpoint of range explored by Pataky et al.
true_vals = c(r_true = r_true, theta_true = theta_true, sigma_true = sigma_true)

# Generate posture
links = list(gen_link(seg.length = seg.length[1],
                      plate.center = 0.7*seg.length[1]),
             gen_link(seg.length = seg.length[2],
                      plate.center = 0.5*seg.length[2]),
             gen_link(seg.length = seg.length[3],
                      plate.center = 0.5*seg.length[3]))
posture = gen_posture(links, r = r_true, theta = theta_true)

nits = 1000 # number of iterations.
seeds = 1:nits
i = 1
while (length(list.files('./TripleLink_Mdl/Bayes_p3/')) < nits){
    files = list.files(path = paste0("./TripleLink_Mdl/Bayes_p5/"))
    processed_seeds = as.numeric(gsub("(.+?)(\\_.*)", "\\1", files))
    #set seed
    seed = seeds[i]

    if(!(seed %in% processed_seeds)){
        set.seed(seed)
        print(sprintf("Iteration %.0f of %.0f  -  seed = %.0f",length(list.files('./TripleLink_Mdl/Bayes_p5/')), nits, seed))
        # gen obs
        y = gen_obs(posture, sigma_true)

        #Compute LS solution.
        LS_result = LS_soln(y, links,
                            inits = c(r_true, theta_true, sigma_true),
                            init_type = 'true_vals')

        print("LS Solution Complete")
        r_mdl_file_name = paste0('./TripleLink_Mdl/LS/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, 'LSModel')
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(LS_result, file = r_mdl_file_name)
        #
        # Compute Bayes_P1
            Bayes_p1_result = Bayes_soln(y=y,
                                         links = links,
                                         mdlfile = './BayesModel_p1.jags',
                                         init_type = INIT_TYPE,
                                         true_vals = true_vals,
                                         ls_est = c(LS_result$r.hat, LS_result$theta.hat, LS_result$sigma.hat))
        print("Bayes P1 Solution Complete")
        mdl_type = 'BayesModel_p1'
        r_mdl_file_name = paste0('./TripleLink_Mdl/Bayes_p1/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, mdl_type)
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(Bayes_p1_result, file = r_mdl_file_name)

        # Compute Bayes_P2
        Bayes_p2_result = Bayes_soln(y=y,
                                         links = links,
                                         mdlfile = './BayesModel_p2.jags',
                                         init_type = INIT_TYPE,
                                         true_vals = true_vals,
                                         ls_est = c(LS_result$r.hat, LS_result$theta.hat, LS_result$sigma.hat))

        print("Bayes P2 Solution Complete")
        mdl_type = 'BayesModel_p2'
        r_mdl_file_name = paste0('./TripleLink_Mdl/Bayes_p2/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, mdl_type)
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(Bayes_p2_result, file = r_mdl_file_name)

        # Compute Bayes_P3
        Bayes_p3_result = Bayes_soln(y=y,
                                         links = links,
                                         mdlfile = './BayesModel_p3.jags',
                                         init_type = INIT_TYPE,
                                         true_vals = true_vals,
                                         ls_est = c(LS_result$r.hat, LS_result$theta.hat, LS_result$sigma.hat))

        print("Bayes P3 Solution Complete")
        mdl_type = 'BayesModel_p3'
        r_mdl_file_name = paste0('./TripleLink_Mdl/Bayes_p3/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, mdl_type)
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(Bayes_p3_result, file = r_mdl_file_name)

        # Comppute Bayes P4
        Bayes_p4_result = Bayes_soln(y=y,
                                     links = links,
                                     mdlfile = './BayesModel_p4.jags',
                                     init_type = INIT_TYPE,
                                     true_vals = true_vals,
                                     ls_est = c(LS_result$r.hat, LS_result$theta.hat, LS_result$sigma.hat))

        mdl_type = 'BayesModel_p4'
        r_mdl_file_name = paste0('./TripleLink_Mdl/Bayes_p4/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, mdl_type)
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(Bayes_p4_result, file = r_mdl_file_name)

        # Comppute Bayes P5
        Bayes_p5_result = Bayes_soln(y=y,
                                     links = links,
                                     mdlfile = './BayesModel_p5.jags',
                                     init_type = INIT_TYPE,
                                     true_vals = true_vals,
                                     ls_est = c(LS_result$r.hat, LS_result$theta.hat, LS_result$sigma.hat))

        mdl_type = 'BayesModel_p5'
        r_mdl_file_name = paste0('./TripleLink_Mdl/Bayes_p5/', as.character(as.numeric(seed)))
        r_mdl_file_name = paste0(r_mdl_file_name, '_')
        r_mdl_file_name = paste0(r_mdl_file_name, mdl_type)
        r_mdl_file_name = paste0(r_mdl_file_name, '.rda')
        print(sprintf("Saving file %s", r_mdl_file_name))
        saveRDS(Bayes_p5_result, file = r_mdl_file_name)
    }
    i = i+1
    print('#######################################################################')
}


#' Estimating the generalised joint exceedance (GJE) environmental contours
#'
#' @description
#' This function estimates the generalised joint exceedance (GJE) environmental contours, as proposed in Jonathan et al. (2014), for a
#' given sample data set or a fitted joint distribution of class \code{ht} or \code{wln}.
#'
#' @param object either a fitted joint distribution of class \code{wln} or \code{ht}, or an existing sample data
#' set containing \code{hs} and \code{tp} as columns. See details.
#'
#' @param output_rp the required return periods (in years) for the estimated contours.
#'
#' @param type the type of joint exceedance definition. The default value is 2. See details.
#'
#' @param npy the (optional) number of data points per year. This argument is only needed if the input
#' \code{object} is a sample data set.
#'
#' @param n_point the number of points to output around each contour. The default value is 100.
#'
#' @details
#' The joint exceedance contour is a collection of points corresponding to a constant joint exceedance proability.
#' The definition is one-sided or two-sided depending on the input argument \code{type}:
#'
#' \itemize{
#'
#'   \item If \code{type = 1}, the \eqn{T}-year contour passes through any point \eqn{(hs, tp)} such that the
#'   joint exceedance probability \eqn{P(Hs > hs & Tp > tp)} is one in \eqn{T} years.
#'
#'   \item If \code{type = 2}, the \eqn{T}-year contour passes through an anchor point \eqn{(hs0, tp0)}
#'   where \eqn{hs0} is the (\eqn{T/2})-year wave heights, and \eqn{tp0} is the conditional median of the
#'   wave period given the wave height is \eqn{hs0}. The joint exceedance probability is defined as
#'   \eqn{P(Hs > hs & Tp > tp)} for all \eqn{tp > tp0} and \eqn{P(Hs > hs & Tp < tp)} for all \eqn{tp < tp0}.
#'
#' }
#'
#' The function can be applied to an existing sample set, which can be generated by function \code{\link{sample_jdistr}}
#' or otherwise by importing a CSV file using function \code{\link[data.table]{fread}}. The input sample data
#' object must contain \code{hs} and \code{tp} as columns. The user must also specify the number of points per year
#' \code{npy} and a valid \code{output_rp}, i.e. the maximum \code{output_rp} must be shorter than the total
#' duration of the data set.
#'
#' Alternatively this function can be applied to a fitted joint distribution object
#' generated by function \code{\link{fit_ht}} or \code{\link{fit_wln}}.  When applied to a
#' \code{wln} or \code{ht} object, the contour estimation makes use of the importance
#' sampling technique proposed in Huseby et. al. (2014) to improve the efficiency of the function.
#'
#'
#' @return
#' A set of estimated GJE contours with the specified return periods in the format
#' of a \code{data.table} with \code{rp}, \code{hs}, and \code{tp} as columns.
#'
#' @examples
#' # Estimating type-1 GJE contours based on a fitted model
#' data(ww3_pk)
#' wln = fit_wln(data = ww3_pk, npy = nrow(ww3_pk)/10)
#' ec_wln = estimate_gje(object = wln, output_rp = c(10,100,1000,10000), type = 1)
#' plot_ec(ec = ec_wln, raw_data = ww3_pk)
#'
#' # Estimating type-2 GJE contours based on a sample data set
#' data(ww3_ts)
#' ec_data = estimate_gje(object = ww3_ts, output_rp = c(.5, 1, 2), type = 2, npy = ww3_ts[, .N/10])
#' plot_ec(ec = ec_data, raw_data = ww3_ts)
#'
#' @references
#' Jonathan, P., Ewans, K., Flynn, J., 2014. On the estimation of ocean engineering design contours.
#' ASME J. Offshore Mech. Arct. Eng. 136:041101.
#'
#' Huseby, A., Vanem, E., Natvig, B., 2014. A new Monte Carlo method for environmental contour estimation.
#' Conference Proceedings. DOI:10.1201/b17399-286.
#'
#' @seealso \code{\link{fit_ht}}, \code{\link{fit_wln}}, \code{\link{sample_jdistr}}, \code{\link{plot_ec}}
#'
#' @export
estimate_gje = function(
  object, output_rp, type = 2, npy = NULL, n_point = 100){

  if("wln" %in% class(object)){

    res = .estimate_gje_from_wln(wln = object, output_rp = output_rp, type = type, n_point = n_point)

    if(!is.null(npy)){
      warning("Argument npy will be imported from the provided joint distribution object.  The supplied npy is ignored.")
    }

  }else if("ht" %in% class(object)){

    res = .estimate_gje_from_ht(ht = object, output_rp = output_rp, type = type, n_point = n_point)

    if(!is.null(npy)){
      warning("Argument npy will be imported from the provided joint distribution object.  The supplied npy is ignored.")
    }

  }else if("data.table" %in% class(object)){

    if(is.null(npy)){
      stop("Argument npy must be provided for estimating contours from sample data.")
    }else{
      invalid_rp = output_rp[output_rp>object[, .N/npy]]
      if(length(invalid_rp)==length(output_rp)){
        stop("All output_rp values are invalid as they require extrapolation. Consider fitting a joint distribution first.")
      }else if(length(invalid_rp)>=1 & length(invalid_rp)<length(output_rp)){
        warning("Certain output_rp values are ignored as they requires extrapolation. Consider fitting a joint distribution first.")
      }
      output_rp = setdiff(output_rp, invalid_rp)
    }

    res_list=list()
    for(this_rp in output_rp){
      this_ap_hs = quantile(object$hs, 1-type/npy/this_rp)
      this_contour = .estimate_gje_from_data(
        sample_data = object, n_jex = object[,.N/npy]/this_rp,
        ap_hs = this_ap_hs, type=type, n_point = n_point)
      res_list[[as.character(this_rp)]] = cbind(rp = this_rp, this_contour)
    }
    res = rbindlist(res_list)

  }else{
    stop("The input object must be of class ht, wln or data.table.")
  }

  return(res)
}

.estimate_gje_from_data = function(sample_data, n_jex, ap_hs, type, n_point){
  if(!type %in% c(1,2)){
    stop("Only type 1 or 2 is supported.")
  }
  if(type==2){
    calc = data.table(hs1 = seq(ap_hs, min(sample_data$hs), length.out = ceiling((n_point+1)/2)))
    calc[, n_hs_tail:=sample_data[hs>=hs1, .N], .(hs1)]
    calc[, p_tp_upper:=1-n_jex/n_hs_tail]
    calc[, p_tp_lower:=n_jex/n_hs_tail]
    calc[, tp_upper:=sample_data[hs>=hs1, quantile(tp, p_tp_upper)], .(hs1)]
    calc[, tp_lower:=sample_data[hs>=hs1, quantile(tp, p_tp_lower)], .(hs1)]
    out = rbind(calc[.N:2, .(hs=hs1, tp=tp_upper)], calc[, .(hs=hs1, tp=tp_lower)])
  }else{
    # browser()
    calc = data.table(hs1 = seq(ap_hs, min(sample_data$hs), length.out = n_point))
    calc[, tp:=sample_data[hs>=hs1, sort(tp, decreasing = T)[n_jex]], .(hs1)]
    out = calc[, .(hs=hs1, tp)]
  }
  return(out)
}

.estimate_gje_from_wln = function(wln, output_rp, type, n_point){

  res_list=list()
  for(this_rp in output_rp){
    this_sample_data = .sample_wln_is(wln, target_rp=this_rp)
    this_out = .estimate_gje_from_data(
      sample_data = this_sample_data, n_jex = .target_rp_ub,
      ap_hs = this_sample_data[, sort(hs, decreasing = T)[type*.target_rp_ub]],
      type = type, n_point = n_point)
    res_list[[as.character(this_rp)]] = cbind(rp=this_rp, this_out)
  }
  res = rbindlist(res_list)
  return(res)
}

.estimate_gje_from_ht = function(ht, output_rp, type, n_point){

  res_list=list()
  for(this_rp in output_rp){
    this_sample_data = .sample_ht_is(ht, target_rp=this_rp)
    this_out = .estimate_gje_from_data(
      sample_data = this_sample_data, n_jex = .target_rp_ub,
      ap_hs = this_sample_data[, sort(hs, decreasing = T)[type*.target_rp_ub]],
      type = type, n_point = n_point)
    res_list[[as.character(this_rp)]] = cbind(rp=this_rp, this_out)
  }
  res = rbindlist(res_list)
}


### R과 함께 배우는 네트워크 분석 ###

## 01. 핵심 패키지 설치

# 패키지 설치
install.packages("igraph")
install.packages("network")
install.packages("sna")
install.packages("visNetwork")
install.packages("threejs")
install.packages("ndtv")

install.packages("RColorBrewer")
install.packages("png")
install.packages("networkD3")
install.packages("animation")
install.packages("maps")
install.packages("geosphere")

library(RColorBrewer)

### 02. 색 알아보기
plot(x=1:10, y=rep(5,10), pch=19, cex=5, col="dark red")
points(x=1:10, y=rep(6, 10), pch=19, cex=5, col="#557799")
points(x=1:10, y=rep(4, 10), pch=19, cex=5, col=rgb(.25, .5, .3))

## 전체 색 - 657가지의 colors
length(colors())

## 'green'의 이름을 가진 것 알아보기
grep('green', colors(), value=T)

## rgb 컬러는 기본적으로 0-1사이의 값이다.
## 0-255 사이의 값으로 보고 싶다면 다음과 같이 할 수 있다.
rgb(10, 100, 100, maxColorValue = 255)
col1 = rgb(10, 100, 100, maxColorValue = 255)
plot(x=1:10, y=rep(5,10), pch=19, cex=5, col=col1)

### 투명도 - Transparency /ˌtranˈsperənsē/ -
### 우리는 alpha를 이용하여 투명도 설정이 가능하다.
plot(x=1:5, y=rep(5,5), pch=19, cex=10,
     col=rgb(.25, .5, .3, alpha=0.5), xlim=c(0,6))

### - Palettes -
### 때로는 대조되는 여러가지 색상이 필요하다.
### R은 우리를 위해 함수를 만들어 놓았다.

pal1 <- heat.colors(5, alpha=1)   # 반투명
pal2 <- rainbow(5, alpha=.5)      # 투명

plot(x=1:10, y=1:10, pch=19, cex=10, col=pal1)
par(new=TRUE)  # 첫번째 plot를 지우지 않고, 두번째 플롯을 더한다.
plot(x=10:1, y=1:10, pch=19, cex=10, col=pal2)

### - colorRampPalette() -
### 그래디언트를 발생시키기 위해 우리는 위의 함수를 사용한다.
### 이 함수를 이용하여 우리가 필요로 하는 수의 색을 발생시킬 수 있다.
palf <- colorRampPalette(c("gray70", "dark red"))
is(palf)
palf(10)
plot(x=10:1, y=1:10, pch=19, cex=10, col=palf(10))  # 10가지 색

### - colorRampPalette() -
### 우리는 여기에 투명도를 alpha를 이용하여 추가 가능하다.
palf <- colorRampPalette(c(rgb(1,1,1, 0.2), rgb(0.8, 0, 0, 0.7)), alpha=TRUE)
plot(x=10:1, y=1:10, pch=19, cex=10, col=palf(10))  # 10가지 색

### - ~~ ColorBrewer -----
### 우리는 준비되어 있는 색상 R palettes 가 있다.
### 이 함수는 중요한 함수 하나 있다. 'brewer.pal'
### 우리는 이를 이용하여 색의 확인이 가능하다.
### display.brewer.pal(8, "Set3")
library(RColorBrewer)

display.brewer.all()
brewer.pal.info  # 색상 정보 확인
brewer.pal.info["Blues", ]

display.brewer.pal(8, "Set3")     # Set3의 색상 보기
display.brewer.pal(8, "Spectral")
display.brewer.pal(8, "Blues")

### 03. RColorBrewer를 이용한 plot 그리기

par(mfrow=c(1,2)) # plot two figures - 1 row, 2 columns
pal3 <- brewer.pal(10, "Set3")   # Set3의 10가지 색 지정.
plot(x=10:1, y=10:1, pch=19, cex=6, col=pal3)  # 원 크기 6 지정.
plot(x=10:1, y=10:1, pch=19, cex=6, col=rev(pal3)) # backwards-색 거꾸로

dev.off() # 지정된 그래픽 구성을 없애기 위해서는 graphic device를 끈다.
detach("package:RColorBrewer")   # search() 경로로부터 이용가능한 R개체 없애기

?detach

### 04. 네트워크 데이터를 igraph로 읽기
### 데이터 다운로드 (http://bit.ly/polnet2018)
rm(list=ls())

### 작업 디렉터리 지정
setwd("D:/polnet2018")
getwd()

library(igraph)

# 데이터 읽기
nodes <- read.csv("./Data files/Dataset1-Media-Example-NODES.csv", header=T, as.is=T)
links <- read.csv("./Data files/Dataset1-Media-Example-EDGES.csv", header=T, as.is=T)

## 데이터 확인
head(nodes); head(links)
dim(nodes); dim(links)

## -- graph_from_data_frame(d=links, vertices=nodes, directed=T)
## 데이터를 igraph 객체로 변환
## graph_from_data_frame() 함수는 d와 vertices 컬럼을 갖는다.
## 소스와 대상은 node IDs를 포함한다.
## 'vertices'는 Node IDs 컬럼으로 시작해야 한다.
##

net <- graph_from_data_frame(d=links, vertices=nodes, directed=T)

class(net)
net

?E  # 그래프의 Edges
?V  # 그래프의 Vertices
E(net)
V(net)
E(net)$type    # hyperlink/mention
V(net)$media   # 미디어 회사명

E(net)[type=="mention"]
V(net)[media=="BBC"]

as_edgelist(net, names=T)   # edge의 리스트를 확인
as_adjacency_matrix(net, attr="weight")

# Or data frames describing nodes and edges:
as_data_frame(net, what="edges")
as_data_frame(net, what="vertices")
?as_data_frame

# 그래프를 그리기 위한 첫번째 시도
plot(net) # not pretty!

# Removing loops from the graph:
net <- simplify(net, remove.multiple = F, remove.loops = T)

# 화살표 크기를 줄이고, Lables 를 제거하는 것
plot(net, edge.arrow.size=.4,vertex.label=NA)

### 05. 2번째 데이터 셋 : matrix
### 데이터 읽기
nodes2 <- read.csv("./Data files/Dataset2-Media-User-Example-NODES.csv", header=T, as.is=T)
links2 <- read.csv("./Data files/Dataset2-Media-User-Example-EDGES.csv", header=T, row.names=1)

head(links2, 100)

net2 <- graph_from_incidence_matrix(links2)

table(V(net2)$type)

plot(net2,vertex.label=NA)

class(net2)
net2

### 06. igraph를 이용한 분석 및 시각화
plot(net, edge.arrow.size=.4)

### 03. RColorBrewer를 이용한 plot 그리기
### 04. 네트워크 데이터를 igraph로 읽기
### 05. 2번째 데이터 셋 : matrix
### 06. igraph를 이용한 분석 및 시각화
plot(net, edge.arrow.size=.4, edge.curved=.3)  # 곡선 커브 및 방향
plot(net, edge.arrow.size=.2, edge.curved=0,
     vertex.color="orange", vertex.frame.color="#555555",
     vertex.label=V(net)$media, vertex.label.color="black",
     vertex.label.cex=.7)

# Generate colors based on media type:
colrs <- c("gray50", "tomato", "gold")
V(net)$color <- colrs[V(net)$media.type]

# Compute node degrees (#links) and use that to set node size:
deg <- degree(net, mode="all")
V(net)$size <- deg*3
# Alternatively, we can set node size based on audience size:
V(net)$size <- V(net)$audience.size*0.7

# The labels are currently node IDs.
# Setting them to NA will render no labels:
V(net)$label.color <- "black"
V(net)$label <- NA

# Set edge width based on weight:
E(net)$width <- E(net)$weight/6

#change arrow size and edge color:
E(net)$arrow.size <- .2
E(net)$edge.color <- "gray80"

# We can even set the network layout:
graph_attr(net, "layout") <- layout_with_lgl
plot(net)

# 그래프의 edge=color을 구하기
plot(net, edge.color="orange", vertex.color="gray50")
plot(net)
legend(x=-1.1, y=-1.1, c("Newspaper","Television", "Online News"), pch=21,
       col="#777777", pt.bg=colrs, pt.cex=2.5, bty="n", ncol=1)

# Sometimes, especially with semantic networks, we may be interested in
# plotting only the labels of the nodes:

plot(net, vertex.shape="none", vertex.label=V(net)$media,
     vertex.label.font=2, vertex.label.color="gray40",
     vertex.label.cex=.7, edge.color="gray85")

# Let's color the edges of the graph based on their source node color.
# We'll get the starting node for each edge with "ends()".
edge.start <- ends(net, es=E(net), names=F)[,1]
edge.col <- V(net)$color[edge.start]

plot(net, edge.color=edge.col, edge.curved=.1)

# Let's generate a slightly larger 100-node graph using
# a preferential attachment model (Barabasi-Albert).

net.bg <- sample_pa(100, 1.2)
V(net.bg)$size <- 8
V(net.bg)$frame.color <- "white"
V(net.bg)$color <- "orange"
V(net.bg)$label <- ""
E(net.bg)$arrow.mode <- 0
plot(net.bg)

## Ref
## https://bookdown.org/rdpeng/exdata/plotting-and-color-in-r.html
## Exploratory Data Analysis with R
## RColorBrewer : http://ftp.auckland.ac.nz/software/CRAN/doc/packages/RColorBrewer.pdf


## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Functions for the Soay sheep coat colour IBM
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

library(arm) ## invlogit

## Population statistics from annual sheep data
sdNt <- 127.1302
midNt <- 431.7917
scaleN <- function(N) return((N-midNt)/sdNt)

## Mean age and capture weight of mothers of age 0 individuals (from
## Soay demographic data)
mean.ageMum <- 4.06147
mean.capWgtMum <- 3.082146

initState <- function (m.par, init.pop.size = 500, init.G = NULL, maxA = 15) {
    ## Initialise individual state array for the population.

    ## Follows strategy in supplementary material to Rees et al. 2014,
    ## which is to initialise the population with a random selection
    ## of new recruits from a normal distribution based on mean parent
    ## size.

    ## If an initial genotype is specified then the new individuals
    ## are assigned to that genotype, with an equal number of
    ## offspring of each sex. Otherwise, offspring are equally
    ## distributed between sexes and genotypes.

    ## Args:
    ##   m.par: Demographic model parameters.
    ##   init.pop.size: Initial size of the population.
    ##   init.G: Initial genotype of population.
    ##   maxA: Maximum age.

    ## Returns:
    ##   Individual state array containing the size of each individual
    ##   in the population, structured by age, sex and coat colour
    ##   genotype.

    ## Population structure - age, sex, genotype
    Aset <- as.character(seq.int(0, maxA))
    Gset <- c("GG","GT","TT")
    Sset <- c("F","M")

    ## Initialise state array
    z <- mk.flist(list(S=Sset, A=Aset, G=Gset))

    ## Assume equal numbers of offspring of each sex, distributed
    ## equally amongst genotypes, unless an initial genotype is
    ## specified.
    n <- ifelse(is.null(init.G), init.pop.size/6, init.pop.size/2)

    ## Get proportion of twins in the new population based on twinning
    ## rate for an average adult female
    p.twin <- invlogit(m.par["t.a1.F.(Intercept)"] + m.par["t.a1.F.capWgt"]*mean.capWgtMum
                       + m.par["t.a1.F.poly(ageY, 2, raw = TRUE)1"]*mean.ageMum
                       + m.par["t.a1.F.poly(ageY, 2, raw = TRUE)2"]*mean.ageMum^2)
    ## Proportion of twins is number of twin lambs over total number of lambs
    ## From twinning rate, 0.5*num.twin = p.twin(0.5*num.twin + num.sing))
    ## Substitute num.sing = num.tot - num.twin and rearrange to get num.twin/num.tot:
    prop.twin <- 2*p.twin/(1+p.twin)

    ## Get the sizes of the new individuals in each class, based on
    ## mean maternal state
    for (G in Gset) {
        if (!is.null(init.G) && init.G != G) next
        for (S in Sset) {
            mu <- m.par["sz.off.(Intercept)"] +
                m.par["sz.off.capWgtMum"]*mean.capWgtMum +
                    m.par["sz.off.ageMum"]*mean.ageMum +
                        m.par["sz.off.sexM"]*as.numeric(S=="M")

            z[[S, "0", G]] <- c(rnorm(n*prop.twin
                                      , mean = mu + m.par["sz.off.isTwnMat"]
                                      , sd = m.par["sz.off.sigma"]),
                                rnorm(n*(1-prop.twin)
                                      , mean = mu
                                      , sd = m.par["sz.off.sigma"]))

        }
    }
    return(z)
}

doSim <- function (model.params, init.pop.size = 500, sim.length = 200, init.G = NULL, init.state = NULL, maxA = 15) {
    ## Run simulation for the Soay coat colour genotype IBM.

    ## This could be generalised to require that client passes in the
    ## functions required to set up the state array for the system and
    ## to iterate the array between time steps.

    ## Args:
    ##   model.params: Demographic model parameters.
    ##   init.pop.size: Initial size of the population.
    ##   sim.length: Maximum number of time-steps in simulation.
    ##   init.G: Initial genotype.
    ##   init.state: Initial state array. If not specified then an
    ##      initial population state array is generated.
    ##   maxA: Maximum age of individuals.

    ## Returns:
    ##   Time series data consisting of population state array for each time step.

    ## Check the dimensions of model.params to determine whether
    ## to run simulation as averaged or variable environment.
    tEnv <- numeric(0)
    mPar <- model.params
    if (!is.null(dim(mPar))) {
        ## Sample environment parameters from the set available
        tEnv <- sample(1:(dim(mPar)[2]), sim.length+1, replace=TRUE)
        mPar <- model.params[,tEnv[1]]
    }

    ## Get initial population state
    if (is.null(init.state)) {
        z <- initState (mPar, init.pop.size, init.G, maxA)
    } else {
        z <- init.state
    }

    ## Population structure - age, sex, genotype
    Aset <- dimnames(z)$A
    Gset <- dimnames(z)$G
    Sset <- dimnames(z)$S

    numAset <- as.numeric(Aset)
    names(numAset) <- Aset
    numGset <- seq.int(-1,1)
    names(numGset) <- Gset

    ## Storage for simulated population at each timestep
    zt <- list(sim.length)

    for (i in seq_len(sim.length)) {
        if(length(tEnv)>0) {
            ## Select model parameters for this step
            mPar <- model.params[,tEnv[i+1]]
        }

        ## Get the current population density (standardized according
        ## to the mean and standard deviation observed in the St Kilda
        ## population)
        Nt <- scaleN(length(unlist(z)))

        ## Calculate male paternity probabilities for each genotype
        pGm <- calcMalePaternity(z, Nt, mPar)

        ## Apply life history events
        z1 <- mk.flist(list(S=Sset, A=Aset, G=Gset))
        z1.rec <- mk.flist(list(S=Sset, G=Gset))
        for (G in Gset) {
            numG <- switch(G, GG=-1, GT=0, TT=+1)
            for (A in Aset) {
                numA <- numAset[A]
                for (S in Sset) {
                    z.sub <- z[[S, A, G]]
                    if (!is.null(z.sub) & length(z.sub) > 0) {

                        ## survival
                        pS <- p.surv(z.sub, numG, S, numA, Nt, mPar)
                        surv <- rbinom(n=length(z.sub), prob=pS, size=1)
                        z.sub <- z.sub[which(surv == 1)]

                        if (length(z.sub) == 0) next

                        ## growth
                        if (numA < maxA) {
                            z1[[S, as.character(numA+1), G]] <- r.grow(z.sub, numG, S,
                                                                       numA, Nt, mPar)
                        }

                        ## reproduction
                        if (S == "F" && !is.na(pGm)) {
                            pB <- p.repr(z.sub, numG, numA, Nt, mPar)
                            repr <- rbinom(n=length(z.sub), prob=pB, size=1)
                            z.repr <- z.sub[which(repr == 1)]

                            ## Twin or not?
                            pT <- p.twin(z.repr, numG, numA, Nt, mPar)
                            twin <- rbinom(n=length(z.repr), prob=pT, size=1)

                            for (t.off in seq.int(0,1)) {

                                z_ <- z.repr[which(twin == t.off)]
                                n.off <- ifelse(t.off == 1, 2*length(z_), length(z_))

                                ## Offspring sex
                                o.sex <- rbinom(n=n.off, prob=0.5, size=1)
                                o.sex <- paste(c("F","M")[match(o.sex, c(0,1))])

                                ## Offspring genotype
                                ## pGm is paternity probability for male genotypes
                                pOffgen <- p.offgenotype(Gset, G, pGm)
                                ## Assign (numeric) genotypes to
                                ## offspring; not taking account of
                                ## paternity of twins for now. (Pemberton
                                ## et al. 1999 recorded 26% of twins with
                                ## known paternity having same sire.)
                                o.gen <- rmultinom(n=n.off, prob=pOffgen, size=1)
                                o.gen <- t(o.gen)%*%numGset

                                ## Offspring survival
                                ## Need to do for each offspring sex (M/F) and twin (0/1) status
                                for (s.off in Sset) {
                                    ## Index into maternal set for this sex
                                    ## This should sort out the parents appropriately for twin offspring
                                    i.subset <- which(o.sex == s.off)
                                    i.par <- ifelse(t.off == 1, ceiling(i.subset/2), i.subset)
                                    pRec <- p.offsurv(z_[i.par], A=numA, Nt=Nt, mPar=mPar,
                                                      S.off=s.off, T.off=t.off)
                                    recr <- rep(NA, n.off)
                                    recr[i.subset] <- rbinom(n=length(i.subset), prob=pRec, size=1)

                                    ## Recruit size
                                    if (sum(recr,na.rm=TRUE)>0) {
                                        z1.rec <- rep(NA, n.off)
                                        i.recr <- which(recr == 1)
                                        i.par <- ifelse(t.off == 1, ceiling(i.recr/2), i.recr)
                                        z1.rec[i.recr] <- r.offsize(z_[i.par], numG, numA, Nt,
                                                                    mPar, S.o=s.off, T.off=t.off)
                                        ## Store result for each genotype from the survivors
                                        for (numG.off in unique(o.gen[which(recr==1)])) {
                                            G.off <- Gset[numG.off+2]
                                            z1[[s.off, "0", G.off]] <- c(z1[[s.off, "0", G.off]],
                                                                         z1.rec[which(recr == 1 &
                                                                                o.gen == numG.off)])
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        ## Store population for this timestep
        zt[[i]] <- z1
        if(length(unlist(z1)) == 0) {
            break
        }
        z <- z1
    }
    return(zt)
}

calcMalePaternity <- function(z, Nt, model.params) {
    ## Calculate male paternity probabilities for each genotype.

    ## Calculates male paternity probabilities for each genotype based
    ## on the expected number of offspring for males of each genotype
    ## in the population.

    ## Args:
    ##   z: Population state vector
    ##   Nt: Population density
    ##   model.params: Vital rate model parameters

    ## Returns:
    ##   Vector indicating the probability of paternity for males of
    ##   each genotype, or NA if the expected number of offspring for
    ##   all genotypes is zero.

    Gset <- dimnames(z)$G
    Aset <- dimnames(z)$A
    num.off <- mk.flist(list(G=Gset))
    for (G in Gset) {
        num.off[[G]] <- 0
        numG <- switch(G, GG=-1, GT=0, TT=+1)
        for (A in Aset) {
            z.sub <- z[["M", A, G]]
            if (!is.null(z.sub) & length(z.sub) > 0) {
                numA <- as.numeric(A)
                ## n.mrepro gives mean num offspring for each individual
                lambda <- n.mrepro(z.sub, numG, numA, Nt, model.params)
                ## rpois gets random pick from Poisson distribution with the given mean
                num.off[[G]] <- sum(sapply(lambda, function(x) rpois(1,x))) + num.off[[G]]
            }
        }
    }
    ## obtain mating probs
    nTot <- sum(unlist(num.off))
    if (nTot > 0) {
        pGm <- sapply(num.off, function(x) x/nTot)
    } else {
        pGm <- NA
    }
    return(pGm)
}

summariseSimRun <- function (simRun) {
    simLen <- length(simRun)
    ntT <- ntF <- ntM <- ntGG <- ntGT <- ntTT <- numeric(simLen)
    ## compute quantities
    for (tt in 1:simLen) {
        nt1 <- simRun[[tt]]
        ntT [tt] <- length(unlist(nt1[   ,,    ]))
        ntF [tt] <- length(unlist(nt1["F",,    ]))
        ntM [tt] <- length(unlist(nt1["M",,    ]))
        ntGG[tt] <- length(unlist(nt1[   ,,"GG"]))
        ntGT[tt] <- length(unlist(nt1[   ,,"GT"]))
        ntTT[tt] <- length(unlist(nt1[   ,,"TT"]))
    }
    return( list(ntT=ntT, ntF=ntF, ntM=ntM, ntGG=ntGG, ntGT=ntGT, ntTT=ntTT) )
}

## Adapted from IPM code
r.grow.list <- mk.flist(list(S=c("F","M"), A=c(0,+1)))

r.grow.list[["F","0"]] <- function(x, Nt, mPar) {
    mu <- mPar["g.a0.F.(Intercept)"]+mPar["g.a0.F.capWgt"]*x+mPar["g.a0.F.Nt"]*Nt
    sg <- mPar["g.a0.F.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.grow.list[["F","1"]] <- function(x, G, A, Nt, mPar) {
    mu <- mPar["g.a1.F.(Intercept)"] +
        mPar["g.a1.F.capWgt"]*x +
            mPar["g.a1.F.poly(ageY, 2, raw = TRUE)1"]*A +
                mPar["g.a1.F.Nt"]*Nt +
                    mPar["g.a1.F.ageY:Nt"]*Nt*A +
                        mPar["g.a1.F.poly(ageY, 2, raw = TRUE)2"]*A^2
    sg <- mPar["g.a1.F.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.grow.list[["M","0"]] <- function(x, Nt, mPar) {
    mu <- mPar["g.a0.M.(Intercept)"]+
        mPar["g.a0.M.capWgt"]*x+
            mPar["g.a0.M.Nt"]*Nt+
                mPar["g.a0.M.obsY"]*mPar["obsY"]
    sg <- mPar["g.a0.M.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.grow.list[["M","1"]] <- function(x, G, A, Nt, mPar) {
    mu <- mPar["g.a1.M.(Intercept)"] +
        mPar["g.a1.M.capWgt"]*x
    sg <- mPar["g.a1.M.sigma"]
    return(rnorm(length(x),mu,sg))
}

r.grow <- function(x, G, S, A, Nt, mPar)
{
    ## expect S and A to have length=1
    if (length(S) != 1 | length(A) != 1)
        stop("growth function not vectorised for S(ex) and (A)ge")
    ## assign the required function
    if       (S=="F") {
        if        (A ==  0) {
            f <- r.grow.list[["F", "0"]]
        } else if (A >=  1) {
            f <- r.grow.list[["F", "1"]]
        } else stop("invalid (A)ge")
    } else if (S=="M") {
        if        (A ==  0) {
            f <- r.grow.list[["M", "0"]]
        } else if (A >=  1) {
            f <- r.grow.list[["M", "1"]]
        } else stop("invalid (A)ge")
    } else stop("invalid (S)ex")
    ## get the required arguments as a list of atomic 'names'
    fargs <- names(formals(f))
    fargs <- sapply(fargs, as.name)
    ## call the function and return
    return(do.call("f", fargs))
}

## Adapted from IPM code
r.offsize.list <- mk.flist(list(S.off=c("F","M"), T.off=c("0","1")))

r.offsize.list[["F","0"]] <- function(x, A, Nt, mPar) {
    mu <- mPar["sz.off.(Intercept)"]+mPar["sz.off.capWgtMum"]*x+
          mPar["sz.off.ageMum"]*A+
          mPar["sz.off.Ntm1"]*Nt+
          mPar["sz.off.obsY"]*mPar["obsY"]+
          mPar["sz.off.ageMum:obsY"]*A*mPar["obsY"]
    sg <- mPar["sz.off.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.offsize.list[["M","0"]] <- function(x, A, Nt, mPar) {
    mu <- mPar["sz.off.(Intercept)"]+mPar["sz.off.capWgtMum"]*x+
          mPar["sz.off.ageMum"]*A+
          mPar["sz.off.Ntm1"]*Nt+mPar["sz.off.sexM"]+
          mPar["sz.off.obsY"]*mPar["obsY"]+
          mPar["sz.off.ageMum:obsY"]*A*mPar["obsY"]
    sg <- mPar["sz.off.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.offsize.list[["F","1"]] <- function(x, A, Nt, mPar) {
    mu <- mPar["sz.off.(Intercept)"]+mPar["sz.off.capWgtMum"]*x+
          mPar["sz.off.ageMum"]*A+
          mPar["sz.off.Ntm1"]*Nt+mPar["sz.off.isTwnMat"]+
          mPar["sz.off.obsY"]*mPar["obsY"]+
          mPar["sz.off.ageMum:obsY"]*A*mPar["obsY"]
    sg <- mPar["sz.off.sigma"]
    return(rnorm(length(x),mu,sg))
}
r.offsize.list[["M","1"]] <- function(x, A, Nt, mPar) {
    mu <- mPar["sz.off.(Intercept)"]+mPar["sz.off.capWgtMum"]*x+
          mPar["sz.off.ageMum"]*A+
          mPar["sz.off.Ntm1"]*Nt+mPar["sz.off.sexM"]+mPar["sz.off.isTwnMat"]+
          mPar["sz.off.obsY"]*mPar["obsY"]+
          mPar["sz.off.ageMum:obsY"]*A*mPar["obsY"]
    sg <- mPar["sz.off.sigma"]
    return(rnorm(length(x),mu,sg))
}

r.offsize <- function(x, G, A, Nt, mPar, S.off, T.off)
{
    ## expect S and A to have length=1
    if (length(S.off) != 1 | length(T.off) != 1)
        stop("offspring size function not vectorised for offspring S(ex), female (A)ge and (T)win status")
    ## assign the required function
    if (all(A >= 0)) {
        if        (S.off == "F") {
            if        (T.off == 0) {
                f <- r.offsize.list[["F","0"]]
            } else if (T.off == 1) {
                f <- r.offsize.list[["F","1"]]
            } else stop("invalid offspring (T)win status")
        } else if (S.off == "M") {
            if        (T.off == 0) {
                f <- r.offsize.list[["M","0"]]
            } else if (T.off == 1) {
                f <- r.offsize.list[["M","1"]]
            } else stop("invalid offspring (T)win status")
        } else stop("invalid offspring (S)ex")
    } else stop("invalid (A)ge")
    ## get the required arguments as a list of atomic 'names'
    fargs <- names(formals(f))
    fargs <- sapply(fargs, as.name)
    ## call the function and return
    return(do.call("f", fargs))
}