Christopher Maerzluft

UID: 103877186

Homework 3: pqNumber's

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.

Construction:

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)
}

Comparison:

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)
}

Addition:

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)
}

Subtraction:

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)
}

Testing:

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