Merge branch 'issue-21' into develop (closes #21)

This commit is contained in:
Waldir Leoncio 2022-01-27 12:56:19 +01:00
commit 721b2527ca
26 changed files with 0 additions and 965 deletions

View file

@ -1,14 +0,0 @@
#' @title Blanks
#' @description Create character vector of blanks
#' @details This function emulates the behavior of a homonimous function from Matlab
#' @param n length of vector
#' @return Vector of n blanks
#' @author Waldir Leoncio
#' @export
blanks <- function(n) {
if (n < 0) {
warning("Negative n passed. Treating as n = 0")
n <- 0
}
paste(rep(" ", n), collapse = "")
}

View file

@ -1,36 +0,0 @@
#' @title Cell array
#' @description Creates an array of zeros
#' @param n a the first dimension (or both, if sz is not passed)
#' @param sz the second dimension (or 1st and 2nd, if not passed)
#' @param expandable if TRUE, output is a list (so it can take different
#' lengths)
#' @param ... Other dimensions
#' @return An array of zeroes with the dimensions passed on call
cell <- function(n, sz = c(n, n), expandable = FALSE, ...) {
# Uglyly figuring out if the third arg is an extra dim --- #
sz3 <- vector()
if (!is.logical(expandable)) {
sz3 <- expandable
expandable <- FALSE
}
args <- c(as.list(environment()), list(...))
exp <- args$expandable
extra_dims <- c(sz3, args[names(args) == ""])
# Creating output vector --------------------------------- #
if (exp) {
return(vector("list", length = n))
}
if (length(sz) == 1 & length(extra_dims) == 0) {
return(array(0, dim = c(n, sz)))
} else if (length(extra_dims) > 0) {
return(array(0, dim = c(n, sz, extra_dims)))
} else if (length(sz) == 2) {
return(array(0, dim = sz))
} else {
return(array(0, dim = c(n, sz, ...)))
}
}

View file

@ -1,12 +0,0 @@
#' @title Vector creation
#' @description Simulates the function `colon()` and its equivalent `:` operator from Matlab, which have a similar but not quite equivalent behavior when compared to `seq()` and `:` in R.
#' @param a initial number
#' @param b final number
#' @export
colon <- function(a, b) {
if (a <= b) {
return(a:b)
} else {
return(vector(mode = "numeric"))
}
}

View file

@ -1,15 +0,0 @@
#' @title Find indices and values of nonzero elements
#' @description Emulates behavior of `find`
#' @param x object or logic operation on an object
#' @param sort sort output?
find <- function(x, sort = TRUE) {
if (is.logical(x)) {
out <- which(x)
} else {
out <- which(x > 0)
}
if (sort) {
out <- sort(out)
}
return(out)
}

View file

@ -1,5 +0,0 @@
#' @title Round toward zero
#' @description Rounds each element of input to the nearest integer towards zero. Basically the same as trunc()
#' @param X input element
#' @author Waldir Leoncio
fix <- function(X) trunc(X)

View file

@ -1,18 +0,0 @@
#' @title Gather user input
#' @description Replicates the functionality of the homonymous function in Matlab (sans dialog box)
#' @param prompt Text field with user instructions
#' @param dims number of dimensions in the answwers
#' @param definput default value of the input
#' @export
inputdlg <- function(prompt, dims = 1, definput = NULL) {
if (!is.null(definput)) {
prompt <- append(prompt, paste0(" (default: ", definput, ")"))
}
input_chr <- readline(paste0(prompt, ": "))
if (input_chr == "") input_chr <- definput
input_chr_or_num <- tryCatch(
as.numeric(input_chr),
warning = function(w) input_chr
)
return(input_chr_or_num)
}

View file

@ -1,13 +0,0 @@
#' @title Is Array Empty?
#' @description Determine whether array is empty. An empty array, table, or timetable has at least one dimension with length 0, such as 0-by-0 or 0-by-5.
#' @details Emulates the behavior of the `isempty` function on Matlab
#' @param x array
#'
isempty <- function(x) {
if (class(x)[1] %in% c("array", "matrix")) {
dim_mat_x <- dim(x)
} else {
dim_mat_x <- dim(matrix(x))
}
return(any(dim_mat_x == 0) | is.null(dim_mat_x))
}

View file

@ -1,10 +0,0 @@
#' @title Checks if a list contains a field
#' @description This function tries to replicate the behavior of the `isfield`
#' function in Matlab
#' @param x list
#' @param field name of field
#' @references https://se.mathworks.com/help/matlab/ref/isfield.html
#' @export
isfield <- function(x, field) {
sapply(field, function(f) f %in% names(x))
}

View file

@ -1,11 +0,0 @@
#' @title Determine space characters
#' @description Determine which characters are space characters
#' @param A a character array or a string scalar
#' @return a vector TF such that the elements of TF are logical 1 (true) where corresponding characters in A are space characters, and logical 0 (false) elsewhere
#' @note Recognized whitespace characters are ` ` and `\\t`.
#' @author Waldir Leoncio
isspace <- function(A) {
A_split <- unlist(strsplit(A, ""))
TF <- A_split %in% c(" ", "\t")
return(as.numeric(TF))
}

View file

@ -1,158 +0,0 @@
#' @title Convert Matlab function to R
#' @description Performs basic syntax conversion from Matlab to R
#' @param filename name of the file
#' @param output can be "asis", "clean", "save" or "diff"
#' @param improve_formatting if `TRUE` (default), makes minor changes
#' to conform to best-practice formatting conventions
#' @param change_assignment if `TRUE` (default), uses `<-` as the assignment operator
#' @param append if `FALSE` (default), overwrites file; otherwise, append
#' output to input
#' @return text converted to R, printed to screen or replacing input file
#' @author Waldir Leoncio
#' @importFrom utils write.table
#' @export
#' @note This function is intended to expedite the process of converting a
#' Matlab function to R by making common replacements. It does not have the
#' immediate goal of outputting a ready-to-use function. In other words,
#' after using this function you should go back to it and make minor changes.
#'
#' It is also advised to do a dry-run with `output = "clean"` and only switching
#' to `output = "save"` when you are confident that no important code will be
#' lost (for shorter functions, a careful visual inspection should suffice).
matlab2r <- function(filename, output = "diff", improve_formatting = TRUE, change_assignment = TRUE,
append = FALSE) {
# TODO: this function is too long! Split into subfunctions
# (say, by rule and/or section)
# ======================================================== #
# Verification #
# ======================================================== #
if (!file.exists(filename)) stop("File not found")
# ======================================================== #
# Reading file into R #
# ======================================================== #
txt <- readLines(filename)
original <- txt
# ======================================================== #
# Replacing text #
# ======================================================== #
# Uncommenting ------------------------------------------- #
txt <- gsub("^#\\s?(.+)", "\\1", txt)
# Output variable ---------------------------------------- #
out <- gsub(
pattern = "\\t*function ((\\S|\\,\\s)+)\\s?=\\s?(\\w+)\\((.+)\\)",
replacement = "\\1",
x = txt[1]
) # TODO: improve by detecting listed outputs
if (substring(out, 1, 1) == "[") {
out <- strsplit(out, "(\\,|\\[|\\]|\\s)")[[1]]
out <- out[which(out != "")]
out <- sapply(seq_along(out), function(x) paste(out[x], "=", out[x]))
out <- paste0("list(", paste(out, collapse = ", "), ")")
}
# Function header ---------------------------------------- #
txt <- gsub(
pattern = "\\t*function (.+)\\s*=\\s*(.+)\\((.+)\\)",
replacement = "\\2 <- function(\\3) {",
x = txt
)
txt <- gsub(
pattern = "function (.+)\\((.+)\\)",
replacement = "\\1 <- function(\\2) {",
x = txt
)
# Function body ------------------------------------------ #
txt <- gsub("(.+)\\.\\.\\.", "\\1", txt)
txt <- gsub(";", "", txt)
# Loops and if-statements
txt <- gsub("for (.+)=(.+)", "for (\\1 in \\2) {", txt)
txt <- gsub("end$", "}", txt)
txt <- gsub("if (.+)", "if (\\1) {", txt) # FIXME: paste comments after {
txt <- gsub("else$", "} else {", txt)
txt <- gsub("elseif", "} else if", txt)
txt <- gsub("while (.+)", "while \\1 {", txt)
# MATLAB-equivalent functions in R
txt <- gsub("gamma_ln", "log_gamma", txt)
txt <- gsub("nchoosek", "choose", txt)
txt <- gsub("isempty", "is.null", txt)
# txt <- gsub("(.+)\\'", "t(\\1)", txt)
# Subsets ------------------------------------------------ #
ass_op <- ifelse(change_assignment, "<-", "=")
txt <- gsub(
pattern = "([^\\(]+)\\(([^\\(]+)\\)=(.+)",
replacement = paste0("\\1[\\2] ", ass_op, "\\3"),
x = txt
)
txt <- gsub("\\(:\\)", "[, ]", txt)
txt <- gsub("(.+)(\\[|\\():,end(\\]|\\()", "\\1[, ncol()]", txt)
# Formatting --------------------------------------------- #
if (improve_formatting) {
txt <- gsub("(.),(\\S)", "\\1, \\2", txt)
# Math operators
txt <- gsub("(\\S)\\+(\\S)", "\\1 + \\2", txt)
txt <- gsub("(\\S)\\-(\\S)", "\\1 - \\2", txt)
txt <- gsub("(\\S)\\*(\\S)", "\\1 * \\2", txt)
txt <- gsub("(\\S)\\/(\\S)", "\\1 / \\2", txt)
# Logic operators
txt <- gsub("~", "!", txt)
txt <- gsub("(\\S)>=(\\S)", "\\1 >= \\2", txt)
txt <- gsub("(\\S)<=(\\S)", "\\1 <= \\2", txt)
txt <- gsub("(\\S)==(\\S)", "\\1 == \\2", txt)
# Assignment
txt <- gsub(
pattern = "(\\w)(\\s?)=(\\s?)(\\w)",
replacement = paste0("\\1 ", ass_op, " \\4"),
x = txt
)
# txt <- gsub(
# pattern = "(\\s+(.|\\_|\\[|\\])+)(\\s?)=(\\s?)(.+)",
# replacement = paste0("\\1 ", ass_op, "\\5"),
# x = txt
# )
txt <- gsub("%(\\s?)(\\w)", "# \\2", txt)
}
# Adding output and end-of-file brace -------------------- #
txt <- append(txt, paste0("\treturn(", out, ")\n}"))
# Returning converted code ------------------------------- #
if (output == "asis") {
return(txt)
} else if (output == "clean") {
return(cat(txt, sep = "\n"))
} else if (output == "save") {
return(
write.table(
x = txt,
file = filename,
quote = FALSE,
row.names = FALSE,
col.names = FALSE,
append = append
)
)
} else if (output == "diff") {
diff_text <- vector(mode = "character", length = (2 * length(original) + 1))
for (i in seq_along(txt)) {
new_i <- (2 * i) + i - 2
diff_text[new_i] <- paste(
"-----------------------", "line", i, "-----------------------"
)
diff_text[new_i + 1] <- original[i]
diff_text[new_i + 2] <- txt[i]
}
message("Displaying line number, original content and modified content")
return(cat(diff_text, sep = "\n"))
} else {
stop("Invalid output argument")
}
}

View file

@ -1,33 +0,0 @@
#' @title Minimum (MATLAB version)
#' @description Finds the minimum value for each column of a matrix, potentially returning the indices instead
#' @param X matrix
#' @param indices return indices?
#' @return Either a list or a vector
#' @author Waldir Leoncio
min_MATLAB <- function(X, indices = TRUE) {
if (!is(X, "matrix")) X <- as.matrix(X)
mins <- apply(X, 2, min)
idx <- sapply(seq_len(ncol(X)), function(x) match(mins[x], X[, x]))
if (indices) {
return(list(mins = mins, idx = idx))
} else {
return(mins)
}
}
#' @title Maximum (MATLAB version)
#' @description Finds the minimum value for each column of a matrix, potentially returning the indices instead
#' @param X matrix
#' @param indices return indices?
#' @return Either a list or a vector
#' @author Waldir Leoncio
max_MATLAB <- function(X, indices = TRUE) {
if (!is(X, "matrix")) X <- as.matrix(X)
maxs <- apply(X, 2, max)
idx <- sapply(seq_len(ncol(X)), function(x) match(maxs[x], X[, x]))
if (indices) {
return(list(maxs = maxs, idx = idx))
} else {
return(maxs)
}
}

View file

@ -1,10 +0,0 @@
#' @title Number of function input arguments
#' @description Returns the number of arguments passed to the parent function
#' @return An integer
#' @author Waldir Leoncio
#' @note This function only makes sense inside another function
#' @references https://stackoverflow.com/q/64422780/1169233
nargin <- function() {
if (sys.nframe() < 2) stop("must be called from inside a function")
length(as.list(sys.call(-1))) - 1
}

View file

@ -1,39 +0,0 @@
#' @title Prompt for multiple-choice
#' @param quest Question
#' @param dlgtitle Title of question
#' @param btn Vector of alternatives
#' @param defbtn Scalar with the name of the default option
#' @param accepted_ans Vector containing accepted answers
#' @description This function aims to loosely mimic the behavior of the
#' questdlg function on Matlab
#' @export
questdlg <- function(quest,
dlgtitle = "",
btn = c("y", "n"),
defbtn = "n",
accepted_ans = c("y", "yes", "n", "no")) {
message(dlgtitle)
# ==========================================================================
# Replacing the default option with a capitalized version on btn
# ==========================================================================
btn[match(tolower(defbtn), tolower(btn))] <- toupper(defbtn)
# ==========================================================================
# Creating prompt
# ==========================================================================
option_char <- paste0(" [", paste(btn, collapse = ", "), "]")
answer <- readline(paste0(quest, option_char, ": "))
# ==========================================================================
# Processing answer
# ==========================================================================
answer <- tolower(answer)
if (!(answer %in% tolower(c(btn, accepted_ans)))) {
if (answer != "") {
warning(
"'", answer, "' is not a valid alternative. Defaulting to ",
defbtn
)
}
answer <- defbtn
}
return(answer)
}

View file

@ -1,10 +0,0 @@
#' @title Generate matrix with U(0, 1) trials
#' @description Imitates the behavior of `rand()` on Matlab
#' @param r number of rows of output matrix
#' @param c number of columns of output matrix
#' @return \eqn{r \times c} matrix with random trials from a standard uniform distribution.
#' @importFrom stats runif
#' @export
rand <- function(r = 1, c = 1) {
matrix(runif(r * c), r, c)
}

View file

@ -1,38 +0,0 @@
#' @title Repeat matrix
#' @description Repeats a matrix over n columns and rows
#' @details This function was created to replicate the behavior of a homonymous
#' function on Matlab
#' @param mx matrix
#' @param n either a scalar with the number of replications in both rows and
#' columns or a <= 3-length vector with individual repetitions.
#' @return matrix replicated over `ncol(mx) * n` columns and `nrow(mx) * n` rows
#' @note The Matlab implementation of this function accepts `n` with length > 2.
#'
#' It should also be noted that a concatenated vector in R, e.g. `c(5, 2)`, becomes a column vector when coerced to matrix, even though it may look like a row vector at first glance. This is important to keep in mind when considering the expected output of this function. Vectors in R make sense to be seen as column vectors, given R's Statistics-oriented paradigm where variables are usually disposed as columns in a dataset.
#' @export
repmat <- function(mx, n) {
# Validation
if (length(n) > 3) warning("Extra dimensions of n ignored")
if (!is(mx, "matrix")) mx <- t(as.matrix(mx))
if (length(n) == 1) n <- rep(n, 2)
if (any(n == 0)) {
n_zero <- which(n == 0)
out_dim <- dim(mx)
out_dim[n_zero] <- 0
return(array(dim = out_dim))
}
# Replicating cols
out <- mx_col <- matrix(rep(mx, n[2]), nrow(mx))
# Replicating rows
if (n[1] > 1) {
for (i in seq(n[1] - 1)) out <- rbind(out, mx_col)
}
# Replicating 3rd dimension
if (!is.na(n[3]) & n[3] > 1) out <- array(out, c(dim(out), n[3]))
# Output
return(unname(as.array(out)))
}

View file

@ -1,24 +0,0 @@
#' @title Reshape array
#' @description Reshapes a matrix according to a certain number of dimensions
#' @param A input matrix
#' @param sz vector containing the dimensions of the output vector
#' @details This function replicates the functionality of the `reshape()`
#' function on Matlab. This function is basically a fancy wrapper for the
#' `array()` function in R, but is useful because it saves the user translation
#' time. Moreover, it introduces validation code that alter the behavior of
#' `array()` and makes it more similar to `replicate()`.
#' @note The Matlab function also accepts as input the dismemberment of sz as
#' scalars.
reshape <- function(A, sz) {
# Validation
if (prod(sz) != prod(dim(A))) {
stop("To RESHAPE the number of elements must not change.")
}
if (length(sz) == 1) {
stop("Size vector must have at least two elements.")
}
# Reshaping A
A <- array(A, sz)
return(A)
}

View file

@ -1,25 +0,0 @@
#' @title Set differences of two arrays
#' @description Loosely replicates the behavior of the homonym Matlab function
#' @param A first array
#' @param B second array
#' @param legacy if `TRUE`, preserves the behavior of the setdiff function from MATLAB R2012b and prior releases. (currently not supported)
#' @author Waldir Leoncio
setdiff_MATLAB <- function(A, B, legacy = FALSE) {
if (legacy) message("legacy=TRUE not supported. Ignoring.")
if (is(A, "numeric") & is(B, "numeric")) {
values <- sort(unique(A[is.na(match(A, B))]))
} else if (is(A, "data.frame") & is(B, "data.frame")) {
C <- A
exclude_rows <- vector()
for (r1 in seq_len(nrow(A))) {
for (r2 in seq_len(nrow(B))) {
if (all(A[r1, ] == B[r2, ])) {
exclude_rows <- append(exclude_rows, r1)
}
}
}
values <- C[-exclude_rows, ]
}
# TODO: add support for indices (if necessary)
return(values)
}

View file

@ -1,38 +0,0 @@
#' @title Size of an object
#' @description This functions tries to replicate the behavior of the base function "size" in Matlab
#' @param x object to be evaluated
#' @param d dimension of object to be evaluated
#' @note On MATLAB, size(1, 100) returns 1. As a matter of fact, if the user
#' calls for a dimension which x doesn't have `size()` always returns 1. R's
#' default behavior is more reasonable in those cases (i.e., returning NA),
#' but since the point of this function is to replicate MATLAB behaviors
#' (bugs and questionable behaviors included), this function also does this.
#' @export
size <- function(x, d) {
# Determining the number of dimensions
if (all(is.na(x))) {
if (missing(d)) {
return(c(0, 0))
} else {
return(ifelse(d <= 2, 0, 1))
}
}
if (length(x) == 1) {
# x is surely a scalar
return(1)
} else {
# x is a vector, a matrix or an array
n_dim <- ifelse(is.null(dim(x)), 1, length(dim(x)))
if (missing(d)) {
if (n_dim == 1) {
out <- c(1, length(x))
} else {
out <- dim(x)
}
} else {
out <- ifelse(n_dim == 1, c(1, length(x))[d], dim(x)[d])
if (is.na(out)) out <- 1 # for MATLAB compatibility
}
return(out)
}
}

View file

@ -1,15 +0,0 @@
#' @title Sort rows of matrix or table
#' @description Emulates the behavior of the `sortrows` function on Matlab
#' @param A matrix
#' @param column ordering column
sortrows <- function(A, column = 1) {
if (length(column) == 1) {
new_row_order <- order(A[, column])
} else if (length(column) == 2) {
new_row_order <- order(A[, column[1]], A[, column[2]])
} else {
stop("Not yet implemented for 2+ tie-breakers")
}
A_reordered <- A[new_row_order, ]
return(A_reordered)
}

View file

@ -1,15 +0,0 @@
#' @title Squeeze
#' @description Remove dimensions of length 1
#' @details This function implements the behavior of the homonimous function on
#' Matlab. `B = squeeze(A)` returns an array with the same elements as the
#' input array A, but with dimensions of length 1 removed. For example, if A is
#' a 3-by-1-by-1-by-2 array, then squeeze(A) returns a 3-by-2 matrix. If A is a
#' row vector, column vector, scalar, or an array with no dimensions of length
#' 1, then squeeze returns the input A.
#' @note This is basically a wrapper of drop() with a minor adjustment to adapt
#' the output to what happens on Matlab
#' @param A input or array matrix
#' @return An array with the same elements as the input array, but with
#' dimensions of length 1 removed.
#' @author Waldir Leoncio
squeeze <- function(A) as.matrix(drop(A))

View file

@ -1,28 +0,0 @@
#' @title Compare two character elements
#' @description Logical test if two character elements are identical
#' @param s1 first character element (string, vector or matrix)
#' @param s2 second character element (string, vector or matrix)
#' @return a logical element of the same type as the input
#' @export
strcmp <- function(s1, s2) {
if (length(s1) == 1 & length(s2) == 1) {
# Both are scalars, comparison is straightforward
return(identical(s1, s2))
} else if (length(s1) == 1 & length(s2) > 1) {
# s1 is a scalar and s2 is a vector or a matrix
checks <- sapply(s2, function(s) s1 %in% s)
if (is(s2, "matrix")) checks <- matrix(checks, nrow(s2))
} else if (length(s1) > 1 & length(s2) == 1) {
# s1 is a vector/matrix, s2 is a scalar
checks <- sapply(s1, function(s) s2 %in% s)
if (is(s1, "matrix")) checks <- matrix(checks, nrow(s1))
} else {
# s1 and s2 are vectors/matrices
if (identical(dim(s1), dim(s2))) {
checks <- as.matrix(s1 == s2)
} else {
stop("Inputs must be the same size or either one can be a scalar.")
}
}
return(checks)
}

View file

@ -1,51 +0,0 @@
#' @title Element-wise matrix multiplication
#' @description Emulates the `times()` and `.*` operators from Matlab.
#' @details This function basically handles elements of different length better than the `*` operator in R, at least as far as behavior from a Matlab user is expecting.
#' @param a first factor of the multiplication
#' @param b second factor of the multiplication
#' @export
#' @returns matrix with dimensions equal to the larger of the two factors
times <- function(a, b) {
# Converting everything to matrix because Matlab looooooves the matrix
a <- as.matrix(a)
b <- as.matrix(b)
dominant_mx <- NULL
if (!all(dim(a) == dim(b))) {
if (all(dim(a) >= dim(b))) {
dominant_mx <- a
dominated_mx <- b
} else if (all(dim(b) >= dim(a))) {
dominant_mx <- b
dominated_mx <- a
} else {
dominant_mx <- "neither"
dominant_dim <- c(max(nrow(b), nrow(a)), max(ncol(b), ncol(a)))
}
}
if (is.null(dominant_mx)) {
out <- a * b
} else if (dominant_mx[1] == "neither") {
a <- repmat(
mx = a,
n = c(dominant_dim[1] - nrow(a) + 1, dominant_dim[2] - ncol(a) + 1)
)
b <- repmat(
mx = b,
n = c(dominant_dim[1] - nrow(b) + 1, dominant_dim[2] - ncol(b) + 1)
)
out <- a * b
} else {
# Expanding dominated matrix
dominated_mx <- repmat(
mx = dominated_mx,
n = c(
nrow(dominant_mx) - nrow(dominated_mx) + 1,
ncol(dominant_mx) - ncol(dominated_mx) + 1
)
)
out <- dominant_mx * dominated_mx
}
return(out)
}

View file

@ -1,31 +0,0 @@
#' @title Select a file for loading
#' @description Loosely mimics the functionality of the `uigetfile` function on
#' Matlab.
#' @references https://se.mathworks.com/help/matlab/ref/uigetfile.html
#' @param filter Filter listed files
#' @param title Pre-prompt message
#' @export
uigetfile <- function(filter = "", title = "") {
# ==========================================================================
# Pre-prompt message
# ==========================================================================
message(title)
# ==========================================================================
# Reading file path and name
# ==========================================================================
filepath <- readline(
paste0("Enter file path (leave empty for ", getwd(), "): ")
)
if (filepath == "") filepath <- getwd()
# ==========================================================================
# Presenting possible files
# ==========================================================================
message("Files present on that directory:")
print(list.files(path = filepath, pattern = filter, ignore.case = TRUE))
filename <- file.choose()
# ==========================================================================
# Organizing output
# ==========================================================================
out <- list(name = filename, path = filepath)
return(out)
}

View file

@ -1,21 +0,0 @@
#' @title Save file
#' @param filter accepted file extension
#' @param title Title
#' @description This function intends to loosely mimic the behaviour of the
#' homonymous Matlab function.
#' @export
uiputfile <- function(filter = ".rda", title = "Save file") {
# ==========================================================================
# Processing input
# ==========================================================================
message(title)
filename <- readline(paste0("File name (end with ", filter, "): "))
filepath <- readline(paste0("File path (leave empty for ", getwd(), "): "))
if (filename == "") filename <- 0
if (filepath == "") filepath <- getwd()
# ==========================================================================
# Processing output
# ==========================================================================
out <- list(name = filename, path = filepath)
return(out)
}

View file

@ -1,50 +0,0 @@
#' @title Matrix of zeros or ones
#' @description Generates a square or rectangular matrix of zeros or ones
#' @param n scalar or 2D vector
#' @param x value to fill matrix with
#' @return n-by-n matrix filled with `x`
#' @details This is a wrapper function to replicate the behavior of the
#' `zeros()` and the `ones()` functions on Matlab
#' @note Actually works for any `x`, but there's no need to bother imposing
#' validation controls here.
zeros_or_ones <- function(n, x) {
# Expanding n to length 2 if necessary
if (length(n) == 1) n <- c(n, n)
# Returning a matrix or an array
if (length(n) == 2) {
return(matrix(x, n[1], n[2]))
} else {
return(array(x, dim = n))
}
}
#' @title Matrix of zeros
#' @description wrapper of `zeros_or_ones()` that replicates the behavior of
#' the `zeros()` function on Matlab
#' @param n1 number of rows
#' @param n2 number of columns
#' @param ... extra dimensions
zeros <- function(n1, n2 = n1, ...) {
if (length(n1) == 1) {
n <- c(n1, n2, ...)
} else {
n <- n1
}
return(zeros_or_ones(n, 0))
}
#' @title Matrix of ones
#' @description wrapper of `zeros_or_ones()` that replicates the behavior of
#' the `ones()` function on Matlab
#' @param n1 number of rows
#' @param n2 number of columns
#' @param ... extra dimensions
ones <- function(n1, n2 = n1, ...) {
if (length(n1) == 1) {
n <- c(n1, n2, ...)
} else {
n <- n1
}
return(zeros_or_ones(n, 1))
}

View file

@ -1,245 +0,0 @@
context("Basic Matlab functions")
test_that("rand works properly", {
expect_equal(dim(rand()), c(1, 1))
expect_equal(dim(rand(1, 2)), c(1, 2))
expect_equal(dim(rand(3, 2)), c(3, 2))
})
test_that("repmat works properly", {
mx0 <- c(1:4) # when converted to matrix, results in a column vector
mx1 <- matrix(5:8)
mx2 <- matrix(0:-3, 2)
expect_error(repmat(mx0))
expect_equal(repmat(mx0, 1), t(as.matrix(mx0)))
expect_equal(
object = repmat(mx0, 2),
expected = unname(cbind(rbind(mx0, mx0), rbind(mx0, mx0)))
)
expect_equal(
object = repmat(mx1, 2),
expected = unname(cbind(rbind(mx1, mx1), rbind(mx1, mx1)))
)
expect_equal(
object = repmat(mx2, c(2, 3)),
expected = cbind(rbind(mx2, mx2), rbind(mx2, mx2), rbind(mx2, mx2))
)
expect_equal(
object = repmat(mx2, c(4, 1)),
expected = rbind(mx2, mx2, mx2, mx2)
)
expect_equal(
object = repmat(mx2, c(1, 1, 2)),
expected = array(mx2, c(2, 2, 2))
)
expect_equal(repmat(1:2, 3), matrix(rep(1:2, 9), 3, 6, byrow = TRUE))
expect_equal(repmat(10, c(3, 2)), matrix(10, 3, 2))
})
test_that("zeros and ones work as expected", {
expect_equal(zeros(1), matrix(0, 1))
expect_equal(zeros(2), matrix(0, 2, 2))
expect_equal(zeros(2, 1), matrix(0, 2, 1))
expect_equal(zeros(1, 10), matrix(0, 1, 10))
expect_equal(zeros(3, 2, 4), array(0, c(3, 2, 4)))
expect_equal(ones(8), matrix(1, 8, 8))
expect_equal(ones(5, 2), matrix(1, 5, 2))
expect_equal(ones(2, 100), matrix(1, 2, 100))
expect_equal(ones(3, 2, 4, 2), array(1, c(3, 2, 4, 2)))
})
test_that("times works as expected", {
expect_equal(times(9, 6), as.matrix(54))
expect_equal(times(9, c(.8, 9)), as.matrix(c(7.2, 81)))
expect_equal(times(c(.8, 9), 5), as.matrix(c(4, 45)))
expect_equal(times(matrix(1:4, 2), 5), matrix(c(5, 10, 15, 20), 2))
expect_equal(times(5, matrix(1:4, 2)), matrix(c(5, 10, 15, 20), 2))
expect_equal(times(matrix(1:4, 2), c(10, 3)), matrix(c(10, 6, 30, 12), 2))
expect_equal(
object = times(matrix(1:4, 2), matrix(c(10, 3), 1)),
expected = matrix(c(10, 20, 9, 12), 2)
)
expect_equal(times(c(10, 3), matrix(1:4, 2)), matrix(c(10, 6, 30, 12), 2))
expect_equal(
object = times(matrix(c(10, -5, 3, 9), 2), matrix(1:4, 2)),
expected = matrix(c(10, -10, 9, 36), 2)
)
expect_equal(
object = times(matrix(c(-1.6, 5), 1), c(8, 1)),
expected = matrix(c(-12.8, -1.6, 40, 5), 2)
)
})
test_that("colon works as expected (hee hee)", {
expect_equal(colon(1, 4), 1:4)
expect_length(colon(4, 1), 0)
})
test_that("size works as on MATLAB", {
sk <- 10
vk <- 1:4
mx <- matrix(1:6, 2)
ra <- array(1:24, c(2, 3, 4))
expect_equal(size(sk), 1)
expect_equal(size(vk), c(1, 4))
expect_equal(size(mx), c(2, 3))
expect_equal(size(ra), c(2, 3, 4))
expect_equal(size(sk, 199), 1)
expect_equal(size(vk, 199), 1)
expect_equal(size(mx, 199), 1)
expect_equal(size(ra, 199), 1)
expect_equal(size(vk, 2), 4)
expect_equal(size(mx, 2), 3)
expect_equal(size(ra, 2), 3)
expect_equal(size(ra, 3), 4)
})
test_that("reshape reshapes properly", {
mx <- matrix(1:4, 2)
ra <- array(1:12, c(2, 3, 2))
expect_equal(reshape(mx, c(1, 4)), matrix(1:4, 1))
expect_equal(reshape(mx, c(2, 2)), mx)
expect_equal(reshape(mx, c(1, 1, 4)), array(mx, c(1, 1, 4)))
expect_error(reshape(mx, c(1, 2, 3)))
expect_error(reshape(ra, c(1, 2, 3)))
expect_equal(reshape(ra, c(3, 2, 2)), array(ra, c(3, 2, 2)))
})
test_that("isfield works as on Matlab", {
S <- list()
S$x <- rnorm(100)
S$y <- sin(S$x)
S$title <- "y = sin(x)"
expect_true(isfield(S, "title"))
expect_equivalent(
object = isfield(S, c("x", "y", "z", "title", "error")),
expected = c(TRUE, TRUE, FALSE, TRUE, FALSE)
)
})
test_that("strcmp works as expected", {
yes <- "Yes"
no <- "No"
ja <- "Yes"
expect_false(strcmp(yes, no))
expect_true(strcmp(yes, ja))
s1 <- "upon"
s2 <- matrix(c("Once", "upon", "a", "time"), 2, byrow = TRUE)
s3 <- c("Once", "upon", "a", "time")
s4 <- matrix(c("A", "bc", "def", "G"), 2, byrow = TRUE)
s5 <- matrix(c("B", "c", "def", "G"), 2, byrow = TRUE)
expect_equal(strcmp(s1, s2), matrix(c(FALSE, FALSE, TRUE, FALSE), 2))
expect_equivalent(strcmp(s1, s3), c(FALSE, TRUE, FALSE, FALSE))
expect_error(strcmp(s2, s3))
expect_equal(strcmp(s4, s5), matrix(c(FALSE, TRUE, FALSE, TRUE), 2))
})
test_that("isempty works as expected", {
A <- array(dim = c(0, 2, 2))
B <- matrix(rep(NA, 4), 2)
C <- matrix(rep(0, 4), 2)
cat1 <- as.factor(c(NA, NA))
cat2 <- factor()
str1 <- matrix(rep("", 3))
expect_true(isempty(A))
expect_false(isempty(B))
expect_false(isempty(C))
expect_false(isempty(cat1))
expect_true(isempty(cat2))
expect_false(isempty(str1))
})
test_that("find works as expected", {
X <- matrix(c(1, 0, 2, 0, 1, 1, 0, 0, 4), 3, byrow = TRUE)
Y <- seq(1, 19, 2)
expect_equal(find(X), c(1, 5, 7, 8, 9))
expect_equal(find(!X), c(2, 3, 4, 6))
expect_equal(find(Y == 13), 7)
})
test_that("sortrows works as expected", {
mx <- matrix(c(3, 2, 2, 1, 1, 10, 0, pi), 4)
expect_equal(sortrows(mx), matrix(c(1, 2, 2, 3, pi, 10, 0, 1), 4))
expect_equal(sortrows(mx, 2), matrix(c(2, 3, 1, 2, 0, 1, pi, 10), 4))
expect_equal(sortrows(mx, 1:2), mx[order(mx[, 1], mx[, 2]), ])
})
test_that("cell works as expected", {
expect_equivalent(cell(0), array(0, dim = c(0, 0)))
expect_equivalent(cell(1), array(0, dim = c(1, 1)))
expect_equivalent(cell(2), array(0, dim = c(2, 2)))
expect_equivalent(cell(3, 4), array(0, dim = c(3, 4)))
expect_equivalent(cell(5, 7, 6), array(0, dim = c(5, 7, 6)))
})
test_that("blanks works as expected", {
expect_warning(blanks(-1))
expect_equal(suppressWarnings(blanks(-1)), "")
expect_equal(blanks(0), "")
expect_equal(blanks(1), " ")
expect_equal(blanks(10), " ")
})
test_that("squeeze works as expected", {
A <- array(dim = c(2, 1, 2))
A[, , 1] <- c(1, 2)
A[, , 2] <- c(3, 4)
expect_equal(squeeze(A), matrix(1:4, 2))
A <- array(0, dim = c(1, 1, 3))
A[, , 1:3] <- 1:3
expect_equal(squeeze(A), matrix(1:3, 3))
})
test_that("fix works as expected", {
X <- matrix(c(-1.9, -3.4, 1.6, 2.5, -4.5, 4.5), 3, byrow = TRUE)
Y <- matrix(c(-1, -3, 1, 2, -4, 4), 3, byrow = TRUE)
expect_identical(fix(X), Y)
})
test_that("isspace works as expected", {
chr <- "123 Main St."
X <- "\t a b\tcde f"
expect_identical(isspace(chr), c(0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0))
expect_identical(isspace(X), c(1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0))
})
test_that("nargin works correctly", {
addme <- function(a, b) {
if (nargin() == 2) {
c <- a + b
} else if (nargin() == 1) {
c <- a + a
} else {
c <- 0
}
return(c)
}
expect_equal(addme(13, 42), 55)
expect_equal(addme(13), 26)
expect_equal(addme(), 0)
})
test_that("setdiff works as expected", {
A <- c(3, 6, 2, 1, 5, 1, 1)
B <- c(2, 4, 6)
C <- c(1, 3, 5)
expect_equal(setdiff_MATLAB(A, B), C)
A <- data.frame(
Var1 = 1:5,
Var2 = LETTERS[1:5],
Var3 = c(FALSE, TRUE, FALSE, TRUE, FALSE)
)
B <- data.frame(
Var1 = seq(1, 9, by = 2),
Var2 = LETTERS[seq(1, 9, by = 2)],
Var3 = rep(FALSE, 5)
)
C <- data.frame(
Var1 = c(2, 4),
Var2 = c("B", "D"),
Var3 = c(TRUE, TRUE)
)
row.names(C) <- c(2L, 4L)
expect_equal(setdiff_MATLAB(A, B), C) # TODO: implement for data frames
# TODO: add more examples from https://se.mathworks.com/help/matlab/ref/double.setdiff.html;jsessionid=0d8d42582d4d299b8224403899f1
})