├── run_tests.R ├── tests ├── testcachesolve.R └── testmakecachematrix.R ├── README.md └── cachematrix.R /run_tests.R: -------------------------------------------------------------------------------- 1 | ## RUnit Test Suite 2 | ## ---------------- 3 | 4 | ## NOTE: the `RUnit` package is required to run (or source) this script! 5 | library('RUnit') 6 | 7 | ## USAGE: To run this script is just necessary to source it! 8 | 9 | ## DESCRIPTION: 10 | ## ------------ 11 | # This script creates a TestSuite (located in the "tests" folder), 12 | # containing Unit Test functions necessary to test the expected behaviors 13 | # of the two functions in the `cachematrix.R` file. 14 | # Unit tests are named according to the following naming convention 15 | # test.R 16 | 17 | source('./cachematrix.R') 18 | 19 | test.suite <- defineTestSuite("cachematrix", 20 | dirs = file.path("tests"), 21 | testFileRegexp = '^test\\w+\\.R') 22 | 23 | test.result <- runTestSuite(test.suite) 24 | 25 | printTextProtocol(test.result) 26 | -------------------------------------------------------------------------------- /tests/testcachesolve.R: -------------------------------------------------------------------------------- 1 | 2 | ## Unit Test for the function `cacheSolve` in the `cachematrix.R` file 3 | ## ------------------------------------------------------------------- 4 | 5 | test.nonInputMatrixRaisesAnError <- function() { 6 | 7 | wrong.inputtype <- list(a=1:2) 8 | checkException(cacheSolve(wrong.inputtype)) 9 | } 10 | 11 | test.inputMatrixIsCohercedAsCacheMatrixAndWorks <- function() { 12 | m <- matrix(1:4, 2, 2) 13 | inverse <- cacheSolve(m) 14 | checkIdentical(solve(m), inverse) 15 | } 16 | 17 | test.wrongInputTypeOrNAOrNullRaisesAnError <- function() { 18 | checkException(cacheSolve(list(a=1:3))) 19 | checkException(cacheSolve(NA)) 20 | checkException(cacheSolve(NULL)) 21 | } 22 | 23 | test.NonInvertibleInputRaisesAnError <- function() { 24 | m <- matrix(1:6, 3, 2) 25 | checkException(cacheSolve(m)) 26 | } 27 | 28 | test.assignmentTestValues <- function() { 29 | m <- matrix(1:4,2,2) 30 | inverse <- cacheSolve(m) 31 | expected <- matrix(c(-2,1,1.5,-0.5),2,2) 32 | checkIdentical(inverse, expected) 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/testmakecachematrix.R: -------------------------------------------------------------------------------- 1 | 2 | ## Unit Test for the function `makeCacheMatrix` in the `cachematrix.R` file 3 | ## ------------------------------------------------------------------------- 4 | 5 | test.correctInput <- function() { 6 | m <- matrix(1:4,2,2) 7 | cacheMatrix <- makeCacheMatrix(m) 8 | # Check that returned data is actually a list 9 | checkTrue(is.list(cacheMatrix)) 10 | 11 | # Check list names 12 | expected.names <- c("setData", "getData", "setInverse", "getInverse") 13 | checkIdentical(names(cacheMatrix), expected.names) 14 | 15 | # check that `getData` actually works 16 | checkIdentical(m, cacheMatrix$getData()) 17 | 18 | # check that inverse is NULL 19 | checkTrue(is.null(cacheMatrix$getInverse())) 20 | } 21 | 22 | test.matrixNotInvertibleRaisesAnError <- function() { 23 | m <- matrix(1:6, 3, 2) 24 | checkException(makeCacheMatrix(m)) 25 | } 26 | 27 | test.NonMatrixInputRaisesAnError <- function() { 28 | m <- list(a=1:3, b=3:2) 29 | checkException(makeCacheMatrix(m)) 30 | } 31 | 32 | test.NAorNullInputRaisesAnError <- function() { 33 | checkException(makeCacheMatrix(NA)) 34 | checkException(makeCacheMatrix(NULL)) 35 | } 36 | 37 | test.setterAndGetterFunctionWorks <- function() { 38 | empty.cm <- makeCacheMatrix() 39 | # Test that the data of an empy matrix are all NAs 40 | checkTrue(all(is.na(empty.cm$getData()))) 41 | 42 | # set data and check identical 43 | m <- matrix(1:4, 2, 2) 44 | empty.cm$setData(m) 45 | checkIdentical(empty.cm$getData(), m) 46 | } 47 | 48 | test.settingAnonInvertibleMatrixLeavesDataUnchanged <- function() { 49 | invertible <- matrix(1:4, 2, 2) 50 | cm <- makeCacheMatrix(invertible) 51 | notInvertible <- matrix(1:6, 3, 2) 52 | cm$setData(notInvertible) 53 | checkIdentical(cm$getData(), invertible) 54 | } 55 | 56 | test.SettingAndGettingInverse <- function() { 57 | invertible <- matrix(1:4, 2, 2) 58 | cm <- makeCacheMatrix(invertible) 59 | 60 | # check that so far, the Inverse is null 61 | checkTrue(is.null(cm$getInverse())) 62 | 63 | # Set the Inverse 64 | data <- cm$getData() 65 | inverse <- solve(data) 66 | cm$setInverse(inverse) 67 | checkIdentical(inverse, cm$getInverse()) 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | 3 | This second programming assignment will require you to write an R 4 | function that is able to cache potentially time-consuming computations. 5 | For example, taking the mean of a numeric vector is typically a fast 6 | operation. However, for a very long vector, it may take too long to 7 | compute the mean, especially if it has to be computed repeatedly (e.g. 8 | in a loop). If the contents of a vector are not changing, it may make 9 | sense to cache the value of the mean so that when we need it again, it 10 | can be looked up in the cache rather than recomputed. In this 11 | Programming Assignment you will take advantage of the scoping rules of 12 | the R language and how they can be manipulated to preserve state inside 13 | of an R object. 14 | 15 | ### Example: Caching the Mean of a Vector 16 | 17 | In this example we introduce the `<<-` operator which can be used to 18 | assign a value to an object in an environment that is different from the 19 | current environment. Below are two functions that are used to create a 20 | special object that stores a numeric vector and caches its mean. 21 | 22 | The first function, `makeVector` creates a special "vector", which is 23 | really a list containing a function to 24 | 25 | 1. set the value of the vector 26 | 2. get the value of the vector 27 | 3. set the value of the mean 28 | 4. get the value of the mean 29 | 30 | 31 | 32 | makeVector <- function(x = numeric()) { 33 | m <- NULL 34 | set <- function(y) { 35 | x <<- y 36 | m <<- NULL 37 | } 38 | get <- function() x 39 | setmean <- function(mean) m <<- mean 40 | getmean <- function() m 41 | list(set = set, get = get, 42 | setmean = setmean, 43 | getmean = getmean) 44 | } 45 | 46 | The following function calculates the mean of the special "vector" 47 | created with the above function. However, it first checks to see if the 48 | mean has already been calculated. If so, it `get`s the mean from the 49 | cache and skips the computation. Otherwise, it calculates the mean of 50 | the data and sets the value of the mean in the cache via the `setmean` 51 | function. 52 | 53 | cachemean <- function(x, ...) { 54 | m <- x$getmean() 55 | if(!is.null(m)) { 56 | message("getting cached data") 57 | return(m) 58 | } 59 | data <- x$get() 60 | m <- mean(data, ...) 61 | x$setmean(m) 62 | m 63 | } 64 | 65 | ### Assignment: Caching the Inverse of a Matrix 66 | 67 | Matrix inversion is usually a costly computation and there may be some 68 | benefit to caching the inverse of a matrix rather than computing it 69 | repeatedly (there are also alternatives to matrix inversion that we will 70 | not discuss here). Your assignment is to write a pair of functions that 71 | cache the inverse of a matrix. 72 | 73 | Write the following functions: 74 | 75 | 1. `makeCacheMatrix`: This function creates a special "matrix" object 76 | that can cache its inverse. 77 | 2. `cacheSolve`: This function computes the inverse of the special 78 | "matrix" returned by `makeCacheMatrix` above. If the inverse has 79 | already been calculated (and the matrix has not changed), then 80 | `cacheSolve` should retrieve the inverse from the cache. 81 | 82 | Computing the inverse of a square matrix can be done with the `solve` 83 | function in R. For example, if `X` is a square invertible matrix, then 84 | `solve(X)` returns its inverse. 85 | 86 | For this assignment, assume that the matrix supplied is always 87 | invertible. 88 | 89 | In order to complete this assignment, you must do the following: 90 | 91 | 1. Fork the GitHub repository containing the stub R files at 92 | [https://github.com/rdpeng/ProgrammingAssignment2](https://github.com/rdpeng/ProgrammingAssignment2) 93 | to create a copy under your own account. 94 | 2. Clone your forked GitHub repository to your computer so that you can 95 | edit the files locally on your own machine. 96 | 3. Edit the R file contained in the git repository and place your 97 | solution in that file (please do not rename the file). 98 | 4. Commit your completed R file into YOUR git repository and push your 99 | git branch to the GitHub repository under your account. 100 | 5. Submit to Coursera the URL to your GitHub repository that contains 101 | the completed R code for the assignment. 102 | 103 | ### Grading 104 | 105 | This assignment will be graded via peer assessment. 106 | -------------------------------------------------------------------------------- /cachematrix.R: -------------------------------------------------------------------------------- 1 | ## Description (Preamble): 2 | ## ----------------------- 3 | ## Matrix inversion is usually a costly computation and there may be some 4 | ## benefit to caching the inverse of a matrix rather than computing 5 | ## it repeatedly. 6 | ## To this end, the two functions reported in this file are responsible to 7 | ## calculate and cache the inverse of a matrix. 8 | ## In more details: 9 | ## * `makeCacheMatrix`: This function creates a special "matrix" object 10 | ## that can cache its inverse. 11 | ## * `cacheSolve`: This function computes the inverse of the special "matrix" 12 | ## (as returned by `makeCacheMatrix` function). 13 | ## If the inverse has already been calculated (and the matrix has not changed), 14 | ## then `cacheSolve` should retrieve the inverse from the cache. 15 | 16 | ## Code Style and Guidelines: 17 | ## -------------------------- 18 | ## The Coding style used in this document follows the guidelines reported in 19 | ## [Google's R Style Guide] 20 | ## (https://google-styleguide.googlecode.com/svn/trunk/Rguide.xml) 21 | ## 22 | ## Note: original function names and parameters (e.g., `makeCacheMatrix`, `x`) 23 | ## have not been changed to respect Assignment guidelines. 24 | 25 | ## Author: Valerio Maggio 26 | 27 | ## SIDE NOTE: 28 | # ---------- 29 | # These two functions could be actually merged into one single function 30 | # by embedding the logic of `cacheSolve` into `setInverse`. 31 | # However, for the sake of the Exercise Assignment, the two are kept separated! 32 | 33 | ## ---------------------------------------- 34 | ## Function to create a special CacheMatrix 35 | ## ---------------------------------------- 36 | 37 | makeCacheMatrix <- function(x = matrix()) { 38 | # Creates a special `matrix` object, i.e., the `CacheMatrix`. 39 | # Note: This is just a Data-Object only necessary to store data values. 40 | # No particular logic is embedded in its behavior. 41 | # 42 | # Args: 43 | # x: an inveertible (i.e., squqre) matrix object. 44 | # (Empty matrix by default). 45 | # If the given matrix is NOT Invertible, an error is raised! 46 | # 47 | # Returns: 48 | # A list of four functions devoted to (respectively): 49 | # - `setData`: set the data of the matrix 50 | # - `getData`: get the data of the matrix 51 | # - `setInverse`: set the value of the inverse of the matrix 52 | # - `getInverse`: get the value of the inverse of the matrix 53 | 54 | # Closure to check if a matrix is invertible (namely it is a Square Matrix) 55 | checkIfMatrixIsInvertible <- function(m = x) { 56 | # check if the given matrix `x` is invertible 57 | return(nrow(m) == ncol(m)) 58 | } 59 | 60 | # Error Handling 61 | # -------------- 62 | if (!is.matrix(x)){ 63 | # Raise an error: Input parameter is NOT a Matrix 64 | stop("Error: Wrong Input Type! It MUST be an Invertible Matrix") 65 | } 66 | if (!checkIfMatrixIsInvertible()){ 67 | stop("Error: The input Matrix is NOT Invertible!") 68 | } 69 | 70 | # So far, so good! :) 71 | matrix.inverse <- NULL # initialize the cache for the Inverse 72 | set.data <- function(matrix) { 73 | # First of all, check if `matrix` is invertible before updating data 74 | if (!checkIfMatrixIsInvertible(matrix)){ 75 | message("Given Matrix is NOT Invertible! No data will be set!") 76 | } else { 77 | x <<- matrix 78 | matrix.inverse <<- NULL # everytime matrix data changes, flush the cache! 79 | } 80 | } 81 | get.data <- function() x 82 | set.inverse <- function(inverse) matrix.inverse <<- inverse # updt the cache 83 | get.inverse <- function() matrix.inverse # return cached inverse 84 | # Return the list of four functions above 85 | list(setData = set.data, getData = get.data, 86 | setInverse = set.inverse, 87 | getInverse = get.inverse) 88 | } 89 | 90 | 91 | ## ---------------------------------------------------------- 92 | ## Function to compute the Inverse of a special `CacheMatrix` 93 | ## ---------------------------------------------------------- 94 | 95 | cacheSolve <- function(x, ...) { 96 | # Get (or Compute) the Inverse of a Special `CacheMatrix` as returned by the 97 | # `makeCacheMatrix` function. 98 | # If the cache is empty, the actual inverse matrix will be computed by 99 | # the `solve` function (in R Std. Lib). 100 | # 101 | # Args: 102 | # x: a `CacheMatrix` (as returned by `makeCacheMatrix`) 103 | # If the input argument is a matrix, it will be coerced as `CacheMatrix`. 104 | # If `x` is not of a valid datatype (matrix or CacheMatrix), 105 | # an error is raised. 106 | # If the input [Cache]Matrix is NOT **invertible**, an error is raised as 107 | # well (by the `makeCacheMatrix` function)!! 108 | # 109 | # Returns: 110 | # The Inverse of the input [Cache]Matrix `x`. 111 | 112 | # Error Handling 113 | # -------------- 114 | if (is.matrix(x)) { 115 | # So far, the input x is an Invertible matrix, coerce it to a CacheMatrix 116 | # NOTE: If x is NOT invertible, an error is Raised! 117 | x <- makeCacheMatrix(x) # Overwrite x by making a cacheMatrix 118 | } else if (is.list(x)) { 119 | # If x is a list, it **could** be a CacheMatrix, thus check names 120 | cacheMatrixNames <- c("setData", "getData", "setInverse", "getInverse") 121 | if (!identical(names(x), cacheMatrixNames)) { 122 | stop("Error: the input parameter is NOT a `CacheMatrix`") 123 | } 124 | } else { # No compatible type found 125 | stop("Error: the input parameter MUST be a Matrix or a CacheMatrix") 126 | } 127 | 128 | # So far, so good! :) 129 | inverse <- x$getInverse() 130 | if(!is.null(inverse)) { # The cache is NOT empty 131 | message("getting cached Inverse") 132 | return(inverse) # return the cached inverse 133 | } 134 | data <- x$getData() 135 | # NOTE: we don't need to check for invertibility here, as CacheMatrix are 136 | # invertible by design! (see function `makeCacheMatrix`) 137 | inverse <- solve(data) # Compute the Inverse 138 | x$setInverse(inverse) # update the CacheMatrix 139 | inverse # Return the (computed) Inverse 140 | } 141 | --------------------------------------------------------------------------------