updated
authorChristian Urban <urbanc@in.tum.de>
Wed, 30 Oct 2019 11:28:44 +0000
changeset 284 9a04eb6a2291
parent 283 ef5f62bf5987
child 285 bd9d142d2cd8
updated
cws/cw01.pdf
cws/cw01.tex
cws/cw02.pdf
cws/cw02.tex
cws/cw03.pdf
cws/cw03.tex
cws/cw04.pdf
cws/cw04.tex
marking1/compare
marking1/drumb_test.sh
marking1/tails
marking2/danube.scala
marking2/danube_test.sh
marking2/danube_test1.scala
marking2/danube_test2.scala
marking2/danube_test2.sh
marking2/danube_test3.scala
marking2/danube_test4.scala
marking2/danube_test5.scala
marking2/danube_test6.scala
misc/compare
misc/tails
solutions2/danube.scala
templates2/danube.jar
templates2/danube.scala
testing1/drumb_test.sh
testing1/mark
testing1/mark2
testing2/danube.scala
testing2/danube_test.sh
testing2/danube_test1.scala
testing2/danube_test2.scala
testing2/danube_test3.scala
testing2/danube_test4.scala
testing2/danube_test5.scala
testing2/danube_test6.scala
testing3/knight1.scala
testing3/knight1_test.sh
testing3/knight_test1.scala
testing3/knight_test2.scala
testing3/knight_test3.scala
Binary file cws/cw01.pdf has changed
--- a/cws/cw01.tex	Tue Oct 29 23:56:13 2019 +0000
+++ b/cws/cw01.tex	Wed Oct 30 11:28:44 2019 +0000
@@ -14,12 +14,11 @@
 
  
 \noindent
-This assignment is about Scala and worth 10\%. The preliminary
-part is due on \cwSIX{} at 4pm, and the core part on \cwSIXa{}
-at 4pm. You are asked to implement two programs about list
-processing and recursion. The core part is more advanced and might
-include material you have not yet seen in the first lecture.
-\bigskip
+This assignment is about Scala. You are asked to implement two programs
+about list processing and recursion. The preliminary part (3\%) is due
+on \cwSIX{} at 4pm, and the core part on \cwSIXa{} at 4pm.  The core
+part is more advanced and might include material you have not yet seen
+in the first lecture.\bigskip
  
 \IMPORTANT{}
 
Binary file cws/cw02.pdf has changed
--- a/cws/cw02.tex	Tue Oct 29 23:56:13 2019 +0000
+++ b/cws/cw02.tex	Wed Oct 30 11:28:44 2019 +0000
@@ -14,12 +14,11 @@
 \mbox{}\hfill\textit{ --- Frederick P.~Brooks (author of The Mythical Man-Month)}\bigskip\medskip
 
 \noindent
-This coursework is worth 10\%. The basic part is due
-on 22 November at 11pm; the main part is due on 20
-December at 11pm. You are asked to implement Scala programs for
-measuring similarity in texts, and for recommending movies
-according to a ratings list.  Note the second part might include
-material you have not yet seen in the first two lectures. \bigskip
+You are asked to implement Scala programs for measuring similarity in
+texts, and for recommending movies according to a ratings list. The
+preliminary part~(4\%) is due on \cwSEVEN{} at 4pm; the core part is due
+on \cwSEVENa{} at 4pm.   Note the core part might include material you
+have not yet seen in the first two lectures. \bigskip
 
 \IMPORTANT{}
 
@@ -84,7 +83,7 @@
 
 
 \newpage
-\subsection*{Basic Part (4 Marks, file docdiff.scala)}
+\subsection*{Preliminary Part (4 Marks, file docdiff.scala)}
 
 It seems source code plagiarism---stealing and submitting someone
 else's code---is a serious problem at other
@@ -168,7 +167,7 @@
 \end{itemize}\bigskip
 
 
-\subsection*{Main Part (2 Marks, file danube.scala)}
+\subsection*{Core Part (6 Marks, file danube.scala)}
 
 You are creating Danube.co.uk which you hope will be the next big thing
 in online movie renting. You know that you can save money by
Binary file cws/cw03.pdf has changed
--- a/cws/cw03.tex	Tue Oct 29 23:56:13 2019 +0000
+++ b/cws/cw03.tex	Wed Oct 30 11:28:44 2019 +0000
@@ -1,3 +1,4 @@
+% !TEX program = xelatex
 \documentclass{article}
 \usepackage{chessboard}
 \usepackage[LSBC4,T1]{fontenc}
@@ -22,16 +23,15 @@
 \mbox{}\hfill\textit{``The problem with object-oriented languages is they’ve got all this implicit,}\\
 \mbox{}\hfill\textit{environment that they carry around with them. You wanted a banana but}\\
 \mbox{}\hfill\textit{what you got was a gorilla holding the banana and the entire jungle.''}\smallskip\\
-\mbox{}\hfill\textit{ --- Joe Armstrong (creator of the Erlang programming language)}\bigskip
+\mbox{}\hfill\textit{ --- Joe Armstrong (creator of the Erlang programming language)}\medskip\bigskip
 
 \noindent
-This coursework is worth 10\%. It is about searching and
-backtracking. The first part is due on 29 November at 11pm; the
-second, more advanced part, is due on 20 December at 11pm. You are
-asked to implement Scala programs that solve various versions of the
-\textit{Knight's Tour Problem} on a chessboard. Note the second, more
-advanced, part might include material you have not yet seen in the
-first three lectures. \bigskip
+This coursework is about searching and backtracking. You are asked to
+implement Scala programs that solve various versions of the
+\textit{Knight's Tour Problem} on a chessboard. The preliminary part is
+due on  \cwEIGHT{} at 4pm; the core part is due on \cwEIGHTa{} at 4pm.
+Note the core, more advanced, part might include material you have not
+yet seen in the first three lectures. \bigskip
 
 \IMPORTANT{}
 Also note that the running time of each part will be restricted to a
Binary file cws/cw04.pdf has changed
--- a/cws/cw04.tex	Tue Oct 29 23:56:13 2019 +0000
+++ b/cws/cw04.tex	Wed Oct 30 11:28:44 2019 +0000
@@ -99,21 +99,22 @@
 \mbox{}\hfill\textit{``[Google’s MapReduce] abstraction is inspired by the}\\
 \mbox{}\hfill\textit{map and reduce primitives present in Lisp and many}\\
 \mbox{}\hfill\textit{other functional language.''}\smallskip\\
-\mbox{}\hfill\textit{ --- Dean and Ghemawat, who designed this concept at Google}\bigskip
+\mbox{}\hfill\textit{ --- Dean and Ghemawat, who designed this concept at Google}
+\bigskip\medskip
 
 \noindent
-This coursework is worth 10\%. It is about a regular expression
-matcher and the shunting yard algorithm by Dijkstra. The first part is
-due on \cwNINE{} at 11pm; the second, more advanced part, is due on
-\cwNINEa{} at 11pm. In the first part, you are asked to implement a
-regular expression matcher based on derivatives of regular
-expressions. The background is that ``out-of-the-box'' regular
-expression matching in mainstream languages like Java, JavaScript and
-Python can sometimes be excruciatingly slow. You are supposed to implement
-an regular expression matcher that is much, much faster. The advanced part is
-about the shunting yard algorithm that transforms the usual infix
-notation of arithmetic expressions into the postfix notation, which is
-for example used in compilers.\bigskip
+This coursework is about the shunting yard algorithm by Dijkstra and a
+regular expression matcher by Brzozowski. The preliminary part is due on
+\cwNINE{} at 4pm; the core, more advanced part, is due on \cwNINEa{}
+at 4pm. The preliminary part is about the shunting yard algorithm that
+transforms the usual infix notation of arithmetic expressions into the
+postfix notation, which is for example used in compilers. In the core
+part, you are asked to implement a regular expression matcher based on
+derivatives of regular expressions. The background is that
+``out-of-the-box'' regular expression matching in mainstream languages
+like Java, JavaScript and Python can sometimes be excruciatingly slow.
+You are supposed to implement an regular expression matcher that is
+much, much faster. \bigskip
 
 \IMPORTANT{}
 
@@ -159,7 +160,116 @@
 \end{lstlisting}%$
 
 
-\subsection*{Part 1 (6 Marks)}
+\subsection*{Preliminary Part (4 Marks)}
+
+The \emph{Shunting Yard Algorithm} has been developed by Edsger Dijkstra,
+an influential computer scientist who developed many well-known
+algorithms. This algorithm transforms the usual infix notation of
+arithmetic expressions into the postfix notation, sometimes also
+called reverse Polish notation.
+
+Why on Earth do people use the postfix notation? It is much more
+convenient to work with the usual infix notation for arithmetic
+expressions. Most modern calculators (as opposed to the ones used 20
+years ago) understand infix notation. So why on Earth? \ldots{}Well,
+many computers under the hood, even nowadays, use postfix notation
+extensively. For example if you give to the Java compiler the
+expression $1 + ((2 * 3) + (4 - 3))$, it will generate the Java Byte
+code
+
+\begin{lstlisting}[language=JVMIS,numbers=none]
+ldc 1 
+ldc 2 
+ldc 3 
+imul 
+ldc 4 
+ldc 3 
+isub 
+iadd 
+iadd
+\end{lstlisting}
+
+\noindent
+where the command \texttt{ldc} loads a constant onto the stack, and \texttt{imul},
+\texttt{isub} and \texttt{iadd} are commands acting on the stack. Clearly this
+is the arithmetic expression in postfix notation.\bigskip
+
+\noindent
+The shunting yard algorithm processes an input token list using an
+operator stack and an output list. The input consists of numbers,
+operators ($+$, $-$, $*$, $/$) and parentheses, and for the purpose of
+the assignment we assume the input is always a well-formed expression
+in infix notation.  The calculation in the shunting yard algorithm uses
+information about the
+precedences of the operators (given in the template file). The
+algorithm processes the input token list as follows:
+
+\begin{itemize}
+\item If there is a number as input token, then this token is
+  transferred directly to the output list. Then the rest of the input is
+  processed.
+\item If there is an operator as input token, then you need to check
+  what is on top of the operator stack. If there are operators with
+  a higher or equal precedence, these operators are first popped off
+  from the stack and moved to the output list. Then the operator from the input
+  is pushed onto the stack and the rest of the input is processed.
+\item If the input is a left-parenthesis, you push it on to the stack
+  and continue processing the input.
+\item If the input is a right-parenthesis, then you pop off all operators
+  from the stack to the output list until you reach the left-parenthesis.
+  Then you discharge the $($ and $)$ from the input and stack, and continue
+  processing the input list.
+\item If the input is empty, then you move all remaining operators
+  from the stack to the output list.  
+\end{itemize}  
+
+\subsubsection*{Tasks (file postfix.scala)}
+
+\begin{itemize}
+\item[(1)] Implement the shunting yard algorithm described above. The
+  function, called \texttt{syard}, takes a list of tokens as first
+  argument. The second and third arguments are the stack and output
+  list represented as Scala lists. The most convenient way to
+  implement this algorithm is to analyse what the input list, stack
+  and output list look like in each step using pattern-matching. The
+  algorithm transforms for example the input
+
+  \[
+  \texttt{List(3, +, 4, *, (, 2, -, 1, ))}
+  \]
+
+  into the postfix output
+
+  \[
+  \texttt{List(3, 4, 2, 1, -, *, +)}
+  \]  
+  
+  You can assume the input list is always a  list representing
+  a well-formed infix arithmetic expression.\hfill[1 Mark]
+
+\item[(2)] Implement a compute function that takes a postfix expression
+  as argument and evaluates it generating an integer as result. It uses a
+  stack to evaluate the postfix expression. The operators $+$, $-$, $*$
+  are as usual; $/$ is division on integers, for example $7 / 3 = 2$.
+  \hfill[1 Mark]
+\end{itemize}
+
+\subsubsection*{Task (file postfix2.scala)}
+
+\begin{itemize}
+\item[(3)] Extend the code in (7) and (8) to include the power
+  operator.  This requires proper account of associativity of
+  the operators. The power operator is right-associative, whereas the
+  other operators are left-associative.  Left-associative operators
+  are popped off if the precedence is bigger or equal, while
+  right-associative operators are only popped off if the precedence is
+  bigger. The compute function in this task should use
+  \texttt{Long}s, rather than \texttt{Int}s.\hfill[2 Marks]
+\end{itemize}
+
+
+
+\subsection*{Core Part (6 Marks)}
 
 The task is to implement a regular expression matcher that is based on
 derivatives of regular expressions. Most of the functions are defined by
@@ -220,7 +330,7 @@
 
 
 \begin{itemize}
-\item[(1)] Implement a function, called \textit{nullable}, by
+\item[(5)] Implement a function, called \textit{nullable}, by
   recursion over regular expressions. This function tests whether a
   regular expression can match the empty string. This means given a
   regular expression it either returns true or false. The function
@@ -238,7 +348,7 @@
 \end{tabular}
 \end{center}~\hfill[1 Mark]
 
-\item[(2)] Implement a function, called \textit{der}, by recursion over
+\item[(6)] Implement a function, called \textit{der}, by recursion over
   regular expressions. It takes a character and a regular expression
   as arguments and calculates the derivative of a xregular expression according
   to the rules:
@@ -294,7 +404,7 @@
 Note, the last derivative can match the empty string, that is it is \textit{nullable}.\\
 \mbox{}\hfill\mbox{[1 Mark]}
 
-\item[(3)] Implement the function \textit{simp}, which recursively
+\item[(7)] Implement the function \textit{simp}, which recursively
   traverses a regular expression, and on the way up simplifies every
   regular expression on the left (see below) to the regular expression
   on the right, except it does not simplify inside ${}^*$-regular
@@ -330,7 +440,7 @@
   simplification in an inside-out fashion, it is always clear which
   simplification should be applied next.\hfill[1 Mark]
 
-\item[(4)] Implement two functions: The first, called \textit{ders},
+\item[(8)] Implement two functions: The first, called \textit{ders},
   takes a list of characters and a regular expression as arguments, and
   builds the derivative w.r.t.~the list as follows:
 
@@ -353,7 +463,7 @@
 true for the regular expression $(a\cdot b)\cdot c$ and the string
 $abc$, but false if you give it the string $ab$. \hfill[1 Mark]
 
-\item[(5)] Implement a function, called \textit{size}, by recursion
+\item[(9)] Implement a function, called \textit{size}, by recursion
   over regular expressions. If a regular expression is seen as a tree,
   then \textit{size} should return the number of nodes in such a
   tree. Therefore this function is defined as follows:
@@ -493,112 +603,6 @@
 \end{center}
 \newpage
 
-\subsection*{Part 2 (4 Marks)}
-
-The \emph{Shunting Yard Algorithm} has been developed by Edsger Dijkstra,
-an influential computer scientist who developed many well-known
-algorithms. This algorithm transforms the usual infix notation of
-arithmetic expressions into the postfix notation, sometimes also
-called reverse Polish notation.
-
-Why on Earth do people use the postfix notation? It is much more
-convenient to work with the usual infix notation for arithmetic
-expressions. Most modern calculators (as opposed to the ones used 20
-years ago) understand infix notation. So why on Earth? \ldots{}Well,
-many computers under the hood, even nowadays, use postfix notation
-extensively. For example if you give to the Java compiler the
-expression $1 + ((2 * 3) + (4 - 3))$, it will generate the Java Byte
-code
-
-\begin{lstlisting}[language=JVMIS,numbers=none]
-ldc 1 
-ldc 2 
-ldc 3 
-imul 
-ldc 4 
-ldc 3 
-isub 
-iadd 
-iadd
-\end{lstlisting}
-
-\noindent
-where the command \texttt{ldc} loads a constant onto the stack, and \texttt{imul},
-\texttt{isub} and \texttt{iadd} are commands acting on the stack. Clearly this
-is the arithmetic expression in postfix notation.\bigskip
-
-\noindent
-The shunting yard algorithm processes an input token list using an
-operator stack and an output list. The input consists of numbers,
-operators ($+$, $-$, $*$, $/$) and parentheses, and for the purpose of
-the assignment we assume the input is always a well-formed expression
-in infix notation.  The calculation in the shunting yard algorithm uses
-information about the
-precedences of the operators (given in the template file). The
-algorithm processes the input token list as follows:
-
-\begin{itemize}
-\item If there is a number as input token, then this token is
-  transferred directly to the output list. Then the rest of the input is
-  processed.
-\item If there is an operator as input token, then you need to check
-  what is on top of the operator stack. If there are operators with
-  a higher or equal precedence, these operators are first popped off
-  from the stack and moved to the output list. Then the operator from the input
-  is pushed onto the stack and the rest of the input is processed.
-\item If the input is a left-parenthesis, you push it on to the stack
-  and continue processing the input.
-\item If the input is a right-parenthesis, then you pop off all operators
-  from the stack to the output list until you reach the left-parenthesis.
-  Then you discharge the $($ and $)$ from the input and stack, and continue
-  processing the input list.
-\item If the input is empty, then you move all remaining operators
-  from the stack to the output list.  
-\end{itemize}  
-
-\subsubsection*{Tasks (file postfix.scala)}
-
-\begin{itemize}
-\item[(7)] Implement the shunting yard algorithm described above. The
-  function, called \texttt{syard}, takes a list of tokens as first
-  argument. The second and third arguments are the stack and output
-  list represented as Scala lists. The most convenient way to
-  implement this algorithm is to analyse what the input list, stack
-  and output list look like in each step using pattern-matching. The
-  algorithm transforms for example the input
-
-  \[
-  \texttt{List(3, +, 4, *, (, 2, -, 1, ))}
-  \]
-
-  into the postfix output
-
-  \[
-  \texttt{List(3, 4, 2, 1, -, *, +)}
-  \]  
-  
-  You can assume the input list is always a  list representing
-  a well-formed infix arithmetic expression.\hfill[1 Mark]
-
-\item[(8)] Implement a compute function that takes a postfix expression
-  as argument and evaluates it generating an integer as result. It uses a
-  stack to evaluate the postfix expression. The operators $+$, $-$, $*$
-  are as usual; $/$ is division on integers, for example $7 / 3 = 2$.
-  \hfill[1 Mark]
-\end{itemize}
-
-\subsubsection*{Task (file postfix2.scala)}
-
-\begin{itemize}
-\item[(9)] Extend the code in (7) and (8) to include the power
-  operator.  This requires proper account of associativity of
-  the operators. The power operator is right-associative, whereas the
-  other operators are left-associative.  Left-associative operators
-  are popped off if the precedence is bigger or equal, while
-  right-associative operators are only popped off if the precedence is
-  bigger. The compute function in this task should use
-  \texttt{Long}s, rather than \texttt{Int}s.\hfill[2 Marks]
-\end{itemize}
 
 
 
--- a/marking1/compare	Tue Oct 29 23:56:13 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#!/bin/sh
-###set -e
-
-trap "exit" INT
-
-files=${1:-assignment20187-*}
-
-for sd in $files; do
-  cd $sd
-  #cp output output1
-  cmp -s output output1 > /dev/null
-  if [ $? -eq 1 ]; then
-      echo $sd + "is different"
-  fi
-  cd ..
-done
-
-
--- a/marking1/drumb_test.sh	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking1/drumb_test.sh	Wed Oct 30 11:28:44 2019 +0000
@@ -160,7 +160,7 @@
   echo -e "" | tee -a $out
   echo -e "  get_deltas(get_prices(List(\"BIDU\"), 2004 to 2008)) == " | tee -a $out
   echo -e "    List(List(None), List(None),                          " | tee -a $out
-  echo -e "         List(Some(0.9277165354330709)), List(Some(2.119679764725104)))) " | tee -a $out
+  echo -e "         List(Some(0.9277165354330709)), List(Some(2.119679764725104)))" | tee -a $out
   
   if (scala_assert "drumb.scala" "drumb_test5.scala") 
   then
--- a/marking1/tails	Tue Oct 29 23:56:13 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-#!/bin/sh
-###set -e
-
-trap "exit" INT
-
-files=${1:-assignment20177-*}
-
-for sd in $files; do
-  cd $sd
-  #echo $sd
-  tail -1 output
-  cd ..
-done
-
-
--- a/marking2/danube.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -5,7 +5,7 @@
 import io.Source
 import scala.util._
 
-//object CW7b { // for purposes of generating a jar
+object CW7b { // for purposes of generating a jar
 
 // (1) Implement the function get_csv_url which takes an url-string
 //     as argument and requests the corresponding file. The two urls
@@ -118,7 +118,7 @@
 def suggestions(recs: Map[String, List[String]], 
                     mov_name: String) : List[String] = {
   val favs = favourites(recs, mov_name).flatten
-  val favs_counted = favs.groupBy(identity).mapValues(_.size).toList
+  val favs_counted = favs.groupBy(identity).view.mapValues(_.size).toList
   val favs_sorted = favs_counted.sortBy(_._2).reverse
   favs_sorted.map(_._1)
 }
@@ -170,11 +170,11 @@
 //}
 
 // helper functions
-//List().take(2
+//List().take(2)
 //List(1).take(2)
 //List(1,2).take(2)
 //List(1,2,3).take(2)
 
-//}
+}
 
 
--- a/marking2/danube_test.sh	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test.sh	Wed Oct 30 11:28:44 2019 +0000
@@ -6,15 +6,21 @@
 
 out=${1:-output}
 
-# read marks for CW7 part 1
-marks=$(( `tail -1 $out` ))
+
 
-echo $marks
+echo -e  "" > $out
 
-echo "" >> $out
+echo -e  "Below is the feedback and provisional marks for your submission" >> $out
+echo -e  "for core assignment 7.  Please note all marks are provisional until" >> $out
+echo -e  "ratified by the assessment board -- this is not an official" >> $out
+echo -e  "results transcript." >> $out
+echo -e  "" >> $out
 
-echo "Below is the feedback for your submission danube.scala" >> $out
-echo "" >> $out
+echo -e  "Below is the feedback for your submission danube.scala" >> $out
+echo -e  "" >> $out
+
+# marks for core CW7
+marks=$(( 0 ))
 
 
 # compilation tests
@@ -26,7 +32,7 @@
 # functional tests
 
 function scala_assert {
-  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" -- "$2" -e "" 2> /dev/null 1> /dev/null)
+  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" -- "$2" 2> /dev/null 1> /dev/null)
 }
 
 # purity test
@@ -38,14 +44,14 @@
 
 # var, .par return, ListBuffer test
 #
-echo "danube.scala does not contain vars, returns etc?" | tee -a $out
+echo -e  "danube.scala does not contain vars, returns etc?" | tee -a $out
 
 if (scala_vars danube.scala)
 then
-  echo "  --> test failed" | tee -a $out
+  echo -e  "  --> test failed" | tee -a $out
   tsts0=$(( 1 ))  
 else
-  echo "  --> success" | tee -a $out
+  echo -e  "  --> success" | tee -a $out
   tsts0=$(( 0 )) 
 fi
 
@@ -54,14 +60,14 @@
 
 if  [ $tsts0 -eq 0 ]
 then 
-  echo "danube.scala runs?" | tee -a $out
+  echo -e  "danube.scala runs?" | tee -a $out
 
   if (scala_compile danube.scala)
   then
-    echo "  --> success" | tee -a $out
+    echo -e  "  --> success" | tee -a $out
     tsts=$(( 0 ))
   else
-    echo "  --> scala did not run danube.scala" | tee -a $out
+    echo -e  "  --> SCALA DID NOT RUN danube.scala" | tee -a $out
     tsts=$(( 1 )) 
   fi
 else
@@ -72,16 +78,16 @@
 
 if [ $tsts -eq 0 ]
 then
-  echo "danube.scala tests:" | tee -a $out
-  echo "  val movies_url = \"\"\"https://nms.kcl.ac.uk/christian.urban/movies.csv\"\"\"" | tee -a $out
-  echo "  get_csv_url(movies_url).length == 9742" | tee -a $out
+  echo -e  "danube.scala tests:" | tee -a $out
+  echo -e  "  val movies_url = \"\"\"https://nms.kcl.ac.uk/christian.urban/movies.csv\"\"\"" | tee -a $out
+  echo -e  "  get_csv_url(movies_url).length == 9742" | tee -a $out
 
   if (scala_assert "danube.scala" "danube_test1.scala")
   then
-      echo "  --> success" | tee -a $out
+      echo -e  "  --> success" | tee -a $out
       marks=$(( marks + 1 ))
   else
-    echo "  --> one of the tests failed" | tee -a $out
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
   fi
 fi
 
@@ -89,25 +95,105 @@
 
 if [ $tsts -eq 0 ]
 then
-  echo "  val good_ratings = process_ratings(ratings)" | tee -a $out
-  echo "  val movie_names = process_movies(movies)" | tee -a $out  
-  echo "  " | tee -a $out
-  echo "  good_ratings.length == 48580 " | tee -a $out
-  echo "  movie_names.length == 9742 " | tee -a $out
+  echo -e  "  val good_ratings = process_ratings(ratings)" | tee -a $out
+  echo -e  "  val movie_names = process_movies(movies)" | tee -a $out  
+  echo -e  "  " | tee -a $out
+  echo -e  "  good_ratings.length == 48580 " | tee -a $out
+  echo -e  "  movie_names.length == 9742 " | tee -a $out
 
   if (scala_assert "danube.scala" "danube_test2.scala") 
   then
-      echo "  --> success" | tee -a $out
+      echo -e  "  --> success" | tee -a $out
+      marks=$(( marks + 1 ))
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
+  fi
+fi
+
+### danube groupById test
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val ls1 = List((\"1\", \"a\"), (\"2\", \"a\"), (\"1\", \"c\"), (\"2\", \"a\"), (\"1\", \"c\"))" | tee -a $out
+  echo -e  "  val ls2 = List((\"1\", \"a\"), (\"1\", \"b\"), (\"2\", \"x\"), (\"3\", \"a\"), (\"2\", \"y\"), (\"3\", \"c\"))" | tee -a $out
+  echo -e  "  groupById(ls1, Map()) == Map(1 -> List(c, c, a), 2 -> List(a, a))" | tee -a $out
+  echo -e  "  groupById(ls2, Map()) == Map(1 -> List(b, a), 2 -> List(x, y), 3 -> List(c, a))" | tee -a $out
+  echo -e  "      where the order in the lists is unimportant" | tee -a $out
+  echo -e  "  val ls3 = (1 to 1000).map(_.toString).toList" | tee -a $out
+  echo -e  "  val ls4 = ls3 zip ls3.tail" | tee -a $out
+  echo -e  "  val ls5 = ls4 ::: ls4.reverse" | tee -a $out
+  echo -e  "  groupById(ls5, Map()) == Map(1 -> List(2,2), 2 -> List(3,3), ....)" | tee -a $out
+
+  if (scala_assert "danube.scala" "danube_test3.scala")
+  then
+      echo -e  -e "  --> success" | tee -a $out
       marks=$(( marks + 1 ))
   else
-    echo "  --> one of the tests failed" | tee -a $out
+      echo -e  -e "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
+  fi
+fi
+
+### danube favourites tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val good_ratings = process_ratings(ratings)" | tee -a $out
+  echo -e  "  val ratings_map = groupById(good_ratings, Map())" | tee -a $out
+  echo -e  "  favourites(ratings_map, \"912\").length  == 80 " | tee -a $out
+  echo -e  "  favourites(ratings_map, \"858\").length  == 158 " | tee -a $out
+  echo -e  "  favourites(ratings_map, \"260\").length  == 201 " | tee -a $out  
+
+  if (scala_assert "danube.scala" "danube_test4.scala") 
+  then
+    echo -e  "  --> success" | tee -a $out
+    marks=$(( marks + 1 ))
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
+  fi
+fi
+
+### danube suggestions tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val good_ratings = process_ratings(ratings)" | tee -a $out
+  echo -e  "  val ratings_map = groupById(good_ratings, Map())" | tee -a $out
+  echo -e  "  suggestions(ratings_map, \"912\").length  == 4110 " | tee -a $out
+  echo -e  "  suggestions(ratings_map, \"858\").length  == 4883 " | tee -a $out
+  echo -e  "  suggestions(ratings_map, \"260\").length  == 4970 " | tee -a $out  
+
+  if (scala_assert "danube.scala" "danube_test5.scala") 
+  then
+    echo -e  "  --> success" | tee -a $out
+    marks=$(( marks + 1 ))
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
+  fi
+fi
+
+### danube recommendation tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  recommendations(ratings_map, movies_map, \"1\").length  == 2 " | tee -a $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"2\").length  == 2 " | tee -a $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"3\").length  == 2 " | tee -a $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"4\").length  == 0 " | tee -a $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"5\").length  == 2 " | tee -a $out
+
+  if (scala_assert "danube.scala" "danube_test6.scala") 
+  then
+    echo -e  "  --> success" | tee -a $out
+    marks=$(( marks + 1 ))
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" | tee -a $out
   fi
 fi
 
 
 
 ## final marks
-echo "Overall mark for CW 7, Part 1 + 2" | tee -a $out
-echo "$marks" | tee -a $out
+echo -e  "Overall mark for CW 7 Core Part" | tee -a $out
+echo -e  "$marks" | tee -a $out
 
 
--- a/marking2/danube_test1.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test1.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,6 +1,8 @@
 
-//val urban_mov_url = """https://nms.kcl.ac.uk/christian.urban/movies.csv"""
-val urban_mov_url = """http://localhost:8000/movies.csv"""
+import CW7b._
+
+val urban_mov_url = """https://nms.kcl.ac.uk/christian.urban/movies.csv"""
+//val urban_mov_url = """http://localhost:8000/movies.csv"""
 
 assert(get_csv_url(urban_mov_url).length == 9742)
 
--- a/marking2/danube_test2.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test2.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -2,6 +2,8 @@
 import io.Source
 import scala.util._
 
+import CW7b._
+
 def urban_get_csv_file(name: String) : List[String] = {
   val csv = Source.fromFile(name)
   csv.mkString.split("\n").toList.drop(1)
--- a/marking2/danube_test2.sh	Tue Oct 29 23:56:13 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-#!/bin/bash
-
-# to make the script fail safely
-set -euo pipefail
-
-
-out=${1:-output}
-
-echo "" > $out
-
-echo "Below is the feedback and provisional marks for your submission" >> $out
-echo "for assignment 7 Part 3.  Please note all marks are provisional until" >> $out
-echo "ratified by the assessment board -- this is not an official" >> $out
-echo "results transcript." >> $out
-echo "" >> $out
-
-# marks for CW7 part 3
-marks=$(( 0 ))
-
-
-# compilation tests
-
-function scala_compile {
-  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -nc "$1" 2>> $out 1>> $out) 
-}
-
-# functional tests
-
-function scala_assert {
-  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -nc -i "$1" "$2" -e "" 2> /dev/null 1> /dev/null)
-}
-
-# purity test
-
-function scala_vars {
-   (egrep '\bvar\b|\breturn\b|\.par|ListBuffer|mutable|new Array' "$1" 2> /dev/null 1> /dev/null)
-}
-
-
-# var, .par return, ListBuffer test
-#
-echo "danube.scala does not contain vars, returns etc?" | tee -a $out  
-
-if (scala_vars danube.scala)
-then
-  echo "  --> test failed" | tee -a $out  
-  tsts0=$(( 1 ))
-else
-  echo -e "  --> success" | tee -a $out  
-  tsts0=$(( 0 )) 
-fi
-
-
-### compilation test
-
-
-if  [ $tsts0 -eq 0 ]
-then 
-  echo "danube.scala runs?" | tee -a $out  
-
-  if (scala_compile danube.scala)
-  then
-    echo -e "  --> success" | tee -a $out  
-    tsts=$(( 0 ))
-  else
-    echo "  --> scala danube.scala did not run successfully" | tee -a $out  
-    tsts=$(( 1 )) 
-  fi
-else
-  tsts=$(( 1 ))     
-fi
-
-### danube groupById test
-
-if [ $tsts -eq 0 ]
-then
-  echo "  val ls1 = List((\"1\", \"a\"), (\"2\", \"a\"), (\"1\", \"c\"), (\"2\", \"a\"), (\"1\", \"c\"))" | tee -a $out
-  echo "  val ls2 = List((\"1\", \"a\"), (\"1\", \"b\"), (\"2\", \"x\"), (\"3\", \"a\"), (\"2\", \"y\"), (\"3\", \"c\"))" | tee -a $out
-  echo "  groupById(ls1, Map()) == Map(1 -> List(c, c, a), 2 -> List(a, a))" | tee -a $out
-  echo "  groupById(ls2, Map()) == Map(1 -> List(b, a), 2 -> List(x, y), 3 -> List(c, a))" | tee -a $out
-  echo "      where the order in the lists is unimportant" | tee -a $out
-  echo "  val ls3 = (1 to 1000).map(_.toString).toList" | tee -a $out
-  echo "  val ls4 = ls3 zip ls3.tail" | tee -a $out
-  echo "  val ls5 = ls4 ::: ls4.reverse" | tee -a $out
-  echo "  groupById(ls5, Map()) == Map(1 -> List(2,2), 2 -> List(3,3), ....)" | tee -a $out
-
-  if (scala_assert "danube.scala" "danube_test3.scala")
-  then
-      echo -e "  --> success" | tee -a $out
-      marks=$(( marks + 1 ))
-  else
-      echo -e "  --> test failed" | tee -a $out
-  fi
-fi
-
-### danube favourites tests
-
-if [ $tsts -eq 0 ]
-then
-  echo "  val good_ratings = process_ratings(ratings)" | tee -a $out
-  echo "  val ratings_map = groupById(good_ratings, Map())" | tee -a $out
-  echo "  favourites(ratings_map, \"912\").length  == 80 " | tee -a $out
-  echo "  favourites(ratings_map, \"858\").length  == 158 " | tee -a $out
-  echo "  favourites(ratings_map, \"260\").length  == 201 " | tee -a $out  
-
-  if (scala_assert "danube.scala" "danube_test4.scala") 
-  then
-    echo "  --> success" | tee -a $out
-    marks=$(( marks + 1 ))
-  else
-    echo "  --> one of the tests failed" | tee -a $out
-  fi
-fi
-
-### danube suggestions tests
-
-if [ $tsts -eq 0 ]
-then
-  echo "  val good_ratings = process_ratings(ratings)" | tee -a $out
-  echo "  val ratings_map = groupById(good_ratings, Map())" | tee -a $out
-  echo "  suggestions(ratings_map, \"912\").length  == 4110 " | tee -a $out
-  echo "  suggestions(ratings_map, \"858\").length  == 4883 " | tee -a $out
-  echo "  suggestions(ratings_map, \"260\").length  == 4970 " | tee -a $out  
-
-  if (scala_assert "danube.scala" "danube_test5.scala") 
-  then
-    echo "  --> success" | tee -a $out
-    marks=$(( marks + 1 ))
-  else
-    echo "  --> one of the tests failed" | tee -a $out
-  fi
-fi
-
-### danube recommendation tests
-
-if [ $tsts -eq 0 ]
-then
-  echo "  recommendations(ratings_map, movies_map, \"1\").length  == 2 " | tee -a $out
-  echo "  recommendations(ratings_map, movies_map, \"2\").length  == 2 " | tee -a $out
-  echo "  recommendations(ratings_map, movies_map, \"3\").length  == 2 " | tee -a $out
-  echo "  recommendations(ratings_map, movies_map, \"4\").length  == 0 " | tee -a $out
-  echo "  recommendations(ratings_map, movies_map, \"5\").length  == 2 " | tee -a $out
-
-  if (scala_assert "danube.scala" "danube_test6.scala") 
-  then
-    echo "  --> success" | tee -a $out
-    marks=$(( marks + 1 ))
-  else
-    echo "  --> one of the tests failed" | tee -a $out
-  fi
-fi
-
-
-## final marks
-echo "Overall mark for CW 7, Part 3" | tee -a $out
-echo "$marks" | tee -a $out
-
--- a/marking2/danube_test3.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test3.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,3 +1,5 @@
+import CW7b._
+
 
 // first test 
 
@@ -12,13 +14,13 @@
 // second test
 
 def urban_gb(ratings: List[(String, String)]) = 
-  ratings.groupBy(_._1).mapValues(_.map(_._2).toSet) 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2).toSet).toMap 
 
 def urban_gb2(ratings: List[(String, String)]) = 
-  ratings.groupBy(_._1).mapValues(_.map(_._2)) 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
 
 def urban_ck(ratings: List[(String, String)]) =
-  urban_gb(ratings) == groupById(ratings, Map()).mapValues(_.toSet)
+  urban_gb(ratings) == groupById(ratings, Map()).view.mapValues(_.toSet).toMap
 
 
 val ls2_urban = List(("1", "a"), ("1", "b"), ("2", "x"), ("3", "a"), ("2", "y"), ("3", "c"))
--- a/marking2/danube_test4.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test4.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,9 +1,10 @@
 
 // first test 
 
+import CW7b._
 
 def urban_groupById(ratings: List[(String, String)]) = 
-  ratings.groupBy(_._1).mapValues(_.map(_._2)) 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
 
 def urban_get_csv_file(name: String) : List[String] = {
   import io.Source
--- a/marking2/danube_test5.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test5.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,9 +1,10 @@
 
 // first test 
 
+import CW7b._
 
 def urban_groupById(ratings: List[(String, String)]) = 
-  ratings.groupBy(_._1).mapValues(_.map(_._2)) 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
 
 def urban_get_csv_file(name: String) : List[String] = {
   import io.Source
--- a/marking2/danube_test6.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/marking2/danube_test6.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,9 +1,11 @@
+
+import CW7b._
 
 // first test 
 
 
 def urban_groupById(ratings: List[(String, String)]) = 
-  ratings.groupBy(_._1).mapValues(_.map(_._2)) 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
 
 def urban_get_csv_file(name: String) : List[String] = {
   import io.Source
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/compare	Wed Oct 30 11:28:44 2019 +0000
@@ -0,0 +1,18 @@
+#!/bin/sh
+###set -e
+
+trap "exit" INT
+
+files=${1:-assignment20187-*}
+
+for sd in $files; do
+  cd $sd
+  #cp output output1
+  cmp -s output output1 > /dev/null
+  if [ $? -eq 1 ]; then
+      echo $sd + "is different"
+  fi
+  cd ..
+done
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/tails	Wed Oct 30 11:28:44 2019 +0000
@@ -0,0 +1,15 @@
+#!/bin/sh
+###set -e
+
+trap "exit" INT
+
+files=${1:-assignment20177-*}
+
+for sd in $files; do
+  cd $sd
+  #echo $sd
+  tail -1 output
+  cd ..
+done
+
+
--- a/solutions2/danube.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/solutions2/danube.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,6 +1,6 @@
-// Part 2 and 3 about Movie Recommendations 
+// Core Part about Movie Recommendations 
 // at Danube.co.uk
-//===========================================
+//========================================
 
 import io.Source
 import scala.util._
@@ -117,7 +117,7 @@
 def suggestions(recs: Map[String, List[String]], 
                     mov_name: String) : List[String] = {
   val favs = favourites(recs, mov_name).flatten
-  val favs_counted = favs.groupBy(identity).mapValues(_.size).toList
+  val favs_counted = favs.groupBy(identity).view.mapValues(_.size).toList
   val favs_sorted = favs_counted.sortBy(_._2).reverse
   favs_sorted.map(_._1)
 }
Binary file templates2/danube.jar has changed
--- a/templates2/danube.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/templates2/danube.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,7 +1,9 @@
-// Part 2 and 3 about Movie Recommendations 
+// Core Part about Movie Recommendations 
 // at Danube.co.uk
 //===========================================
 
+object CW7b {
+
 import io.Source
 import scala.util._
 
@@ -52,12 +54,6 @@
 
 
 
-//==============================================
-// Do not change anything below, unless you want 
-// to submit the file for the advanced part 3!
-//==============================================
-
-
 
 // (3) Implement a grouping function that calculates a Map
 //     containing the userIDs and all the corresponding recommendations 
@@ -168,9 +164,10 @@
 //}
 
 // helper functions
-//List().take(2
+//List().take(2)
 //List(1).take(2)
 //List(1,2).take(2)
 //List(1,2,3).take(2)
 
 
+}
--- a/testing1/drumb_test.sh	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing1/drumb_test.sh	Wed Oct 30 11:28:44 2019 +0000
@@ -94,8 +94,8 @@
 then
   echo "  get_prices(List(\"GOOG\", \"AAPL\"), 2010 to 2012) ==" >> $out
   echo "       List(List(Some(311.349976), Some(20.544939))," >> $out 
-  echo "              List(Some(300.222351), Some(31.638695))," >> $out 
-  echo "              List(Some(330.555054), Some(39.478039))))" >> $out
+  echo "            List(Some(300.222351), Some(31.638695))," >> $out 
+  echo "            List(Some(330.555054), Some(39.478039)))" >> $out
 
   if (scala_assert "drumb.scala" "drumb_test3.scala")
   then
@@ -133,7 +133,7 @@
   echo -e "" >> $out
   echo -e "  get_deltas(get_prices(List(\"BIDU\"), 2004 to 2008)) == " >> $out
   echo -e "    List(List(None), List(None),                          " >> $out
-  echo -e "         List(Some(0.9277165354330709)), List(Some(2.119679764725104)))) " >> $out
+  echo -e "         List(Some(0.9277165354330709)), List(Some(2.119679764725104))) " >> $out
   
   if (scala_assert "drumb.scala" "drumb_test5.scala") 
   then
--- a/testing1/mark	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing1/mark	Wed Oct 30 11:28:44 2019 +0000
@@ -10,6 +10,6 @@
 ./collatz_test.sh output1
 ./drumb_test.sh output2
 
-echo "Here is an automated test report for your work so far on assignment 6.  Please note that this is not the mark for your work; it is provided only in the hope that it is useful in developing your solution.  Passing these tests does not guarantee your code is free from bugs: after the deadline, your code will be marked against a different, more thorough set of test cases.\n\n" > $1
+echo -e "Here is an automated test report for your work so far on assignment 6.  Please note that this is not the mark for your work; it is provided only in the hope that it is useful in developing your solution.  Please ensure you test your code on your own machine in order to make sure it is bug free!! Passing these tests does not guarantee your code is free from bugs!! Also after the deadline, your code will be marked against a different, more thorough set of test cases.\n\n" > $1
 
 cat output1 output2 >> $1 
--- a/testing1/mark2	Tue Oct 29 23:56:13 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#!/bin/bash
-###set -e
-
-trap "exit" INT
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-cp $DIR/* .
-
-./drumb_test2.sh output1
-
-echo -e "Here is an automated test report for your work so far on assignment 6, Part 3.  Please note that this is not the mark for your work; it is provided only in the hope that it is useful in developing your solution.  Passing these tests does not guarantee your code is free from bugs: after the deadline, your code will be marked against a different, more thorough set of test cases.\n\n" > $1
-
-cat output1 >> $1 
--- a/testing2/danube.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing2/danube.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,11 +1,11 @@
-// Part 2 and 3 about Movie Recommendations 
+// Core Part about Movie Recommendations 
 // at Danube.co.uk
 //===========================================
 
 import io.Source
 import scala.util._
 
-//object CW7b { // for purposes of generating a jar
+object CW7b { // for purposes of generating a jar
 
 // (1) Implement the function get_csv_url which takes an url-string
 //     as argument and requests the corresponding file. The two urls
@@ -188,9 +188,9 @@
 //}
 
 // helper functions
-//List().take(2
+//List().take(2)
 //List(1).take(2)
 //List(1,2).take(2)
 //List(1,2,3).take(2)
 
-//}
+}
--- a/testing2/danube_test.sh	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing2/danube_test.sh	Wed Oct 30 11:28:44 2019 +0000
@@ -37,7 +37,7 @@
 
 if (scala_vars danube.scala)
 then
-  echo "  --> fail (make triple-sure your program conforms to the required format)" >> $out
+  echo "  --> FAIL (make triple-sure your program conforms to the required format)" >> $out
   tsts0=$(( 0 ))
 else
   echo "  --> success" >> $out
@@ -56,7 +56,7 @@
     echo "  --> success" >> $out
     tsts=$(( 0 ))
   else
-    echo "  --> scala did not run danube.scala" >> $out
+    echo "  --> SCALA DID NOT RUN danube.scala" >> $out
     tsts=$(( 1 )) 
   fi
 else
@@ -73,9 +73,9 @@
 
   if (scala_assert "danube.scala" "danube_test1.scala")
   then
-    echo "  --> success" >> $out
+    echo -e "  --> success" >> $out
   else
-    echo "  --> one of the tests failed" >> $out
+    echo -e "  --> ONE OF THE TESTS FAILED\n" >> $out
   fi
 fi
 
@@ -91,9 +91,85 @@
 
   if (scala_assert "danube.scala" "danube_test2.scala") 
   then
-    echo "  --> success" >> $out
+    echo -e "  --> success" >> $out
   else
-    echo "  --> one of the tests failed" >> $out
+    echo -e "  --> ONE OF THE TESTS FAILED\n" >> $out
+  fi
+fi
+
+### danube groupById test
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val ls1 = List((\"1\", \"a\"), (\"2\", \"a\"), (\"1\", \"c\"), (\"2\", \"a\"), (\"1\", \"c\"))" >> $out
+  echo -e  "  val ls2 = List((\"1\", \"a\"), (\"1\", \"b\"), (\"2\", \"x\"), (\"3\", \"a\"), (\"2\", \"y\"), (\"3\", \"c\"))" >> $out
+  echo -e  "  groupById(ls1, Map()) == Map(1 -> List(c, c, a), 2 -> List(a, a))" >> $out
+  echo -e  "  groupById(ls2, Map()) == Map(1 -> List(b, a), 2 -> List(x, y), 3 -> List(c, a))" >> $out
+  echo -e  "      where the order in the lists is unimportant" >> $out
+  echo -e  "  val ls3 = (1 to 1000).map(_.toString).toList" >> $out
+  echo -e  "  val ls4 = ls3 zip ls3.tail" >> $out
+  echo -e  "  val ls5 = ls4 ::: ls4.reverse" >> $out
+  echo -e  "  groupById(ls5, Map()) == Map(1 -> List(2,2), 2 -> List(3,3), ....)" >> $out
+
+  if (scala_assert "danube.scala" "danube_test3.scala")
+  then
+      echo -e  -e "  --> success" >> $out
+  else
+      echo -e  -e "  --> ONE OF THE TESTS FAILED\n" >> $out
   fi
 fi
 
+### danube favourites tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val good_ratings = process_ratings(ratings)" >> $out
+  echo -e  "  val ratings_map = groupById(good_ratings, Map())" >> $out
+  echo -e  "  favourites(ratings_map, \"912\").length  == 80 " >> $out
+  echo -e  "  favourites(ratings_map, \"858\").length  == 158 " >> $out
+  echo -e  "  favourites(ratings_map, \"260\").length  == 201 " >> $out  
+
+  if (scala_assert "danube.scala" "danube_test4.scala") 
+  then
+    echo -e  "  --> success" >> $out
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" >> $out
+  fi
+fi
+
+### danube suggestions tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  val good_ratings = process_ratings(ratings)" >> $out
+  echo -e  "  val ratings_map = groupById(good_ratings, Map())" >> $out
+  echo -e  "  suggestions(ratings_map, \"912\").length  == 4110 " >> $out
+  echo -e  "  suggestions(ratings_map, \"858\").length  == 4883 " >> $out
+  echo -e  "  suggestions(ratings_map, \"260\").length  == 4970 " >> $out  
+
+  if (scala_assert "danube.scala" "danube_test5.scala") 
+  then
+    echo -e  "  --> success" >> $out
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" >> $out
+  fi
+fi
+
+### danube recommendation tests
+
+if [ $tsts -eq 0 ]
+then
+  echo -e  "  recommendations(ratings_map, movies_map, \"1\").length  == 2 " >> $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"2\").length  == 2 " >> $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"3\").length  == 2 " >> $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"4\").length  == 0 " >> $out
+  echo -e  "  recommendations(ratings_map, movies_map, \"5\").length  == 2 " >> $out
+
+  if (scala_assert "danube.scala" "danube_test6.scala") 
+  then
+    echo -e  "  --> success" >> $out
+  else
+    echo -e  "  --> ONE OF THE TESTS FAILED\n" >>  $out
+  fi
+fi
+
--- a/testing2/danube_test1.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing2/danube_test1.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,5 +1,8 @@
+
+import CW7b._
 
 val urban_mov_url = """https://nms.kcl.ac.uk/christian.urban/movies.csv"""
+//val urban_mov_url = """http://localhost:8000/movies.csv"""
 
 assert(get_csv_url(urban_mov_url).length == 9742)
 
--- a/testing2/danube_test2.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing2/danube_test2.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -2,6 +2,8 @@
 import io.Source
 import scala.util._
 
+import CW7b._
+
 def urban_get_csv_file(name: String) : List[String] = {
   val csv = Source.fromFile(name)
   csv.mkString.split("\n").toList.drop(1)
--- a/testing2/danube_test3.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing2/danube_test3.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,8 +1,56 @@
+import CW7b._
 
-val ls_urban = List(("1", "a"), ("2", "a"), ("1", "c"), ("2", "a"), ("1", "c"))
+
+// first test 
 
-val m_urban = groupById(ls_urban, Map())
+val ls1_urban = List(("1", "a"), ("2", "a"), ("1", "c"), ("2", "a"), ("1", "c"))
+
+val m_urban = groupById(ls1_urban, Map())
 
 assert(m_urban.getOrElse("1", Nil).count(_ == "c") == 2)
 assert(m_urban.getOrElse("1", Nil).count(_ == "a") == 1)
 assert(m_urban.getOrElse("2", Nil) == List("a", "a"))
+
+// second test
+
+def urban_gb(ratings: List[(String, String)]) = 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2).toSet).toMap 
+
+def urban_gb2(ratings: List[(String, String)]) = 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
+
+def urban_ck(ratings: List[(String, String)]) =
+  urban_gb(ratings) == groupById(ratings, Map()).view.mapValues(_.toSet).toMap
+
+
+val ls2_urban = List(("1", "a"), ("1", "b"), ("2", "x"), ("3", "a"), ("2", "y"), ("3", "c"))
+
+assert(urban_ck(ls2_urban))
+
+// thrird test
+
+val ls3_urban = (1 to 1000).map(_.toString).toList
+val ls4_urban = ls3_urban zip ls3_urban.tail
+val ls5_urban = ls4_urban ::: ls4_urban.reverse
+
+assert(urban_ck(ls5_urban))
+
+
+/*
+import io.Source
+import scala.util._
+
+def urban_get_csv_file(name: String) : List[String] = {
+  val csv = Source.fromFile(name)
+  csv.mkString.split("\n").toList.drop(1)
+}
+
+def urban_process_ratings(lines: List[String]) : List[(String, String)] = {
+  for (cols <- lines.map(_.split(",").toList); 
+       if (cols(2).toFloat >= 4)) yield (cols(0), cols(1))  
+}
+
+val urban_ratings = urban_process_ratings(urban_get_csv_file("ratings.csv").take(1000))
+
+assert(urban_ck(urban_ratings))
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testing2/danube_test4.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -0,0 +1,29 @@
+
+// first test 
+
+import CW7b._
+
+def urban_groupById(ratings: List[(String, String)]) = 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
+
+def urban_get_csv_file(name: String) : List[String] = {
+  import io.Source
+  import scala.util._
+  val csv = Source.fromFile(name)
+  csv.mkString.split("\n").toList.drop(1)
+}
+
+def urban_process_ratings(lines: List[String]) : List[(String, String)] = {
+  for (cols <- lines.map(_.split(",").toList); 
+       if (cols(2).toFloat >= 4)) yield (cols(0), cols(1))  
+}
+
+
+val urban_good_ratings = process_ratings(urban_get_csv_file("ratings.csv"))
+
+val urban_ratings_map = urban_groupById(urban_good_ratings)
+
+assert(favourites(urban_ratings_map, "912").length  == 80)
+assert(favourites(urban_ratings_map, "858").length  == 158)
+assert(favourites(urban_ratings_map, "260").length  == 201)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testing2/danube_test5.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -0,0 +1,29 @@
+
+// first test 
+
+import CW7b._
+
+def urban_groupById(ratings: List[(String, String)]) = 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
+
+def urban_get_csv_file(name: String) : List[String] = {
+  import io.Source
+  import scala.util._
+  val csv = Source.fromFile(name)
+  csv.mkString.split("\n").toList.drop(1)
+}
+
+def urban_process_ratings(lines: List[String]) : List[(String, String)] = {
+  for (cols <- lines.map(_.split(",").toList); 
+       if (cols(2).toFloat >= 4)) yield (cols(0), cols(1))  
+}
+
+
+val urban_good_ratings = process_ratings(urban_get_csv_file("ratings.csv"))
+
+val urban_ratings_map = urban_groupById(urban_good_ratings)
+
+assert(suggestions(urban_ratings_map, "912").length  == 4110)
+assert(suggestions(urban_ratings_map, "858").length  == 4883)
+assert(suggestions(urban_ratings_map, "260").length  == 4970)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testing2/danube_test6.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -0,0 +1,35 @@
+
+import CW7b._
+
+// first test 
+
+
+def urban_groupById(ratings: List[(String, String)]) = 
+  ratings.groupBy(_._1).view.mapValues(_.map(_._2)).toMap 
+
+def urban_get_csv_file(name: String) : List[String] = {
+  import io.Source
+  import scala.util._
+  val csv = Source.fromFile(name)
+  csv.mkString.split("\n").toList.drop(1)
+}
+
+def urban_process_ratings(lines: List[String]) : List[(String, String)] = {
+  for (cols <- lines.map(_.split(",").toList); 
+       if (cols(2).toFloat >= 4)) yield (cols(0), cols(1))  
+}
+
+def urban_process_movies(lines: List[String]) : List[(String, String)] = {
+  for (cols <- lines.map(_.split(",").toList)) yield (cols(0), cols(1))  
+}
+
+
+val urban_good_ratings = process_ratings(urban_get_csv_file("ratings.csv"))
+val urban_movie_names = process_movies(urban_get_csv_file("movies.csv")).toMap
+
+val urban_ratings_map = urban_groupById(urban_good_ratings)
+
+assert((for (n <- List("1", "2", "3", "4", "5")) yield {
+  recommendations(urban_ratings_map, urban_movie_names, n).length
+}) == List(2, 2, 2, 0, 2))
+
--- a/testing3/knight1.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing3/knight1.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,7 +1,7 @@
-// Part 1 about finding and counting Knight's tours
-//==================================================
+// Preliminary Part 1 finding and counting Knight's tours
+//========================================================
 
-//object CW8a {   // for preparing the jar
+object CW8a {  
 
 type Pos = (Int, Int)    // a position on a chessboard 
 type Path = List[Pos]    // a path...a list of positions
@@ -165,6 +165,6 @@
 //time_needed(0, print_board(8, first_tour(8, List((0, 0))).get))
 
 
-//}
+}
 
 
--- a/testing3/knight1_test.sh	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing3/knight1_test.sh	Wed Oct 30 11:28:44 2019 +0000
@@ -18,15 +18,15 @@
 # functional tests
 
 function scala_assert_slow {
-  (ulimit -t 120; JAVA_OPTS="-Xmx1g" scala -i "$1" "$2" -e "" 2> /dev/null 1> /dev/null)
+  (ulimit -t 120; JAVA_OPTS="-Xmx1g" scala -i "$1" "-- $2" 2> /dev/null 1> /dev/null)
 }
 
 function scala_assert_thirty {
-  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" "$2" -e "" 2> /dev/null 1> /dev/null)
+  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" -- "$2" 2> /dev/null 1> /dev/null)
 }
 
 function scala_assert_quick {
-  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" "$2" -e "" 2> /dev/null 1> /dev/null)
+  (ulimit -t 30; JAVA_OPTS="-Xmx1g" scala -i "$1" -- "$2"  2> /dev/null 1> /dev/null)
 }
 
 # purity test
--- a/testing3/knight_test1.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing3/knight_test1.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,3 +1,4 @@
+import CW8a._
 
 assert(is_legal(8, Nil, (3, 4)) == true)
 assert(is_legal(8, List((4, 1), (1, 0)), (4, 1)) == false)
--- a/testing3/knight_test2.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing3/knight_test2.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,3 +1,5 @@
+
+import CW8a._
 
 assert(legal_moves(8, Nil, (2,2)) == List((3,4), (4,3), (4,1), (3,0), (1,0), (0,1), (0,3), (1,4)))
 assert(legal_moves(8, Nil, (7,7)) == List((6,5), (5,6)))
--- a/testing3/knight_test3.scala	Tue Oct 29 23:56:13 2019 +0000
+++ b/testing3/knight_test3.scala	Wed Oct 30 11:28:44 2019 +0000
@@ -1,4 +1,4 @@
-
+import CW8a._
 
 //type Pos = (Int, Int)    // a position on a chessboard 
 //type Path = List[Pos]    // a path...a list of positions