# R code accompanying lecture 5 for 36-350, Fall 2012
  # See lecture for context


  # Example of predicting from a model

# Predict response values from a power-law scaling model
# Presumes: power-law model objects are lists containing a scaling factor
  # called "y0" and a scaling exponent called "a"
# Inputs: fitted power-law model (object), vector of values at which to make
  # predictions at (newdata)
  # Argument names follow conventions for other predict() functions
# Outputs: vector of predicted response values
predict.plm <- function(object, newdata) {
  # Check that object has the right components
  stopifnot("a" %in% names(object), "y0" %in% names(object))
  a <- object$a
  y0 <- object$y0
  # Sanity check the inputs
  stopifnot(is.numeric(a),length(a)==1)
  stopifnot(is.numeric(y0),length(y0)==1)
  stopifnot(is.numeric(newdata))
  return(y0*newdata^a)  # Actual calculation and return
}

	# Examples of plotting

# Plot fitted curve from power law model over specified range
# Inputs: list containing parameters (plm), start and end of range (from, to)
# Outputs: TRUE, silently, if successful
# Side-effect: Makes the plot
  # Take sanity-checking of parameters as read
  y0 <- plm$y0 # Extract parameters
  a <- plm$a
  f <- function(x) { return(y0*x^a) }
  curve(f(x),from=from,to=to)
  # Return with no visible value on the terminal
  invisible(TRUE)
}


	# With other arguments for curve()
          # illustrates the use of "..." to pass along extra arguments

# Plot fitted curve from power law model over specified range
# Inputs: list containing parameters (plm), start and end of range (from, to),
  # other graphical arguments for curve (...)
# Outputs: TRUE, silently, if successful
# Side-effect: Makes the plot
plot.plm.2 <- function(plm,...) {
  y0 <- plm$y0
  a <- plm$a
  f <- function(x) { return(y0*x^a) }
  # from and to are possible arguments to curve()
  curve(f(x),...)
  invisible(TRUE)
}


	# Using predict.plm()

# Plot fitted curve from power law model over specified range
# Inputs: list containing parameters (plm), start and end of range (from, to),
  # number of points for plotting (n), other graphical arguments for plot (...)
# Outputs: TRUE, silently, if successful
# Side-effect: Makes the plot
plot.plm.3 <- function(plm,from,to,n=101,...) {
  x <- seq(from=from,to=to,length.out=n)
  y <- predict.plm(object=plm,newdata=x)
  plot(x,y,...)
  invisible(TRUE)
}





	# Examples of recursion

# Calculate a factorial
# Input: a number (n)
# Output: the factorial of n
# Presumes: n is a single positive integer
my.factorial <- function(n) {
  if (n == 1) {  # Base case
    return(1)
  } else {  # Recursion
    return(n*my.factorial(n-1))
  }
}

# Calculate a Fibonacci number
# Input: a number (n)
# Output: the nth Fibonacci number
# Presumes: n is a single positive integer
fib <- function(n) {
  if ( (n==1) || (n==0) ) {  # Base cases
   return(1)
  } else {  # Recursion
   return (fib(n-1) + fib(n-2))
  }
}
   # EXERCISE: Try tracing through fib(5)



	# Our resource allocation example, made more modular and recursive

# Adjust an output plan until it's under budget and close to full utilization
# Inputs: vector of initial planned output levels (output), matrix giving
  # resources needed per unit of output (factory), vector of resources
  # available (available), vector of acceptable idle resource amounts
  # (slack), factor by which to adjust plans (tweak)
# Output: List containing acceptable output levels (output) and resources
  # required (needed)
planner <- function(output,factory,available,slack,tweak=0.1) {
  needed <- plan.needs(output,factory)
  if (all(needed <= available) && all(available-needed <= slack)) {
    return(list(output=output,needed=needed))
  }
  else {
    output <- adjust.plan(output,needed,available,tweak)
    return(planner(output,factory,available,slack))
  }
}


# Calculate resources needed by a given output plan
# Inputs: vector of output levels (output), matrix giving resources needed per
  # unit of output (factory)
# Output: column matrix giving resources needed
plan.needs <- function(output,factory) { factory %*% output }


# Make multiplicative adjustments to an unsatisfactory output plan
# Inputs: vector of planned output levels (output), resources needed by plan
  # (needed), vector of resources available (available), factor by which to
  # adjust plans
# Output: vector of new output levels
# Presumes: resources needed do not exactly match resources available
adjust.plan <- function(output,needed,available,tweak) {
  # If we use too much, reduce all planned output levels by the same factor
  if (all(needed >= available)) { return(output*(1-tweak)) }
  # If we use too little, increase all planned output levels
  if (all(needed < available)) { return((1+tweak)) }
  # If we use too much of some things and too little of others, randomly
    # change all output levels
    # See help(runif)
  return(output*runif(n=length(output),min=1-tweak,max=1+tweak))
  # N.B., should never call this when needed == available
}

