---
title: "Lab 9m: Defensive Programming"
author: "Statistical Computing, 36-350"
date: "Monday October 24, 2016"
---

Name:  
Andrew ID:  
Collaborated with:  

This lab is to be completed in class. You can collaborate with your classmates, but you must identify their names above, and you must submit **your own** lab as an Rmd file on Blackboard, by 11:59pm on the day of the lab.

There are Homework 8 questions dispersed throughout. These must be written up in a separate Rmd document, together with all Homework 8 questions from other labs. Your homework writeup must start as this one: by listing your name, Andrew ID, and who you collaborated with. You must submit **your own** homework as a knit HTML file on Blackboard, by 11:59pm on **Tuesday November 1**. This document contains 15 of the 45 total points for Homework 8.

Some light defense
===

- The function `log()` will throw an error when the input is not a numeric variable. It will return NaN when the input is negative. See the example below, and note what happens when an error is encountered in the `for()` loop: the execution halts and the loop stops. Write a function `robust.log()`, that takes a single input `x`, and first checks that `x` is numeric. If this is not the case, then the function should throw a warning (with an informative error message, about requiring numeric inputs) and return NA; otherwise, the function should compute the logarithm of the input as usual. After you have written this, replace `log()` below in the `for()` loop example with `robust.log()`. Now what do you notice about the behavior when an input is encountered that is not numeric?

```{r, error=TRUE}
input.list = list(0, 1, 2, -1, "Jessica", exp(1), exp(pi))
for (i in 1:length(input.list)) {
  cat(paste("We are at iteration", i, "with x =", input.list[[i]],
            "and log(x) =", log(input.list[[i]]), "\n")) 
}
```

- Recall the `fibonacci()` function from Lab 8w, which is pasted below for your convenience. (This is the correct version, i.e., the debugged version, of the function.) The function computes the $n$th number in the Fibonacci sequence 1, 1, 2, 3, 5, 8, 13, 21, 34, ..., which begins with 1, 1, and where every number after this is the sum of the previous two. But for negative numbers, or non-integer positive numbers, we get errors, as demonstrated in the examples below. Using an appropriate `if()` statement and the `warning()` function, implement the following behavior: if is the input is negative, or positive but not an integer, then the function throws a warning (with an informative warning message) and returns `NA`. Check that you get the appropriate behavior on the examples below.

```{r, error=TRUE}
fibonacci = function(n) {
  if (n==1 || n==2) return(1)
  my.fib = c(1,1)
  for (i in 3:n) my.fib[i] = my.fib[i-1] + my.fib[i-2]  
  return(my.fib[n])
}

fibonacci(20)
fibonacci(-1)
fibonacci(1.5)
```

- For (say) strings, matrices, or lists as inputs, we still get errors (or unexpected behavior) with `fibonacci()`, as demonstrated below. Further modify this function so that it throws a warning (with an informative message) and returns NA whenever the input is not a single numeric variable, i.e., a numeric variable of length 1. Check that you get the appropriate behavior on the examples below.

```{r, error=TRUE}
fibonacci("hi")
fibonacci(matrix(1:4, 2, 2))
fibonacci(list(x=0, y=1))
```

**Hw8 Q1 (6 points).** Fill out the function skeleton `quadratic.solver()` below that takes inputs `a`, `b`, `c`, all numeric variables, representing the coefficients of a quadratic $q(x)=ax^2 + bx + c$, and returns a numeric vector of length 2, which contains the roots of the quadratic, i.e., the solutions to $q(x)=0$. Use error checking at the start to make sure that `a`, `b`, `c` are all really single numeric variables---if not, then your function should throw a warning (with an informative message), and return NA. Demonstrate this behavior on the test inputs below. Also, when one or two of the roots are complex numbers, your function should throw a warning (with an informative message). (Hint: you'll want to remind yourself of the well-known formula for the roots of a quadratic; there's even a song, to the tune of "Jing Bells"!) Check that the outputs of your function match those described in comments, for the test inputs below.

```{r}
quadratic.solver = function(a,b,c) {
  return(0)
}

quadratic.solver("catch","this","bug") # Should throw a warning, and return NA
quadratic.solver(1:5,6:10,1:15) # Should throw a warning, and return NA
quadratic.solver(1,0,-1) # Should return c(1, -1)
quadratic.solver(1,0,1) # Should return c(0+1i, 0-1i), with a warning
```

Trying and catching
===

- Recall the functions `get.wordtab()`, `get.wordtabs()` from Lab 6m (and earlier labs and mini-lectures), pasted below for your convenience. The function `get.wordtab()` uses `readLines()` to read text from the website specified by the first input `str.url`. The function `get.wordtabs()` uses a `for()` loop and calls `get.wordtab()` for each element in the first input `str.urls`. The function `get.wordtab()` will crash when the specified website doesn't exist. Note that this also causes problems for `get.wordtabs()`, which will quit its `for()` loop prematurely when `get.wordtab()` encounters an error. See the examples below. Modify the `get.wordtab()` function so that it uses a `tryCatch()` statement around `readLines()`. If an error is encountered, then this function should catch it, print a message to the screen indicating that an error has occurred, print the error message, and return NULL. Check that you get the appropriate behavior on the examples below. In particular, is `get.wordtabs()` able to retrieve the rest of the word tables successfully, even after a failed attempt, when particular a website doesn't exist?

```{r, error=TRUE}
get.wordtab = function(str.url, split="[[:space:]]|[[:punct:]]",
                       tolower=TRUE, keep.numbers=FALSE) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split=split)[[1]]
  words = words[words != ""]
    
  # Convert to lower case, if we're asked to
  if (tolower) words = tolower(words)
  
  # Get rid of words with numbers, if we're asked to
  if (!keep.numbers)
    words = grep("[0-9]", words, inv=TRUE, val=TRUE)
  
  table(words)
}

get.wordtabs = function(str.urls, split="[[:space:]]|[[:punct:]]",
                       tolower=TRUE, keep.numbers=FALSE) {
  wordtabs = list()
  for (i in 1:length(str.urls)) {
    cat(paste("* Website:",str.urls[i],"\n"))
    wordtabs[[i]] = get.wordtab(str.urls[i], split, tolower, keep.numbers)
  }
  return(wordtabs)
}

str.urls = c("http://stat.cmu.edu/~ryantibs/research.html",
             "www.this.is.not.a.real.website.com",
             "http://stat.cmu.edu/~ryantibs/teaching.html")
wordtab = get.wordtab(str.urls[2]) 
wordtabs = get.wordtabs(str.urls)
```

**Hw8 Q2 (9 points).** The function `evil.rbinom()` below is an evil binomial sampler written by your professor. Think about it as operating in three modes: "warning" mode, "error" mode, and "normal" mode. Each time `evil.rbinom()` is called, it enters warning mode with probability $p_{\rm warning}$, error mode with probability $p_{\rm error}$, and normal mode otherwise. In warning mode, the function throws a warning, and returns an arbitrary number between 1 and 100. In error mode, the function throws an error. In normal mode, it samples a binomial random variable, with size 100, and success probability $p_{\rm heads}$ (i.e., this is the number of coin tosses that came up heads out of 100 i.i.d. coin tosses, each with probability $p_{\rm heads}$ of coming up heads.) 

Your task is to estimate $p_{\rm warning}$ (the probability of getting a warning), $p_{\rm error}$ (the probability of getting an error), and $p_{\rm heads}$ (the probability of single coin toss being heads, in normal mode), by calling `evil.rbinom()` as many times as you wish. After we have loaded it with `source()` below, this function is included in your R workspace, so you may just call `evil.rbinom()` directly. (Hint: you'll want to use a `for()` loop to repeatedly call `evil.rbinom()` many times. You'll also want to use a `tryCatch()` statement to catch both errors *and* warnings, that could appear each time you call `evil.rbinom()`. For example, code of the form:
```
tryCatch({
  # EXPRESSION GOES HERE
  }, warning = function(w) { 
    return(-1) 
  }, error = function (e) {
    return(-2)  
  })
```
will run some expression and then either call the warning function or the error function, depending on what gets encountered. When a warning is encountered, the above code block will evaluate to -1; when an error is encountered, the above will evaluate to -2; otherwise it evaluates to the value of the expression.) Your code must support your estimated probabilities, i.e., it must show clearly how you computed them. Simply stating numbers will not get you points.

```{r}
source("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/labs/evil.rbinom.R")
```