In this homework we defined a new object class, called pqNumber, in order to handle large numbers. A pqNumber is defined by four components: sign which is equal to 1 or -1 determines whether the number is positive or negative; p determines how many numbers appear after the decimal point; q+1 determines how many numbers appear before the decimal point; Finally nums is a vector of length p+q+1 containing a series of numbers between 0 and 9 where the first element is the last decimal place and the last element if first number read when the pqNumber is converted to an ordinary float.
Our first function takes four elements and creates a pqNumber from them, if it makes sense to do so. Otherwise it gives a warning explaining why you can't make a pqNumber from the desired elements.
"as.pqNumber" <- function(sign, p, q, nums) {
sign <- as.integer(sign)
p <- as.integer(p)
q <- as.integer(q)
nums <- as.integer(nums)
if (abs(sign) != 1 || length(sign) != 1)
stop("The element 'sign' must be either positive or negative one.")
if (length(nums) != abs(p) + abs(q) + 1)
stop("The length of `nums` must add up to p+q+1.")
if (p < 0 || q < 0 || length(p) != 1 || length(q) != 1)
stop("Elements 'p' and 'q' must be single integers greater than or equal to 0.")
if (min(nums) < 0 || max(nums) > 9)
stop("The element 'nums' can only contain values between 0 and 9.")
z <- structure(list(sign = sign, p = p, q = q, nums = nums), class = "pqNumber")
return(z)
}
For purposes of checking our function we create a function to convert a pqNumber back into a regular number.
convert <- function(x) {
if (class(x) != "pqNumber") {
stop("Can only convert pqNumber's")
}
xr <- x$sign * sum((x$nums) * 10^(-(x$p):(x$q)))
return(xr)
}
For later use we need to be able to compare a pqNumber to another pqNumber. So we create the standard 5 comparisons functions: >=,>,<,<=, and ==.
">=.pqNumber" <- function(x, y) {
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must be pqNumber's")
}
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
greaterthan <- TRUE
# Tests comparison based on sign alone
if (x$sign < y$sign) {
greaterthan <- FALSE
} else if (x$sign > y$sign) {
greaterthan <- TRUE
}
# Tests each element of x$num and y$num until it determines the relation
# between x and y
if (x$sign == y$sign) {
for (i in length(x$nums):1) {
if (x$nums[i] > y$nums[i]) {
greaterthan <- TRUE
break
}
if (x$nums[i] < y$nums[i]) {
greaterthan <- FALSE
break
}
if (x$nums[i] == y$nums[i]) {
greaterthan <- TRUE
}
}
}
return(greaterthan)
}
">.pqNumber" <- function(x, y) {
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must be pqNumber's")
}
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
greaterthan <- TRUE
# Tests comparison based on sign alone
if (x$sign < y$sign) {
greaterthan <- FALSE
} else if (x$sign > y$sign) {
greaterthan <- TRUE
}
# Tests each element of x$num and y$num until it determines the relation
# between x and y
if (x$sign == y$sign) {
for (i in length(x$nums):1) {
if (x$nums[i] > y$nums[i]) {
greaterthan <- TRUE
break
}
if (x$nums[i] < y$nums[i]) {
greaterthan <- FALSE
break
}
if (x$nums[i] == y$nums[i]) {
greaterthan <- FALSE
}
}
}
return(greaterthan)
}
"<=.pqNumber" <- function(x, y) {
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must be pqNumber's")
}
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
lessthan <- TRUE
# Tests comparison based on sign alone
if (x$sign > y$sign) {
lessthan <- FALSE
} else if (x$sign < y$sign) {
lessthan <- TRUE
}
# Tests each element of x$num and y$num until it determines the relation
# between x and y
if (x$sign == y$sign) {
for (i in length(x$nums):1) {
if (x$nums[i] < y$nums[i]) {
lessthan <- TRUE
break
}
if (x$nums[i] > y$nums[i]) {
lessthan <- FALSE
break
}
if (x$nums[i] == y$nums[i]) {
lessthan <- TRUE
}
}
}
return(lessthan)
}
"<.pqNumber" <- function(x, y) {
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must be pqNumber's")
}
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
# Tests comparison based on sign alone
lessthan <- TRUE
if (x$sign > y$sign) {
lessthan <- FALSE
} else if (x$sign < y$sign) {
lessthan <- TRUE
}
# Tests each element of x$num and y$num until it determines the relation
# between x and y
if (x$sign == y$sign) {
for (i in length(x$nums):1) {
if (x$nums[i] < y$nums[i]) {
lessthan <- TRUE
break
}
if (x$nums[i] > y$nums[i]) {
lessthan <- FALSE
break
}
if (x$nums[i] == y$nums[i]) {
lessthan <- FALSE
}
}
}
return(lessthan)
}
"==.pqNumber" <- function(x, y) {
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must be pqNumber's")
}
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
equalsign <- TRUE
equalnums <- TRUE
# Tests comparison based on sign alone
if (x$sign != y$sign) {
equalsign <- FALSE
}
# Tests each element of x$num and y$num until it determines the relation
# between x and y
for (i in length(x$nums):1) {
if (x$nums[i] < y$nums[i]) {
equalnums <- FALSE
break
}
if (x$nums[i] > y$nums[i]) {
equalnums <- FALSE
break
}
if (x$nums[i] == y$nums[i]) {
equalnums <- TRUE
}
}
equal <- equalsign && equalnums
return(equal)
}
Also for use later on, we must define the abs function as well.
"abs.pqNumber" <- function(x) {
if (class(x) != "pqNumber") {
stop("Can only convert pqNumber's")
}
xsign <- abs(x$sign)
xp <- x$p
xq <- x$q
xnums <- x$nums
xpq <- as.pqNumber(sign = xsign, p = xp, q = xq, nums = xnums)
return(xpq)
}
Finally we arive at our addition function which takes two pqNumber's and adds them together regardless of their signs, p values, or q values.
"+.pqNumber" <- function(x, y) {
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must have class 'pqNumber'")
}
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
znum1 <- c()
zsign <- 1
# adding two positives or two negatives
if (x$sign > 0 && y$sign > 0 || x$sign < 0 && y$sign < 0) {
znum1 <- x$nums + y$nums
# carry over: if a given element is greater than nine we take away ten and
# add one to the next element
for (i in 1:(length(znum1) - 1)) {
if (znum1[i] >= 10) {
znum1[i + 1] <- znum1[i + 1] + (znum1[i]%/%10)
znum1[i] <- znum1[i]%%10
}
}
# overflow: if the last element is greater than nine we add a new element to
# the end
if (znum1[length(znum1)] >= 10) {
znum2 <- znum1[length(znum1)]%/%10
znum1[length(znum1)] <- znum1[length(znum1)]%%10
znum1 <- c(znum1, znum2)
}
# Appropriate adjustment to the sign of our new pqNumber based on x and y
if (x$sign < 0 && y$sign < 0) {
zsign <- -1
} else {
zsign <- 1
}
# adding a negative to a positive or a positive to a negative
} else if (x$sign < 0 && y$sign > 0 || y$sign < 0 && x$sign > 0) {
# uses abs.pqNumber()
if (abs(y) > abs(x)) {
znum1 <- c()
# subtraction carry over: same as in addition but borrows 10 from the next
# number by taking 1 away from it
for (i in 1:(length(y$nums))) {
if (x$nums[i] > y$nums[i]) {
y$nums[i + 1] <- y$nums[i + 1] - 1
y$nums[i] <- y$nums[i] + 10
znum1[i] <- y$nums[i] - x$nums[i]
} else {
znum1[i] <- y$nums[i] - x$nums[i]
}
}
zsign <- y$sign
# uses abs.pqNumber()
} else if (abs(x) >= abs(y)) {
znum1 <- c()
for (i in 1:(length(x$nums))) {
if (y$nums[i] > x$nums[i]) {
x$nums[i + 1] <- x$nums[i + 1] - 1
x$nums[i] <- x$nums[i] + 10
znum1[i] <- x$nums[i] - y$nums[i]
} else {
znum1[i] <- x$nums[i] - y$nums[i]
}
}
zsign <- x$sign
}
}
znums <- znum1
zp <- max(x$p, y$p)
zq <- abs(length(znums) - zp - 1)
zpq <- as.pqNumber(sign = zsign, p = zp, q = zq, nums = znums)
return(zpq)
}
Lastly we reach our subtraction function which can handle any two pqNumber's as well.
"-.pqNumber" <- function(x, y) {
# appending zeros to front and back of x$nums or y$nums so that
# adding/subtracting/comparison works correctly
if (class(x) != "pqNumber" || class(y) != "pqNumber") {
stop("x and y must have class 'pqNumber'")
}
if (x$p < y$p) {
x$nums <- c(rep(0, times = y$p - x$p), x$nums)
x$p <- y$p
}
if (y$p < x$p) {
y$nums <- c(rep(0, times = x$p - y$p), y$nums)
y$p <- x$p
}
if (x$q < y$q) {
x$nums <- c(x$nums, rep(0, times = y$q - x$q))
x$q <- y$q
}
if (y$q < x$q) {
y$nums <- c(y$nums, rep(0, times = x$q - y$q))
y$q <- x$q
}
znum1 <- c()
zsign <- 1
# subtracting a negative from a positive or a postive from a negative
if (x$sign > 0 && y$sign < 0 || x$sign < 0 && y$sign > 0) {
znum1 <- x$nums + y$nums
# carry over: adds 1 to the next element and removes 10 from the current
# element
for (i in 1:(length(znum1) - 1)) {
if (znum1[i] >= 10) {
znum1[i + 1] <- znum1[i + 1] + (znum1[i]%/%10)
znum1[i] <- znum1[i]%%10
}
}
# over flow: if the last element is greater than 10 adds a new element to
# the end equal to one and subtracts 10 from the last element
if (znum1[length(znum1)] >= 10) {
znum2 <- znum1[length(znum1)]%/%10
znum1[length(znum1)] <- znum1[length(znum1)]%%10
znum1 <- c(znum1, znum2)
}
if (x$sign < 0 && y$sign > 0) {
zsign <- -1
} else {
zsign <- 1
}
# subtracting two negatives or two positives
} else if (x$sign > 0 && y$sign > 0 || x$sign < 0 && y$sign < 0) {
# uses abs.pqNumber()
if (abs(y) > abs(x)) {
znum1 <- c()
# borrowing for subtraction
for (i in 1:(length(y$nums))) {
if (x$nums[i] > y$nums[i]) {
y$nums[i + 1] <- y$nums[i + 1] - 1
y$nums[i] <- y$nums[i] + 10
znum1[i] <- y$nums[i] - x$nums[i]
} else {
znum1[i] <- y$nums[i] - x$nums[i]
}
}
if (x$sign > 0 && y$sign > 0) {
zsign <- -1
} else {
zsign <- 1
}
} else if (abs(x) >= abs(y)) {
znum1 <- c()
# borrowing for subtraction
for (i in 1:(length(x$nums))) {
if (y$nums[i] > x$nums[i]) {
x$nums[i + 1] <- x$nums[i + 1] - 1
x$nums[i] <- x$nums[i] + 10
znum1[i] <- x$nums[i] - y$nums[i]
} else {
znum1[i] <- x$nums[i] - y$nums[i]
}
}
if (x$sign > 0 && y$sign > 0) {
zsign <- 1
} else {
zsign <- -1
}
}
}
znums <- znum1
zp <- max(x$p, y$p)
zq <- abs(length(znums) - zp - 1)
zpq <- as.pqNumber(sign = zsign, p = zp, q = zq, nums = znums)
return(zpq)
}
For testing purposes we create 6 different types of pqNumber's which vary in all aspects.
options(digits = 15)
x <- as.pqNumber(1, 3, 4, c(9, 9, 9, 9, 9, 9, 9, 9))
y <- as.pqNumber(1, 3, 4, c(8, 9, 7, 8, 8, 7, 9, 8))
z <- as.pqNumber(1, 0, 7, 8:1)
mx <- as.pqNumber(-1, 6, 0, 3:9)
my <- as.pqNumber(sign = -1, p = 0, q = 7, nums = c(0, 4, 1, 3, 0, 0, 0, 0))
mz <- as.pqNumber(-1, 1, 6, 8:1)
using our convert function above we create the float version of these numbers as well.
rx <- convert(x)
ry <- convert(y)
rz <- convert(z)
rmx <- convert(mx)
rmy <- convert(my)
rmz <- convert(mz)
x + y
## $sign
## [1] 1
##
## $p
## [1] 3
##
## $q
## [1] 5
##
## $nums
## [1] 7 9 7 8 8 7 9 8 1
##
## attr(,"class")
## [1] "pqNumber"
convert(x + y)
## [1] 189788.797
rx + ry
## [1] 189788.797
y - z
## $sign
## [1] -1
##
## $p
## [1] 3
##
## $q
## [1] 7
##
## $nums
## [1] 2 0 2 9 8 8 5 5 2 2 1
##
## attr(,"class")
## [1] "pqNumber"
convert(y - z)
## [1] -12255889.202
ry - rz
## [1] -12255889.202
z + mx
## $sign
## [1] 1
##
## $p
## [1] 6
##
## $q
## [1] 7
##
## $nums
## [1] 7 5 4 3 2 1 8 6 6 5 4 3 2 1
##
## attr(,"class")
## [1] "pqNumber"
convert(z + mx)
## [1] 12345668.123457
rz + rmx
## [1] 12345668.123457
mx - my
## $sign
## [1] 1
##
## $p
## [1] 6
##
## $q
## [1] 7
##
## $nums
## [1] 7 5 4 3 2 1 0 3 1 3 0 0 0 0
##
## attr(,"class")
## [1] "pqNumber"
convert(mx - my)
## [1] 3130.123457
rmx - rmy
## [1] 3130.123457
my + mz
## $sign
## [1] -1
##
## $p
## [1] 1
##
## $q
## [1] 7
##
## $nums
## [1] 8 7 0 7 7 3 2 1 0
##
## attr(,"class")
## [1] "pqNumber"
convert(my + mz)
## [1] -1237707.8
rmy + rmz
## [1] -1237707.8