To download and install Scala on your home machines see:
Remember that this is only to enable you to play with the assignment at home: The final version turned in must work on the ACS Linux machines. While you can use Windows/Mac etc. to begin working with Scala, the code you turn in must be that required for the Linux environment.
The overall objective of this assignment is to introduce you to Scala, including classes, objects, closures, iteration as well as its functional features.
The assignment is spread over a single zip file hw5.zip. When you download and open it with unzip hw5.zip
out come tumbling the following files
which contain several skeleton Scala functions, with missing bodies, which currently contain the placeholder text sys.error("TO BE WRITTEN")
. Your task is to replace the placeholder text in those files with the appropriate Scala code for each case.
The zip also contains the following helper files that you will not modify
Your functions/programs must compile and/or run on a ACS Linux machine (e.g. ieng6.ucsd.edu
) as this is where your solutions will be checked. While you may develop your code on any system, ensure that your code runs as expected on an ACS machine prior to submission. You should test your code in the directories from which the zip files (see below) will be created, as this will approximate the environment used for grading the assignment.
Most of the points, will be awarded automatically, by evaluating your functions against a given test suite. test.ml
contains a very small suite of tests which gives you a flavor of of these tests. At any stage, by typing at the UNIX shell :
> make test | grep "130>>" > log
you will get a report on how your code stacks up against the simple tests.
The last line of the file log
must contain the word ``Compiled" otherwise you get a zero for the whole assignment.
If for some problem, you cannot get the code to compile, leave it as is with the sys.error("TO BE DONE")
with your partial solution enclosed below as a comment.
The second last line of the log file will contain your overall score, and the other lines will give you a readout for each test. You are encouraged to try to understand the code in Test.scala
, and subsequently devise your own tests and add them to Test.scala
, but you will not be graded on this.
Alternately, inside the Scala shell, type:
scala> import Test._
scala> Test()
.
.
130>> Results: ...
130>> Compiled
and it should return a pair of integers, reflecting your score and the max possible score on the sample tests. If instead an error message appears, your code will receive a zero.
To turn-in your solution, simply type
$ make turnin
in the directory in which you are working.
turnin
will provide you with a confirmation of the submission process; make sure that the size of the file indicated by turnin
matches the size of your zip file. See the ACS Web page on turnin for more information on the operation of the program.
Misc.scala
type this at the shell$ scalac Misc.scala
However, we have provided a Makefile
to make things easier. So you should just enter
$ make
and that will compile all the files pertaining to the assignment.
scala> :load Misc.scala
Loading Misc.scala...
import java.io.File
defined module Lines
scala> val zs = Lines.list("Misc.scala")
zs: List[String] = List(import java.io.File, "", /********************************************************/, /********** Helper Functions: Read/Write Files **********/, /********************************************************/, "", object Lines {, " ", " def iterator(file: String) : Iterator[String] = { ", " val f = new File(file)", " scala.io.Source.fromFile(f).getLines()", " }", " ", " def list(file: String) : List[String] = { ", " val buf = scala.io.Source.fromFile(new File(file)) ", " val lines = buf.getLines().toList", " buf.close()", " lines ", " }", "", }, "", // vim: set ts=2 sw=2 et:, "")
:load
step. This is recommended.Fill in the definition for def apply
)
def apply(file: String) : Iterator[String] =
sys.error("TO BE WRITTEN")
inside object Words
. The function takes as input a filename file
and returns an Iterator
over the words in the file converted to lower-case. The input file
will have one word per line. The resulting words should be returned in a list in the same order they occur in the input file. When you are done, you should get the following behavior in the REPL:
scala> val zs = Words("words")
zs: Iterator[String] = non-empty iterator
scala> zs.next
res0: String = aditya
scala> zs.next
res1: String = adivasi
scala> zs.next
res2: String = adiz
scala> zs.next
res3: String = 1080
Next, complete the definition of the function
def groupFreq[A,B](xs: Iterator[A], f: A => B): HashMap[B, Int] =
sys.error("TO BE WRITTEN")
As indicated by the type, the function takes an xs: Iterator[A]
and a grouping function f
that converts A
values into their B
group. The function should return a HashMap
that counts for each B
group, the number of A
values that fell into the group.
When you are done, you should get the following behavior:
scala> Words.groupFreq(Words.inti.iterator, Words.isEven)
res9: scala.collection.immutable.HashMap[String,Int] = Map(Odd -> 5, Even -> 2)
Write a function
def sizeFreq(file: String): HashMap[Int, Int]
that uses groupFreq
to compute HashMap
that maps the word-lengths to the number of words of that length. Once you have implemented the function, you should get the following behavior:
scala> val wf = Words.sizeFreq("words")
res0: scala.collection.immutable.HashMap[Int,Int] =
Map( 5 -> 25099, 10 -> 54640, 24 -> 19, 25 -> 9, 14 -> 19300
, 20 -> 508, 29 -> 2, 1 -> 53, 6 -> 41696, 28 -> 2, 21 -> 239
, 9 -> 62608, 13 -> 27947, 2 -> 1271, 45 -> 1, 17 -> 4012
, 22 -> 103, 27 -> 3, 12 -> 37559, 7 -> 53939, 3 -> 6221
, 18 -> 2008, 16 -> 7128, 31 -> 1, 11 -> 46474, 26 -> 2, 23 -> 50
, 8 -> 62332, 30 -> 1, 19 -> 1052, 4 -> 13209, 15 -> 12140)
scala> wf(4)
res5: Int = 13209
scala> wf(30)
res3: Int = 1
scala> wf(12)
res4: Int = 37559
Write a function
def charFreq(file: String): HashMap[Char, Int]
that uses groupFreq
to compute HashMap
that maps each Char
to the number of times the Char
appears across all the entire file file
. Note that you should combine upper- and lower-case occurences of each char and return just the score for the lower-case Char
.
Once you have implemented the function, you should get the following behavior:
scala> val cf = Words.charFreq("words")
res0: scala.collection.immutable.HashMap[Char, Int] ..
scala> cf('d')
res3: Int = 155630
scala> cf('z')
res4: Int = 18107
scala> cf('e')
res5: Int = 484449
Write a function
def wordsOfSize(file: String, size: Int) : Iterator[String]
which returns an iterator over all the words in file
that have length equal to size
. When you are done, you should get the following behavior:
scala> Words.wordsOfSize("words", 29).toList
res10: List[String] = List(cyclotrimethylenetrinitramine, trinitrophenylmethylnitramine)
Write a function
def wordsWithAllVowels(file: String): Iterator[String]
which returns an iterator over all the words in file
that contain all the vowels. When you are done, you should get the following behavior:
scala> val vows = Words.wordsWithAllVowels("words")
vows: Iterator[String] = non-empty iterator
scala> vows.next
res2: String = abdomino-uterotomy
scala> vows.next
res3: String = abevacuation
scala> vows.next
res4: String = abietineous
scala> vows.next
res5: String = abiogenous
scala> vows.next
res6: String = aboideau
scala> vows.next
res7: String = aboideaus
Write a function
def wordsWithNoVowels(file: String): Iterator[String]
which returns an iterator over all the words in file
that do not contain any vowels. When you are done, you should get the following behavior:
scala> Words.wordsWithNoVowels("words").toList
res0: List[String] = List(1080, 10th, 1st, 2, 2,4,5-t, 2,4-d, 2d, 2nd, 30-30, 3-d, 3-d, 3d, 3m, 3rd, 4-d, 4gl, 4h, 4th, 5-t, 5th, 6th, 7th, 8th, 9th, b, b-, b, b911, b/b, bb, bb, bbb, b.b.c., bbc, bbl, bbl, bbl., bbls, bbn, bbs, bbxrt, b.c., b/c, bc, bcbs, bcc, bcd, bcd, bcf, b.ch., bch, bch, bchs, b.c.l., bcl, bcm, bcp, bcpl, bcr, bcs, bcwp, bcws, b.d., b/d, bd, bd, bd., bdc, bdd, bdf, bdft, bdl, bdl., bdls, bdrm, b.d.s., bds, bds, bdt, b/f, bf, b.f., bf, bfd, bfdc, bfhd, bfr, bfs, bft, bg, bg, bglr, bgp, bh, bhc, bhd, bhl, bhp, bhp, bht, bk, bk, bk., bkbndr, bkcy, bkcy., bkg, bkg., bkgd, bklr, bkpr, bkpt, bks, bks., bkt, b.l., b/l, bl, b/l, bl, bl., bld, bldg, bldg., bldr, blds, blf, blk, blk., bll, blm, bls, bls, blt, blv, blvd, blvd, bly, blynn, blyth, b.m., bm, bm, bmg, bmj, bmp, b...
Finally, write a function
def wordsMatchingRegexp(file: String, re: Regex): Iterator[String]
which loads the words from the file file
which match the regular expression re
. Recall that we have already seen regular expressions in PA4, see this for information on how to match strings against regular expressions in Scala. Once you have implemented the function, you should get the following behavior in the REPL:
scala> val re1 = new Regex("""^[a-z].{2}$""")
re1: scala.util.matching.Regex = ^[a-z].{2}$
scala> val ws1 = Words.wordsMatchingRegexp("words", re1).take(5).toList
ws1: List[String] = List(a-1, aaa, aaa, aae, aaf)
scala> val re2 = new Regex("""^ca.*{2}e$""")
re2: scala.util.matching.Regex = ^ca.*{2}e$
scala> val ws2 = Words.wordsMatchingRegexp("words", re2).take(5).toList
ws2: List[String] = List(caballine, cabane, cabbage, cabbagelike, cabbage-tree)
scala> val re3 = new Regex("""^xYx.*$""")
scala> val ws3 = Words.wordsMatchingRegexp("words", re3).take(5).toList
ws3: List[String] = List()
For this problem, you will write a simple dictionary-based password cracker. This should primarily be an exercise in string manipulation and data structures. You will be attempting to crack as many passwords as possible in a UNIX password file. The passwords are encrypted using the UNIX crypt()
function. For more information see man passwd man crypt on UNIX or Linux.
Write a Scala function
def transformReverse(w: String) : Iterator[String]
which takes a string and returns an iterator containing the the original string and the reversal of the original string. When you are done, you should get the following behavior (the order of the results is not important):
scala> Crack.transformReverse("caterpillar").toList
res1: List[String] = List(caterpillar, rallipretac)
Write a Scala function
def transformCapitalize(w: String) : Iterator[String]
which takes a string and returns an iterator containing all the possible ways to capitalize the input string. When you are done, you should get the following behavior (the order of the results is not important):
scala> Crack.transformCapitalize("taco").toList
res4: List[String] = List(taco, tacO, taCo, taCO, tAco, tAcO, tACo, tACO, Taco, TacO, TaCo, TaCO, TAco, TAcO, TACo, TACO)
Write a Scala function
def transformDigits(w:String) : Iterator[String]
which should return return a list of all possible ways to replace letters with similar looking digits according to the following mappings. This should be done without regard to the capitalization of the input string, however when a character is not replaced with a digit, the character’s capitalization should be preserved.
o -> 0 i,l -> 1 z -> 2 e -> 3 a -> 4 s -> 5 b -> 6, 8 t -> 7 g,q -> 9
When you are done, you should get the following behavior (the order of the results is not important):
scala> Crack.transformDigits("Bow").toList
res0: List[String] = List(Bow, B0w, 6ow, 60w, 8ow, 80w)
Write a function Entry.apply
def apply(line: String) : Entry
which takes a string – a line from the file passwd
– and returns an instance of the case class Entry
(which has fields account
, password
, UID
, GID
, GECOS
, directory
, and shell
). The UID
and GID
fields should be returned as integers. The passwd
file contains lines of the following format:
account:password:UID:GID:GECOS:directory:shell
(See this for more information.)
Once you have implemented the function, you should get the following behavior:
scala> val e = Entry("checani:IqAFDoIjL2cDs:1:1:Pengpu Checani:/home/checani:/bin/bash")
e: Entry = Entry(checani,IqAFDoIjL2cDs,1,1,Pengpu Checani,/home/checani,/bin/bash)
scala> e.password
res1: String = IqAFDoIjL2cDs
scala> e.account
res2: String = checani
Hint: The function .split
method may be of use.
The Scala function
def checkPassword(plain: String, enc: String) : Boolean
takes a plain-text password, plain
, and an encrypted password enc
and returns true
if plain
encrypts to enc
using the function Crypt.crypt
(provided in the Java file Crypt.java
).
scala> Crack.checkPassword("asarta","IqAFDoIjL2cDs")
res3: Boolean = true
scala> Crack.checkPassword("asartaz","IqAFDoIjL2cDs")
res4: Boolean = false
Use the above to write a function Crack.apply
def apply(pwdFile: String, wordsFile: String, outFile: String) : Unit
which takes a password file name pwdFile
, a file with a list of words named wordsFile
and a string corresponding to an output file named outFile
. The function apply
should generate candidate passwords from the words in wordsFile
by applying the various transformations (transformReverse
, transformDigits
, and transformCapitalize
).
Attempt to crack as many of the passwords in the password file as possible. As passwords are discovered, they should be written to the output file in the following format:
username=passfor example,
checani=asarta
After each line is written, the output file should be flushed. Your function will only be allowed to run for a limited amount of time (several minutes). Thus, you should attempt to crack the easiest passwords first. All passwords in the input file are between 6 and 8 characters and can be cracked using words in the provided dictionary along with a combination of the transformations above. The base points will be provided if all the passwords which are untransformed strings can be cracked. Fractional credit will be proportional to the number of additional passwords cracked, with the students cracking the most extra words getting the full 25 points.
Once you have implemented the function, you should get the following behavior:
scala> Crack("passwd", "words", "out")
# time passes ...
# Hit Ctrl-C
...
The file out
should contain something like the following:
checani=asarta
root=…
…=…
…=…
Alternately, at the UNIX prompt:
$ make crack
$ scala Crack passwd words out
and as the program runs, it should produce the above output in the file out
.