1#' Transform an integer to an array of base-n digits
2#'
3#' Transform an integer to an array of base-n digits
4#'
5#' This function converts the elements of an integer vector as an array of its
6#' digits.  The base of the numbering scheme may be changed away from 10, which
7#' defines our decimal system, to any other integer value. For base=2, the
8#' number is returned in the binary system. The least significant digit has the
9#' highest index in the array, i.e. it appears on the right.  The highest
10#' exponent is at position 1, i.e. left.
11#'
12#' To write decimal values in another base is very common in computer science.
13#' In particular at the basis 2 the then possible values 0 and 1 are often
14#' interpreted as logical false or true. And at the very interface to
15#' electrical engineering, it is indicated as an absence or presence of
16#' voltage. When several bit values are transported synchronously, then it is
17#' common to give every lane of such a data bus a unique 2^x value and
18#' interpret it as a number in the binary system. To distinguish 256 characters
19#' one once needed 8 bit ("byte"). It is the common unit in which larger
20#' non-printable data is presented.  Because of the many non-printable
21#' characters and the difficulty for most humans to memorize an even longer
22#' alphabet, it is presented as two half bytes ("nibble") of 4 bit in a
23#' hexadecimal presentation. Example code is shown below.
24#'
25#' For statisticians, it is more likely to use bit representations for hashing.
26#' A bit set to 1 (TRUE) at e.g. position 2, 9 or 17 is interpreted as the
27#' presence of a particular feature combination of a sample.  With baseOf, you
28#' can refer to the bit combination as a number, which is more easily and more
29#' efficiently dealt with than with an array of binary values. The example code
30#' presents a counter of combinations of features which may be interpreted as a
31#' Venn diagram.
32#'
33#' @param v A single integer value to be transformed.
34#' @param base The base to which to transform to.
35#' @param len The minimal length of the returned array.
36#' @author Steffen Moeller \email{moeller@@debian.org}
37#' @keywords base
38#' @examples
39#'
40#' # decimal representation
41#' baseOf(123)
42#'
43#' # binary representation
44#' baseOf(123, base = 2)
45#'
46#' # octal representation
47#' baseOf(123, base = 8)
48#'
49#' # hexadecimal representation
50#' baseOf(123, base = 16)
51#'
52#' # hexadecimal with more typical letter-notation
53#' c(0:9, LETTERS)[baseOf(123, 16)]
54#'
55#' # hexadecimal again, now showing a single string
56#' paste(c(0:9, LETTERS)[baseOf(123, 16)], collapse = "")
57#'
58#' # decimal representation but filling leading zeroes
59#' baseOf(123, len = 5)
60#'
61#' # and converting that back
62#' sum(2^(4:0) * baseOf(123, len = 5))
63#'
64#' # hashing and a tabular venn diagram derived from it
65#' m <- matrix(sample(c(FALSE, TRUE), replace = TRUE, size = 300), ncol = 4)
66#' colnames(m) <- c("strong", "colorful", "nice", "humorous")
67#' names(dimnames(m)) <- c("samples", "features")
68#' head(m)
69#'
70#' m.val <- apply(m, 1, function(X) {
71#'   return(sum(2^((ncol(m) - 1):0) * X))
72#' })
73#' m.val.rle <- rle(sort(m.val))
74#' m.counts <- cbind(
75#'   baseOf(m.val.rle$value, base = 2, len = ncol(m)),
76#'   m.val.rle$lengths
77#' )
78#' colnames(m.counts) <- c(colnames(m), "num")
79#' rownames(m.counts) <- apply(m.counts[, 1:ncol(m)], 1, paste, collapse = "")
80#' m.counts[1 == m.counts[, "nice"] & 1 == m.counts[, "humorous"], , drop = FALSE]
81#' m.counts[, "num", drop = TRUE]
82#' @export
83baseOf <- function(v,
84                   base = 10,
85                   len = 1) {
86  if (is.null(v)) {
87    stop("v is null")
88  }
89  if (length(v) == 0) {
90    return(integer(0))
91  }
92
93  if (any(as.integer(v) != v)) {
94    stop("non-integer value(s) provided for v.")
95  }
96
97  if (length(v) > 1) {
98    # this returns a list which may have vectors of varying lenths
99    val.list <- lapply(X = v, FUN = baseOf.inner, base = base, len = len)
100    longest <- max(sapply(val.list, length))
101
102    # call again, forcing all elements to have the same lenth
103    retval <- t(sapply(X = v, FUN = baseOf.inner, base = base, len = longest))
104
105    # add informative row and column names
106    rownames(retval) <- paste0("v.", v)
107    colnames(retval) <- paste0("b.", rev(c(0, base^(1:(longest - 1)))))
108
109    retval
110  }
111  else {
112    retval <- baseOf.inner(v = v, base = base, len = len)
113  }
114
115  retval
116}
117
118
119# Transform integer to array of digits in specified
120baseOf.inner <- function(v,
121                         base = 10,
122                         len = 1) {
123  if (is.na(v)) {
124    return(rep(NA, len))
125  }
126
127  if (v == 0) {
128    return(rep(0, len))
129  }
130
131  remainder <- v
132  i <- len
133  ret <- NULL
134  while (remainder > 0 || i > 0) {
135    # print(paste("i=",i," remainder=",remainder))
136    m <- remainder %% base
137    if (is.null(ret)) {
138      ret <- m
139    }
140    else {
141      ret <- c(m, ret)
142    }
143    remainder <- remainder %/% base
144    i <- i - 1
145  }
146
147  if (length(ret) > 1) {
148    names(ret) <- rev(c(0, base^(1:(length(ret) - 1))))
149  }
150
151  return(ret)
152}
153