diff --git a/.gitignore b/.gitignore index 5cb2b23..aa4ecbe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,306 @@ a.out .DS_Store **/.DS_Store +# TeX +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + diff --git a/Assignment2/SolutionPA2.hs b/Assignment2/SolutionPA2.hs index c9f427b..eb92de1 100644 --- a/Assignment2/SolutionPA2.hs +++ b/Assignment2/SolutionPA2.hs @@ -136,11 +136,12 @@ myDouble n = foldr (+) 0 [n,n] -- >>> [1,4,3] doubleEveryOther :: [Int] -> [Int] --- I don't like this code. It doesn't feel very Haskell. --- Use list comprehension to get all the indices of xs. --- Then if the index is odd, double the value. --- This will double every other starting with the 2nd digit. -doubleEveryOther xs = [if myMod i 2 == 0 then xs !! i else myDouble (xs!!i) | i <- [0..(length xs) - 1]] +-- Base cases. +doubleEveryOther [] = [] +doubleEveryOther [x] = [x] +-- In the recursive case, just multiply the 2nd element from the start. +-- Then doubleEveryOther on the rest of the list. +doubleEveryOther (x:y:xs) = x : myDouble y : doubleEveryOther xs --mySquare --Write your own my square function using foldr @@ -171,9 +172,8 @@ mySquare n = foldr (*) 1 [n,n] -- 30 sqSum :: [Int] -> Int --- We can use foldr and list comprehension to add evertything in a new --- list where mySquare was applied (map). -sqSum ns = foldr (+) 0 [mySquare x | x <- ns] +-- The lambda function should square and then add to accumulator. +sqSum = foldr (\x acc -> mySquare x + acc) 0 --sumDigits is to add the sum of all the number inside the list that is already turn into single digit (10 pts) @@ -185,11 +185,9 @@ sqSum ns = foldr (+) 0 [mySquare x | x <- ns] -- >>> 10 sumDigits :: [Int] -> Int --- We can use a double list comprehension combined with foldr. --- Take every n from ns. --- Find the digits, and then get every digit x. --- Combine it all into a list and apply foldr. -sumDigits ns = foldr (+) 0 [x | n <- ns, x <- toDigit n] +-- The lambda will take the sum of the list of digits of each element +-- and add it to the accumulator. +sumDigits = foldr (\x acc -> acc + (sumList $ toDigit x)) 0 -- sepConcat will concatenate the defined seperator to a list of string. If the list is empty despite the defined seperator return empty string. -- @@ -203,14 +201,10 @@ sumDigits ns = foldr (+) 0 [x | n <- ns, x <- toDigit n] -- >>> "a#b#c#d#e" sepConcat :: String -> [String] -> String --- Base case 1, when the list is empty. -sepConcat _ [] = [] --- Pattern match a list with only one element. --- When we reach this case, we don't add separator to the end. -sepConcat _ (t:[]) = t --- On the recursive case, we patten match the first element and the rest --- of the list. We then concat it with the separator and recurse. -sepConcat sep (t:ts) = t ++ sep ++ sepConcat sep ts +-- The lambda in foldR will prepend the strings+separator to the accumulator but +-- skip the separator in the beginning so that the string doesn't end in +-- a separator. +sepConcat sep = foldr (\x acc -> x ++ if acc == [] then acc else sep ++ acc) "" -- Part C: Credit Card problem @@ -290,7 +284,7 @@ mergeList axs@( px@(_,x) : xs) ays@( py@(_,y) : ys) -- If x <= y, the first pair ps should go first. -- Then we can merge the rest of xs with all of ays. | x <= y = px : mergeList xs ays --- Otherwise, we can just call mergeList again. +-- Otherwise, we can just call mergeList again and prepend py. | otherwise = py : mergeList axs ys @@ -341,8 +335,6 @@ type BigInt = [Int] clone :: a -> Int -> [a] -- Base case clone _ 0 = [] --- List with just x -clone x 1 = x : [] -- Create a list with [x], then recursive call function. clone x n = x : clone x (n-1) @@ -383,8 +375,6 @@ padZero xs ys | xl > yl = (xs, clonez (xl-yl) ++ ys) -- >>> [] removeZero :: BigInt -> BigInt --- Base case -removeZero [] = [] -- If we can pattern match a zero, recurse with rest of list. removeZero (0:xs) = removeZero xs -- Otherwise just return it. @@ -399,8 +389,22 @@ removeZero xs = xs -- bigAdd [9, 9, 9, 9] [9, 9, 9] -- >>> [1, 0, 9, 9, 8] ---bigAdd :: BigInt -> BigInt -> BigInt -bigAdd _ _ = "not implemented" +-- Takes reverse. +-- Base case, we use n as the carry bit, and so we just leave it at the +-- end. +bigAdd' [] [] n = [n] +-- For recursion, we use mod with the sum to get the digit, +-- and then we recurse with the carry bit being the integer division of +-- 10. +bigAdd' (x:xs) (y:ys) n = (myMod sum 10) : bigAdd' xs ys (div sum 10) + where sum = x+y+n + +bigAdd :: BigInt -> BigInt -> BigInt +-- We use bigAdd' after we zero-pad the incoming bigInts. +-- Since bigAdd' wants reversed, we reverse the lists. We finally have +-- to reverse the list at the end again. +bigAdd xs ys = removeZero $ reverseList $ bigAdd' (reverseList zxs) (reverseList zys) 0 + where (zxs,zys) = padZero xs ys -- `mulByDigit i n` returns the result of multiplying @@ -409,8 +413,20 @@ bigAdd _ _ = "not implemented" -- mulByDigit 9 [9,9,9,9] -- >>> [8,9,9,9,1] ---mulByDigit :: Int -> BigInt -> BigInt -mulByDigit _ _ = "not implemented" +-- Takes reverse. + +-- Base case will just return the carry digits. +digMul' [] _ n = reverseList $ toDigit n +-- For recursion, we use mod with the sum to get the digit, +-- and then we recurse with the carry-over being the integer division of +-- 10, where the sum is the digit in the list * q, then added the +-- carry-over. +digMul' (x:xs) q n = (myMod sum 10) : digMul' xs q (div sum 10) + where sum = (x*q)+n + +-- Just delegate to digMul' after reversing the list. +mulByDigit :: Int -> BigInt -> BigInt +mulByDigit q xs = removeZero $ reverseList $ digMul' (reverseList xs) q 0 -- `bigMul n1 n2` returns the `BigInt` representing the @@ -422,7 +438,27 @@ mulByDigit _ _ = "not implemented" -- bigMul [9,9,9,9,9] [9,9,9,9,9] -- >>> [9,9,9,9,8,0,0,0,0,1] ---bigMul :: BigInt -> BigInt -> BigInt -bigMul _ _ = "not implemented" +-- Long multiply algorithm: +-- For each digit in ys, multiply xs by it. +-- Based on the index, shift the whole thing by the number of powers of +-- 10. +-- Then, just bigSum them all. + +-- Helper function. +-- Like sumList but for bigInts. +-- So megaSum. +megaSum :: [BigInt] -> BigInt +megaSum [] = [0] +megaSum (x:xs) = bigAdd x $ megaSum xs + +bigMul :: BigInt -> BigInt -> BigInt +-- Implementation: use megaSum on a list of bigInts. +-- We use zip with [0..] and ys to get tuples of the index and the +-- value. +-- Then, we can use mulByDigit to multiply xs by the digit of y, then +-- use the index to add the correct number of zeros to the end. +-- Finally the megaSum adds all of the resulting bigInts. +bigMul xs ys = megaSum [mulByDigit q xs ++ clone 0 i | (i,q) <- zip [0..] rys] + where rys = reverseList ys diff --git a/Assignment2/report2.pdf b/Assignment2/report2.pdf new file mode 100644 index 0000000..b6cf9a5 Binary files /dev/null and b/Assignment2/report2.pdf differ diff --git a/Assignment2/report2.tex b/Assignment2/report2.tex new file mode 100644 index 0000000..4d1faeb --- /dev/null +++ b/Assignment2/report2.tex @@ -0,0 +1,421 @@ +%! TeX program = lualatex +\RequirePackage[l2tabu,orthodox]{nag} +\DocumentMetadata{lang=en-US} +\documentclass[a4paper]{scrartcl} +\usepackage{geometry} +%\usepackage{tikz} +%\usepackage{tikz-uml} +\usepackage{hyperref} +%\usepackage{caption} +\usepackage{bookmark} +\usepackage{fontspec} +\usepackage{microtype} +\usepackage{minted} +\hypersetup{ + colorlinks=true, + urlcolor=blue +} + +% Math packages +%\usepackage{amsmath} +%\usepackage{mathtools} +%\usepackage{amsthm} +%\usepackage{thmtools} +%\usepackage{lualatex-math} + +% Unicode Math +\usepackage[warnings-off={mathtools-colon,mathtools-overbracket},math-style=upright]{unicode-math} +\usepackage{newcomputermodern} + +% Use Euler Math fonts and Concrete Roman +%\usepackage{euler-math} +%\setmainfont{cmunorm.otf}[ +% BoldFont=cmunobx.otf, +% ItalicFont=cmunoti.otf, +% BoldItalicFont=cmunobi.otf +%] +\setmonofont{0xProto}[Scale=MatchLowercase] + +\newcommand*{\figref}[2][]{% + \hyperref[{fig:#2}]{% + Figure~\ref*{fig:#2}% + \ifx\\#1\\% + \else + \,#1% + \fi + }% +} + +%\DeclarePairedDelimiter\ceil{\lceil}{\rceil} +%\DeclarePairedDelimiter\floor{\lfloor}{\rfloor} + +%\declaretheorem[within=chapter]{definition} +%\declaretheorem[sibling=definition]{theorem} +%\declaretheorem[sibling=definition]{corollary} +%\declaretheorem[sibling=definition]{principle} + +\usepackage{polyglossia} +%\usepackage[backend=biber]{biblatex} + +\setdefaultlanguage[variant=american,ordinalmonthday=true]{english} + +\day=15 +\month=3 +\year=2024 + +\title{Programming Assignment 2} +\subtitle{CS-420 Spring 2024} +\author{Juan Pablo Zendejas} +\date{\today} + +\begin{document} + +\maketitle +%\listoftheorems[ignoreall,onlynamed={theorem,corollary,principle}] +%\listoftheorems[ignoreall,onlynamed={definition},title={List of Definitions}] +%\tableofcontents +For this programming assignment, I was tasked with creating a variety of +functions for different purposes to practice functional programming +concepts like fold/reduce, recursion, and function composition. In all, +I managed to get a full score on the autograder and I felt that I had +created acceptable solutions that are clean and easy to understand. + +While I didn't work with anyone specifically on this project, I did end +up searching for help online when I felt I had hit a wall. For example, +I wanted to use only one \texttt{foldr} call on my \texttt{sepConcat} +function and I was having trouble creating a lambda function that would +work. I looked at some +StackOverflow\footnote{\url{https://stackoverflow.com/a/44101237}} answers +for some guidance and I +found I was close, but I just had to re-arrange my use of the \texttt{++} +operator to follow the ordering of the \texttt{foldr}. I also found out +about the trick of using \texttt{zip [0..] xs} to get the index of each +element of a list, which was useful for the final problem. + +Now, we will look at each function and my solution. +\section{Part A: Basic Haskell Prelude} +These functions are simple implementations using basic Haskell prelude +functions. + +\subsection{myMod} +The idea is fairly simple. Use \texttt{div} to perform integer division +of x and y. Then, we can multiply it by y and subtract that from x to +get the remainder of the division, which is the modulus operator. +\begin{minted}[frame=single]{haskell} +myMod :: Int -> Int -> Int +myMod x y = x - (y * div x y) +\end{minted} + +\subsection{toDigit} +This function uses recursion. If the input is \le 0, then we just return +an empty list. The recursive step then splits the integer into the first +digit using \texttt{myMod}, and then the rest of the number using +\texttt{div}. Finally, the digit is appended to the end and toDigit is +called recursively to prepend its result. +\begin{minted}[frame=single]{haskell} +toDigit :: Int -> [Int] +toDigit n | n <= 0 = [] + | otherwise = toDigit (div n 10) ++ [myMod n 10] +\end{minted} + +\subsection{reverseList} +Another recursive function. To reverse the list, I use a pattern +matching to split the first element of the list out. Then, just prepend +the reverse of the rest of the list to the first element. +\begin{minted}[frame=single]{haskell} +reverseList :: [a] -> [a] +reverseList [] = [] +reverseList (x:xs) = reverseList xs ++ [x] +\end{minted} + +\subsection{sumList} +Follows a similar strategy. Pattern-matching to split the first element +off, then add it to the sum of the rest of the list. This recursive call +is ended by a base case of 0 for an empty list. +\begin{minted}[frame=single]{haskell} +sumList :: [Int] -> Int +sumList [] = 0 +sumList (x:xs) = sumList xs + x +\end{minted} + +\subsection{toDigitRev} +Just a function composition. +\begin{minted}[frame=single]{haskell} +toDigitRev :: Int -> [Int] +toDigitRev n = reverseList (toDigit n) +\end{minted} + +\section{Part B: Folding Functions} +These functions use the \texttt{foldr} prelude function to perform +operations on a list. + +\subsection{myDouble} +The idea was to use \texttt{foldr} with a list of the parameter twice, +so foldr could add them together. +\begin{minted}[frame=single]{haskell} +myDouble :: Int -> Int +myDouble n = foldr (+) 0 [n,n] +\end{minted} + +\subsection{doubleEveryOther} +This function did not have to use \texttt{foldr}. Here, the idea was to +use recursion. The recursive case would grab the first two elements and +the rest of the list. Then, the 2nd element would be doubled, and the +elements would be appended in order but with the doubleEveryOther +recursive call for the rest of the list. +\begin{minted}[frame=single]{haskell} +doubleEveryOther :: [Int] -> [Int] +doubleEveryOther [] = [] +doubleEveryOther [x] = [x] +doubleEveryOther (x:y:xs) = x : myDouble y : doubleEveryOther xs +\end{minted} + +\subsection{mySquare} +Similar to myDouble, but using multiply instead of adding them. +\begin{minted}[frame=single]{haskell} +mySquare :: Int -> Int +mySquare n = foldr (*) 1 [n,n] +\end{minted} + +\subsection{sqSum} +Here, we use a custom lambda function. In addition, because the last +parameter is the list, we can also use partial function application or +currying to omit the name of the first argument and make the declaration +cleaner. The lambda function just adds the square of x to the +accumulator of \texttt{foldr}. +\begin{minted}[frame=single]{haskell} +sqSum :: [Int] -> Int +sqSum = foldr (\x acc -> mySquare x + acc) 0 +\end{minted} + +\subsection{sumDigits} +This function needs to add the digits of all the numbers in the list. +Here, the custom lambda function uses the previous \texttt{sumList} +and \texttt{toDigit} functions. It adds the sum of the digits of each +element to the accumulator. +\begin{minted}[frame=single]{haskell} +sumDigits :: [Int] -> Int +sumDigits = foldr (\x acc -> acc + (sumList $ toDigit x)) 0 +\end{minted} + +\subsection{sepConcat} +Concatenate a list of strings while interspersing a separator string in +between each string. For this, the custom lambda function will prepend +the string to the accumulator, but only put the separator in between if +there has already been an element added to the accumulator. This way, we +avoid the off-by-one error of appending the separator to the end of the +final string. +\begin{minted}[frame=single,breaklines=true]{haskell} +sepConcat :: String -> [String] -> String +sepConcat sep = foldr (\x acc -> x ++ if acc == [] then acc else sep ++ acc) "" +\end{minted} + +\section{Part C: Credit Card Problem} +This is an application of some of the functions I've written to create a +function that validates a credit card number using the Luhn algorithm. +Double every other digit of the card number starting from the end +(reversed). Take the sum of those digits. If the sum is equal to 0 +mod 10, it is a valid credit card number. +\begin{minted}[frame=single]{haskell} +validate :: Int -> Bool +validate n = myMod (sumDigits (doubleEveryOther (toDigitRev n))) 10 == 0 +\end{minted} + +\section{Part D: Sorting Algorithms} +The task for this section was to implement some sorting algorithms in a +functional manner. + +\subsection{splitHalf} +For the \texttt{splitHalf} function, it needs to split a list into a tuple where +the left and right tuple each have half of the list. To accomplish this, +I first created a generic \texttt{mySplit} function that splits onto an +index. This is accomplished by taking in an index and a pair of lists. +The index is the number of elements from the right pair that should be +moved into the left pair. So, every recursive call will move the first +element from the right list into the end of the left list. Then, mySplit +will be called again with \texttt{n} decremented. The base case, when +\texttt{n = 0}, is to just return the pair. + +Finally, \texttt{splitHalf} just calls the \texttt{mySplit} function +with \texttt{n} equal to half the length of the list. +\begin{minted}[frame=single]{haskell} +mySplit 0 p = p +mySplit n (xs,(y:ys)) = mySplit (n-1) (xs++[y], ys) + +splitHalf :: [a] -> ([a], [a]) +splitHalf xs = mySplit (div l 2) ([], xs) + where l = length xs +\end{minted} + +\subsection{mergeList} +This is where it starts to get complicated. \texttt{mergeList} is a +function that receives two lists of pairs that are sorted by the 2nd +element of the pair, and merges them into one big sorted list. The way I +thought about this problem after talking with the professor is to +consider the first element of each list. Then, I just let the smaller +one go first and recurse. To accomplish this, I used a bunch of pattern +matching and a guarded statement. Here, \texttt{axs} and \texttt{ays} +are the whole lists. Then, \texttt{px} and \texttt{py} are the pairs +that I should prepend. \texttt{xs} and \texttt{ys} are the rest of the +list without the first element. Finally, \texttt{x} and \texttt{y} are +the element the list is sorted by. The guard statement just compares x +and y, and uses that information to prepend the pair with the smaller +sorting value. +\begin{minted}[frame=single]{haskell} +mergeList :: Ord b => [(a, b)] -> [(a, b)] -> [(a, b)] +mergeList [] pys = pys +mergeList pxs [] = pxs + +mergeList axs@( px@(_,x) : xs) ays@( py@(_,y) : ys) + | x <= y = px : mergeList xs ays + | otherwise = py : mergeList axs ys +\end{minted} + +\subsection{mergeSort} +Finally, I got to implement the merge sort algorithm. I first created +some helper functions. \texttt{applyPair} applies a function $f$ with +the parameters given by a tuple. \texttt{applyEachPair} applies a +function $f$ to both elements of a tuple, and returns a new +tuple. Then, \texttt{mergeSort} puts it all together and applies merge +sort to each half of the list; then applies mergeList to the pair of the +sorted halves. The base cases end the recursion of mergeSort, since a +list of 0 or 1 elements is sorted. +\begin{minted}[frame=single]{haskell} +applyPair :: (a->b->c) -> (a,b) -> c +applyPair f (x,y) = f x y + +applyEachPair :: (a->b) -> (a,a) -> (b,b) +applyEachPair f (x, y) = (f x, f y) + +mergeSort :: Ord b => [(a,b)] -> [(a,b)] +mergeSort [] = [] +mergeSort [x] = [x] +mergeSort xs = applyPair mergeList (applyEachPair mergeSort (splitHalf xs)) +\end{minted} + +\section{Part E: BigInt} +Here, I was tasked to work with a new type BigInt which is a list of +integers. + +\subsection{clone} +Clone takes a value and a number, and creates a list with that value +repeated n times. So, a simple recursive solution is to use the cons +operator and call clone again with n decremented. The base case is when +n = 0, which returns and empty list. +\begin{minted}[frame=single]{haskell} +clone :: a -> Int -> [a] +clone _ 0 = [] +clone x n = x : clone x (n-1) +\end{minted} + +\subsection{padZero} +This function will take two \texttt{BigInt}s and return a pair of new +\texttt{BigInt}s that have the same length. Here, I just made a quick +helper function \texttt{clonez} that clones zero n times. Then, I use a +guarded expression to prepend zeros based on the difference of the +lengths. If \texttt{ys} is shorter, it gets \texttt{xl - yl} zeros +prepended, and vice-versa for \texttt{xs}. +\begin{minted}[frame=single]{haskell} +clonez = clone 0 + +padZero :: BigInt -> BigInt -> (BigInt, BigInt) +padZero xs ys | xl > yl = (xs, clonez (xl-yl) ++ ys) + | xl < yl = (clonez (yl-xl) ++ xs, ys) + | otherwise = (xs,ys) + where xl = length xs + yl = length ys +\end{minted} + +\subsection{removeZero} +Kind of like the opposite of \texttt{padZero}. Takes a \texttt{BigInt} +and removes leading zeros that have no value. This is just a simple +recursive pattern matching. If the first element is zero, remove it and +call \texttt{removeZero} again. If we can't pattern match a zero, just +return the list. +\begin{minted}[frame=single]{haskell} +removeZero :: BigInt -> BigInt +removeZero (0:xs) = removeZero xs +removeZero xs = xs +\end{minted} + +\subsection{bigAdd} +This function will add two \texttt{BigInt}s. To implement this, I +delegated to a helper function \texttt{bigAdd'}. The reasoning was so I +could use tail recursion and bring the carry value over each time. + +First, \texttt{bigAdd'} will take two reversed \texttt{BigInt}s, along +with a carry bit. We take the sum of the first integers from the lists, +along with the carry sum. Then, we take the first digit of the sum and +prepend it, and then recursively call \texttt{bigAdd'} with the carry +bit being the sum divided by 10, and the rest of the two lists. +The base case, then, simply takes empty lists and returns the carry bit. + +The actual implementation of \texttt{bigAdd} pads the incoming +\texttt{BigInt}s with zeros, reverses them, and calls \texttt{bigAdd'} +with a carry of 0. Finally, at the end, the result is reversed and +trimmed of zeros. +\begin{minted}[frame=single,breaklines=true]{haskell} +bigAdd' [] [] n = [n] +bigAdd' (x:xs) (y:ys) n = (myMod sum 10) : bigAdd' xs ys (div sum 10) + where sum = x+y+n + +bigAdd :: BigInt -> BigInt -> BigInt +bigAdd xs ys = removeZero $ reverseList $ bigAdd' (reverseList zxs) (reverseList zys) 0 + where (zxs,zys) = padZero xs ys +\end{minted} + +\subsection{mulByDigit} +I followed a similar strategy to \texttt{bigAdd}. Essentially, the +helper function \texttt{digMul'} will take a reversed list, and then +multiply the first digit by some factor q. Then, the carry bit is added, +and the sum is split into the first digit and the other digits. The +first digit is prepended to the recursive call of \texttt{digMul'} with +the rest of the list and the same factor. +\begin{minted}[frame=single]{haskell} +digMul' [] _ n = reverseList $ toDigit n +digMul' (x:xs) q n = (myMod sum 10) : digMul' xs q (div sum 10) + where sum = (x*q)+n + +mulByDigit :: Int -> BigInt -> BigInt +mulByDigit q xs = removeZero $ reverseList $ digMul' (reverseList xs) q 0 +\end{minted} + +\subsection{bigMul} +Finally, the \texttt{bigMul} function will take two \texttt{BigInt}s and +multiply them together. This function follows a basic long +multiplication algorithm. That is, I take each digit from the second +number and multiply it digit-by-digit to the first number. Then, based +on its position, I add zeros to the end of the result. To implement this +in Haskell, I used a helper function \texttt{megaSum} that recursively +adds a list of \texttt{BigInt}s using the previously created +\texttt{bigAdd} function. To create the list of numbers to add, I had to +reverse the 2nd list \texttt{ys} into \texttt{rys}. Then, by using the +\texttt{zip} method, I combined each element of \texttt{rys} with its +index. Since the list was reversed, this index is the number of zeros to +add onto the sum. Each element of the list passed to \texttt{megaSum} is +that digit of \texttt{ys}, \texttt{q}, passed to \texttt{mulByDigit} +with the whole \texttt{BigInt} \texttt{xs}. Finally, zeros are appended +to the end based on the index. The sum of all of these products is the +final result. +\begin{minted}[frame=single,breaklines=true]{haskell} +megaSum :: [BigInt] -> BigInt +megaSum [] = [0] +megaSum (x:xs) = bigAdd x $ megaSum xs + +bigMul :: BigInt -> BigInt -> BigInt +bigMul xs ys = megaSum [mulByDigit q xs ++ clone 0 i | (i,q) <- zip [0..] rys] + where rys = reverseList ys +\end{minted} + +\section{Results} +The Autograder on EDORAS gave me full marks. + +\begin{Verbatim}[label={Results},frame=single] +--------------------------------------------------------------------------- + Results: [90.00/90.00] | [100.00%] +--------------------------------------------------------------------------- +\end{Verbatim} + +% \inputminted[label={SolutionPA2.hs},breaklines=true]{haskell}{SolutionPA2.hs} + +\end{document} diff --git a/Week8/lecture1.hs b/Week8/lecture1.hs new file mode 100644 index 0000000..2690d13 --- /dev/null +++ b/Week8/lecture1.hs @@ -0,0 +1,9 @@ +fact :: [Int] +fact = scanl (*) 1 [2..] + +fibs :: [Int] +-- to understand recursion one must first understand recursion... +-- F_n+2 = 1 + sum(F_1 .. F_n) +fibs = 1 : scanl (+) 1 fibs + +