Project developed for PFL unit
- Rui Pedro Mendes Moreira up201906355
- Sérgio Rodrigues da Gama up201906690
-
There are tests for all the main BigNumber and Fibonacci modules functions in the Tests.hs file. The tests for
output and scanner
were done withIO Monad
by comparing some expected values with the ones computed by those functions and returning PASSED or FAILED to the screen. -
All the other tests were made with the help of QuickTest module, making a property function for the Fibonacci functions and somaBN, subBn, mulBN and divBn, from BigNumber module. Finally, in order to execute all of these tests with one function call, the main IO function is responsible for running the tests.
-
1.1 fibRec: This function takes one argument, which is the index of the Fibonacci sequence we want to calculate. It is implemented using a
naive recursive strategy
, calculating the value fib(n), by adding fib(n-1) + fib(n-2), aside from the base cases which are fib(0) = 0 and fib(1) = 1; -
1.2 fibLista: This function is based in fibRec, however, taking advantage of a
dynamic programming
approach. It saves the calculated results in a list that it is passed to the next function calls, retrieving the values needed from that list and preventing multiple computations of the same value. In order to achieve this, fibLista() uses an external auxiliary functionfibListaAux()
. -
1.3 fibListaInfinita: In contrary to the last function that uses finite lists, this one takes advantage of
infinit lists
. Due to the lazy computation feature of Haskell, the list is only calculated upon what it is requested. -
1.4 fibRecBN, 1.5 fibListaBN and 1.6 fibListaInfinitaBN: This group of functions work exactly the same way the ones previously explained, but with the integration of BigNumbers.
Brief BigNumber module function description, see beneath for more details
-
2.2 scanner: String to BigNumber conversion.
-
2.3 output: BigNumber to String conversion.
-
2.4 somaBN: Addition function for BigNumbers, returning the result in a BigNumber.
-
2.5 subBN: Subtraction fucntion for BigNumbers.
-
2.6 mulBN: Multiplication function for BigNumbers.
-
2.7 divBN: Division function for BigNumbers, returning both the remainder and the quotient in BigNumber form.
-
3 safeDivBN: Divides one BigNumber by another, by making use of
Maybe
Preludes data to solve division by 0 problem, returning Nothing in that case.
-
bnToInt: BigNumber to Int conversion.
-
intToBN: Int to BigNumber conversion.
-
bnFractionalToInt: Converts a tuple of BigNumbers retrieved from divBN, into an Int.
-
intToList: Int to [Int] conversion.
-
listToInt: [Int] to Int conversion.
-
stringToNumbers: Converts a String into an array of Int's. When the String has non digit characters, digitToInt() prelude function, outputs the matching error.
-
numbersToString: Converts an array of Int's into a String (an array of Char's).
-
trimString: Removes all trailling '0' characters from the beginning of the String, until a different one is found.
-
trimInts: Removes all trailling 0's from an array of Int's from the beginning of the array, until a none 0 Int is found.
-
biggerBN: Compares two BigNumbers (a and b) and returns 0, if a > b, 1 if b > a, 2 if a == b, 4 if both numbers are Zeros
-
notBN: Negates a BigNumber, by changing its signal (Bool). Zero prevails Zero.
-
somaBNaux: Sums two numbers, given has a lists of Ints, and returns a list of Ints with the result.
-
subBNaux: Subtracts two numbers, given has a lists of Ints, and returns a list of Ints with the result.
-
mulBNaux: Multiplies two numbers, given has a lists of Ints, and returns a list of Ints with the result.
-
divBNaux: Divides one number by the other, given has a lists of Ints, and returns a list of Ints with the result.
-
divAuxEmptyToZero: Converts a lists of Ints tuple into a BigNumbers tuple.
- The BigNumbers (Numbers in which the size is only limited by the computer memory) definition was created with the intent of simplifying the further development of the functions developed to manipulate them. Therefore, we defined a constructor BN as being a list of digits and a Bool dictating the signal
(True: positive, False: negative)
. However, 0 is signless, so we added a constructor (Zero) to correctly represent it.
-
scanner: When the String is in the correct format following this Regular Expression
(+|-)(0|1|2|3|4|5|6|7|8|9)* + (+|-|ε)(0)*
, otherwise an error is shown. The convertion is done firstly by checking the signal and atributting the matching Bool into the BigNumber (True → positive, False → negative) and then converting the numbers into a list of Ints using stringToNumbers() util function. When one or multiple 0's are given, we call the Zero constructor of BigNumber data. It also removes the trailling zeros from the String usingtrimString()
util function. -
output: When the BigNumber is Zero, returns "0", otherwise it checks for the number signal and parses into "+" or "-", followed by the concatenation of the digits previously converted and trimmed with the composed function made by
trimString . numbersToString
functions. -
somaBN: somaBN is composed by the 6 cases that can occur, when performing an addition:
- Both numbers are Zero → returns Zero.
- Only one number is Zero → returns the other number.
- When both numbers (a and b) are none Zero numbers:
- (-a) + (-b) <=> -a - b <=> -(a+b) → returns the sum of a + b, through
somaBNaux()
function, therefore changing its signal to False (meaning a negative number), to conclude the computation. - (+a) + (+b) <=> a + b → returns the sum of a + b, through
somaBNaux()
function, assigning its signal to True (meaning a positive number), to conclude the computation. - (+a) + (-b) <=> a - b → returns the subtraction of a - b, by calling the
subBN()
function with a (positive) and b (positive, opposing the given signal value). - (-a) + (+b) <=> b - a → returns the subtraction of b - a, by calling the
subBN()
function with b (positive) and a (positive, opposing the given signal value).
- (-a) + (-b) <=> -a - b <=> -(a+b) → returns the sum of a + b, through
-
subBN: subBN is composed by the 7 cases that can occur , when performing a subtraction:
- Both numbers are Zero → returns Zero.
- The first number is Zero → returns the second number with the signal negated.
- The second number is Zero → returns the first number.
- When both numbers (a and b) are none Zero numbers:
- (+a) - (+b) <=> a - b → returns the subtraction of a - b, through
subBNaux()
function, and its signal is defined, after cheking the value returned bybiggerBN()
function. - (-a) - (-b) <=> b - a → returns the subtraction of b - a, by calling the
subBN()
function with a (positive) and b (positive). - (+a) - (-b) <=> a + b → returns the addition of a + b, by calling the
somaBN()
function with a signal (positive) and b signal (positive). - (-a) - (+b) <=> -(a + b) → returns the addition of a + b, by calling the
somaBN()
function with a signal (positive) and b signal (positive), followed by the negation of the BigNumber throughnotBN()
function.
- (+a) - (+b) <=> a - b → returns the subtraction of a - b, through
-
mulBN: mulBN calls
mulBNaux()
with both BigNumbers digits list and returns a BigNumber with the result of the opperation with the matching signal. mulBN is composed by the four cases that can occur, when performing a multiplication:- (+a) * (+b) <=> a * b → returns the multiplication of a * b, by calling
mulBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a list of digits, and the matching signal. - (-a) * (-b) <=> a * b, recursively calls
mulBN()
function, and then returns the multiplication of a * b, by callingmulBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a list of digits, and the matching signal. - (+a) * (-b), returns the multiplication of a*b, by calling
mulBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a list of digits, and the matching signal. - -a * b returns the multiplication of a * b, by calling
mulBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a list of digits, and the matching signal.
- (+a) * (+b) <=> a * b → returns the multiplication of a * b, by calling
-
divBN: Like mulBN, divBN calls
divBNaux()
with both numbers digits list and returns a tuple of two BigNumbers with the result from divBNaux(), that is previously converted from ([Int],[Int]) to a tuple of BigNumbers usingdivAuxEmptyToZero()
function. divBN is composed by the four cases that can occur, when performing a divison:- (+a) / (+b) <=> a / b → returns the division of a and b, by calling
divBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a tuple(quotient,remainder) respectively in a list of digits form, and the matching signal. - (-a) / (-b) <=> a / b, recursively calls
divBN()
function, returns the division of a and b, by callingdivBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a tuple(quotient,remainder) respectively in a list of digits form, and the matching signal. - (+a) / (-b), returns the division of a and b, by calling
divBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a tuple(quotient,remainder) respectively in a list of digits form, and the matching signal. - -a / b returns the division of a and b, by calling
divBNaux()
function giving has arguments BigNumber list of digits, consequently returning the result in a tuple(quotient,remainder) respectively in a list of digits form, and the matching signal.
- (+a) / (+b) <=> a / b → returns the division of a and b, by calling
-
somaBNaux: Auxiliary function, for
somaBN()
, that receives has arguments, an Int (increment), [Int] (first list of digits) and [Int] (second list of digits),and returns a [Int]. Recursive function, to add numbers one by one given two list of digits. Three base cases where defined, to define the stopping criteria. This function was developed, taking benefit from last() and init() haskell operations. Performing digit by digit addition from bottom to top, and adding an increment for the next operation if necessary, removing the last element from the list, after the operation is completed successfully, and recursively callingsomaBNaux()
function with the digits list, not containing the last digit. -
subBNaux: Auxiliary function, for
subBN()
, that receives has arguments, an Int (decrement), [Int] (first list of digits) and [Int] (second list of digits),and returns a [Int]. Recursive function, to add numbers one by one given two list of digits. 3 base cases where defined, to define the stopping criteria. This function was developed, taking benefit from last() and init() haskell operations. Performing digit by digit subtraction from bottom to top, and adding an increment for the next operation if necessary, removing the last element from the list, after the operation is completed successfully, and recursively callingsubBNaux()
function with the digits list, not containing the last digit. And the final result, contains no trailling 0's. -
mulBNaux: Auxiliary function, for
mulBN()
, that receives has arguments, an [Int] (first list of digits) and [Int] (second list of digits), and returns a [Int]. Recursive function, to multiplie numbers, by adding to the first list of digits the first list of digits, "second list of digits" times. Two base cases were defined, to define the stopping criteria. -
divBNaux: Auxiliary function, for
divBN()
, that receives has arguments, an [Int] (first list of digits) and [Int] (second list of digits), and returns a tuple([Int],[Int]) respectively the quotient and the remainder. Recursive function, to divide numbers, by subtracting to the first list of digits the second list of digits, until the first list of digits, is smaller than the second list of digits or Zero. Three base cases were defined, to define the stopping criteria.
We used :set +s
in ghci to see the execution time of the Fibonacci module functions for Integrals and for BigNumbers, to observate the differences, we tested the execution for the same 3 values 5 times and made the average of those trials.
Values tested: 15, 25, 30, 50 and 500
Note: the higher values were not tested in the naive strategies, because of the computation time too big. Execution time is in seconds.
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 |
25 | 0.41 | 0.42 | 0.49 | 0.40 | 0.40 | 0.424 |
30 | 4.58 | 5.16 | 5.22 | 5.40 | 4.98 | 5.068 |
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0 | 0 | 0 | 0 | 0 | 0 |
25 | 0 | 0 | 0 | 0 | 0 | 0 |
30 | 0 | 0 | 0 | 0 | 0 | 0 |
50 | 0.01 | 0 | 0 | 0.01 | 0.01 | 0.006 |
500 | 0.06 | 0.02 | 0.02 | 0.02 | 0.02 | 0.028 |
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0 | 0 | 0 | 0 | 0 | 0 |
25 | 0 | 0 | 0 | 0 | 0 | 0 |
30 | 0 | 0 | 0 | 0 | 0 | 0 |
50 | 0.01 | 0 | 0 | 0.01 | 0 | 0.004 |
500 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 |
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0.02 | 0.02 | 0.02 | 0.02 | 0.02 | 0.02 |
25 | 1.71 | 1.69 | 1.86 | 1.72 | 1.67 | 1.73 |
30 | 18.3 | 20.38 | 20.36 | 20.62 | 20.1 | 19.952 |
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0 | 0 | 0 | 0 | 0 | 0 |
25 | 0 | 0 | 0 | 0 | 0 | 0 |
30 | 0 | 0 | 0 | 0 | 0 | 0 |
50 | 0.01 | 0.01 | 0.01 | 0 | 0 | 0.006 |
500 | 0.11 | 0.11 | 0.10 | 0.10 | 0.11 | 0.106 |
1 | 2 | 3 | 4 | 5 | Average | |
---|---|---|---|---|---|---|
15 | 0 | 0 | 0 | 0 | 0 | 0 |
25 | 0 | 0 | 0 | 0 | 0 | 0 |
30 | 0 | 0 | 0 | 0 | 0 | 0 |
50 | 0.01 | 0 | 0 | 0 | 0 | 0.002 |
500 | 0.09 | 0.10 | 0.10 | 0.10 | 0.10 | 0.098 |
-
After all these tests we observed that the execution times of naive functions are greater than the rest of the strategies used. Moreover, the use of the dynamic programming strategies with infinit list seems to be faster than the dynamic programming approach with finite lists.
-
The Fibonacci functions with BigNumber integration are in general slower than the ones implemented with Integers (Int and Integers).