###############################################################################

# Author:           Lucy Boyt
# Student Number:   2308345
# Created Date:     21/10/2025
# Module:           Statistical Programming (ACCB5021)
# Assignment Title: Data Manipulation
# Script Title:     lb_data_manipulation_assignment.R
# Description:      This code creates a data-frame to hold 200 values of the Fibonacci numbers, Pell numbers along with the approximation, difference from true √2 and accuracy 

###############################################################################

#------------------
# 1. Initial Setup 
#------------------

# Turn Off Scientific notation to verify output. 
options(scipen = 999)

# Set number of iterations dynamically
values <- 200

# Define initial values for the sequences 
index_n <- c(1, 2)
fib     <- c(0, 1)
pell    <- c(0, 1)
  
# Pre-define supporting vectors
sqrt2         <- sqrt(2)
sqrt_diff     <- NULL
pellsq_approx <- NULL
accuracy      <- NULL
  
#-----------------------------------------------------------
# 2. Question 1 - Generate Index, Fibonacci & Pell sequences  
#-----------------------------------------------------------

# For loop to calculate the next values in index_n, fib, pell up to the dynamic value vector.
  for (i in 3:values) { 
    index_n[i] <- i
    fib[i]     <- fib [i - 1] + fib [i - 2]
    pell[i]    <- (2 * pell [i - 1]) + pell [i - 2]
  }

# insert the above vectors into a data frame called pelldata. 
  pelldata <- data.frame(
    index     = index_n,
    fibonacci = fib,
    pell      = pell
  )

#------------------------------------------------------------------------
# 3. Questions 2 & 3 - Calculate Pell approximation to √2 and difference
#------------------------------------------------------------------------

  # For loop to calculate the Pell Approximation and the difference between the approximation. 
  for (i in 2:length(pell)) {
    
    # Calculate approximation
    pellsq_approx[i] <- (pell[i-1] + pell[i]) / pell[i]
    
    # set decimal place scale (5) 
    dp <- 10^5
    
    #Calculate the difference between the approximation to √2 (to set decimal places)
    sqrt_diff[i] <- trunc((pellsq_approx[i] - sqrt2) * dp) / dp
    
  }
  
  # insert values into data frame (pelldata) as new column
  pelldata$pellsq_approx <-pellsq_approx
  pelldata$sqrt_diff     <-sqrt_diff
  
#------------------------------------------------------------------------
# 4. Question 4 - Calculate accuracy (number of correct decimal places)
#------------------------------------------------------------------------

 for (i in 1:length(pellsq_approx)) {
   
   # Handle NA's by inserting an NA in the vector
   if (is.na(pellsq_approx[i])) {
     accuracy <-c(accuracy, NA)
   } 
   else {
     
     counter <- 0 # counter for decimal point accuracy 
     
     for (j in 1:20) { # accuracy beyond 15–16 is limited by double-precision representation 
       
       # Calculate the lowest and highest allowance around sqrt(2) based on current decimal place (j)
       lower_allow <- sqrt2 - (1/ (10^j)) 
       upper_allow <- sqrt2 + (1/ (10^j)) 
       
       # inserts decimal place if the approximation is between lower and upper allowance, otherwise stops loop
       if (pellsq_approx[i] >= lower_allow & pellsq_approx[i] <= upper_allow) {
         counter <- j 
       } else {
         break
       }
     }  
     # appends the last counter value for this approximation. 
     accuracy<- c(accuracy, counter)
   }
 }

# insert values into data frame (pelldata) as new column
pelldata$accuracy<-accuracy

# Testing code:
# pelldata[1:12,] 
# datacheck<-pelldata[c(6, 15, 32, 45, 70, 130, 200), ]
