Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lamp/cs206
  • bwermeil/cs206-2020
  • zabifade/cs206-2020
  • cauderan/cs206-2020
  • malonga/cs206-2020
  • dumoncel/cs206
  • bounekhe/cs206
  • bergerault/cs206
  • flealsan/cs206
  • hsu/cs206
  • mouchel/cs206
  • vebraun/cs206
  • vcanard/cs206
  • ybelghmi/cs206
  • belghmi/cs206
  • bousbina/cs206
  • waked/cs206
  • gtagemou/cs206
  • arahmoun/cs206
  • elhachem/cs206
  • benrahha/cs206
  • benslima/cs206
22 results
Show changes
Showing
with 0 additions and 1015 deletions
package ch.epfl.lamp
import sbt._
import sbt.Keys._
/**
* Coursera uses two versions of each assignment. They both have the same assignment key and part id but have
* different item ids.
*
* @param key Assignment key
* @param partId Assignment partId
* @param itemId Item id of the non premium version
* @param premiumItemId Item id of the premium version (`None` if the assignment is optional)
*/
case class CourseraId(key: String, partId: String, itemId: String, premiumItemId: Option[String])
/**
* Settings shared by all assignments, reused in various tasks.
*/
object MOOCSettings extends AutoPlugin {
override def requires = super.requires && filteringReporterPlugin.FilteringReporterPlugin
object autoImport {
val course = SettingKey[String]("course")
val assignment = SettingKey[String]("assignment")
val options = SettingKey[Map[String, Map[String, String]]]("options")
val courseraId = settingKey[CourseraId]("Coursera-specific information identifying the assignment")
val testSuite = settingKey[String]("Fully qualified name of the test suite of this assignment")
.withRank(KeyRanks.Invisible)
// Convenient alias
type CourseraId = ch.epfl.lamp.CourseraId
val CourseraId = ch.epfl.lamp.CourseraId
}
import autoImport._
override val globalSettings: Seq[Def.Setting[_]] = Seq(
// supershell is verbose, buggy and useless.
useSuperShell := false
)
override val projectSettings: Seq[Def.Setting[_]] = Seq(
parallelExecution in Test := false,
// Report test result after each test instead of waiting for every test to finish
logBuffered in Test := false,
name := s"${course.value}-${assignment.value}"
)
}
package ch.epfl.lamp
import sbt._
import Keys._
// import scalaj.http._
import java.io.{File, FileInputStream, IOException}
import org.apache.commons.codec.binary.Base64
// import play.api.libs.json.{Json, JsObject, JsPath}
import scala.util.{Failure, Success, Try}
/**
* Provides tasks for submitting the assignment
*/
object StudentTasks extends AutoPlugin {
override def requires = super.requires && MOOCSettings
object autoImport {
val packageSourcesOnly = TaskKey[File]("packageSourcesOnly", "Package the sources of the project")
val packageBinWithoutResources = TaskKey[File]("packageBinWithoutResources", "Like packageBin, but without the resources")
val packageSubmissionZip = TaskKey[File]("packageSubmissionZip")
val packageSubmission = inputKey[Unit]("package solution as an archive file")
lazy val Grading = config("grading") extend(Runtime)
}
import autoImport._
import MOOCSettings.autoImport._
override lazy val projectSettings = Seq(
packageSubmissionSetting,
fork := true,
connectInput in run := true,
outputStrategy := Some(StdoutOutput),
) ++
packageSubmissionZipSettings ++
inConfig(Grading)(Defaults.testSettings ++ Seq(
unmanagedJars += file("grading-tests.jar"),
definedTests := (definedTests in Test).value,
internalDependencyClasspath := (internalDependencyClasspath in Test).value
))
/** **********************************************************
* SUBMITTING A SOLUTION TO COURSERA
*/
val packageSubmissionZipSettings = Seq(
packageSubmissionZip := {
val submission = crossTarget.value / "submission.zip"
val sources = (packageSourcesOnly in Compile).value
val binaries = (packageBinWithoutResources in Compile).value
IO.zip(Seq(sources -> "sources.zip", binaries -> "binaries.jar"), submission, None)
submission
},
artifactClassifier in packageSourcesOnly := Some("sources"),
artifact in (Compile, packageBinWithoutResources) ~= (art => art.withName(art.name + "-without-resources"))
) ++
inConfig(Compile)(
Defaults.packageTaskSettings(packageSourcesOnly, Defaults.sourceMappings) ++
Defaults.packageTaskSettings(packageBinWithoutResources, Def.task {
val relativePaths =
(unmanagedResources in Compile).value.flatMap(Path.relativeTo((unmanagedResourceDirectories in Compile).value)(_))
(mappings in (Compile, packageBin)).value.filterNot { case (_, path) => relativePaths.contains(path) }
})
)
val maxSubmitFileSize = {
val mb = 1024 * 1024
10 * mb
}
/** Check that the jar exists, isn't empty, isn't crazy big, and can be read
* If so, encode jar as base64 so we can send it to Coursera
*/
def prepareJar(jar: File, s: TaskStreams): String = {
val errPrefix = "Error submitting assignment jar: "
val fileLength = jar.length()
if (!jar.exists()) {
s.log.error(errPrefix + "jar archive does not exist\n" + jar.getAbsolutePath)
failSubmit()
} else if (fileLength == 0L) {
s.log.error(errPrefix + "jar archive is empty\n" + jar.getAbsolutePath)
failSubmit()
} else if (fileLength > maxSubmitFileSize) {
s.log.error(errPrefix + "jar archive is too big. Allowed size: " +
maxSubmitFileSize + " bytes, found " + fileLength + " bytes.\n" +
jar.getAbsolutePath)
failSubmit()
} else {
val bytes = new Array[Byte](fileLength.toInt)
val sizeRead = try {
val is = new FileInputStream(jar)
val read = is.read(bytes)
is.close()
read
} catch {
case ex: IOException =>
s.log.error(errPrefix + "failed to read sources jar archive\n" + ex.toString)
failSubmit()
}
if (sizeRead != bytes.length) {
s.log.error(errPrefix + "failed to read the sources jar archive, size read: " + sizeRead)
failSubmit()
} else encodeBase64(bytes)
}
}
/** Task to package solution to a given file path */
lazy val packageSubmissionSetting = packageSubmission := {
val args: Seq[String] = Def.spaceDelimited("[path]").parsed
val s: TaskStreams = streams.value // for logging
val jar = (packageSubmissionZip in Compile).value
val base64Jar = prepareJar(jar, s)
val path = args.headOption.getOrElse((baseDirectory.value / "submission.jar").absolutePath)
scala.tools.nsc.io.File(path).writeAll(base64Jar)
}
/*
/** Task to submit a solution to coursera */
val submit = inputKey[Unit]("submit solution to Coursera")
lazy val submitSetting = submit := {
// Fail if scalafix linting does not pass.
scalafixLinting.value
val args: Seq[String] = Def.spaceDelimited("<arg>").parsed
val s: TaskStreams = streams.value // for logging
val jar = (packageSubmissionZip in Compile).value
val assignmentDetails =
courseraId.?.value.getOrElse(throw new MessageOnlyException("This assignment can not be submitted to Coursera because the `courseraId` setting is undefined"))
val assignmentKey = assignmentDetails.key
val courseName =
course.value match {
case "capstone" => "scala-capstone"
case "bigdata" => "scala-spark-big-data"
case other => other
}
val partId = assignmentDetails.partId
val itemId = assignmentDetails.itemId
val premiumItemId = assignmentDetails.premiumItemId
val (email, secret) = args match {
case email :: secret :: Nil =>
(email, secret)
case _ =>
val inputErr =
s"""|Invalid input to `submit`. The required syntax for `submit` is:
|submit <email-address> <submit-token>
|
|The submit token is NOT YOUR LOGIN PASSWORD.
|It can be obtained from the assignment page:
|https://www.coursera.org/learn/$courseName/programming/$itemId
|${
premiumItemId.fold("") { id =>
s"""or (for premium learners):
|https://www.coursera.org/learn/$courseName/programming/$id
""".stripMargin
}
}
""".stripMargin
s.log.error(inputErr)
failSubmit()
}
val base64Jar = prepareJar(jar, s)
val json =
s"""|{
| "assignmentKey":"$assignmentKey",
| "submitterEmail":"$email",
| "secret":"$secret",
| "parts":{
| "$partId":{
| "output":"$base64Jar"
| }
| }
|}""".stripMargin
def postSubmission[T](data: String): Try[HttpResponse[String]] = {
val http = Http("https://www.coursera.org/api/onDemandProgrammingScriptSubmissions.v1")
val hs = List(
("Cache-Control", "no-cache"),
("Content-Type", "application/json")
)
s.log.info("Connecting to Coursera...")
val response = Try(http.postData(data)
.headers(hs)
.option(HttpOptions.connTimeout(10000)) // scalaj default timeout is only 100ms, changing that to 10s
.asString) // kick off HTTP POST
response
}
val connectMsg =
s"""|Attempting to submit "${assignment.value}" assignment in "$courseName" course
|Using:
|- email: $email
|- submit token: $secret""".stripMargin
s.log.info(connectMsg)
def reportCourseraResponse(response: HttpResponse[String]): Unit = {
val code = response.code
val respBody = response.body
/* Sample JSON response from Coursera
{
"message": "Invalid email or token.",
"details": {
"learnerMessage": "Invalid email or token."
}
}
*/
// Success, Coursera responds with 2xx HTTP status code
if (response.is2xx) {
val successfulSubmitMsg =
s"""|Successfully connected to Coursera. (Status $code)
|
|Assignment submitted successfully!
|
|You can see how you scored by going to:
|https://www.coursera.org/learn/$courseName/programming/$itemId/
|${
premiumItemId.fold("") { id =>
s"""or (for premium learners):
|https://www.coursera.org/learn/$courseName/programming/$id
""".stripMargin
}
}
|and clicking on "My Submission".""".stripMargin
s.log.info(successfulSubmitMsg)
}
// Failure, Coursera responds with 4xx HTTP status code (client-side failure)
else if (response.is4xx) {
val result = Try(Json.parse(respBody)).toOption
val learnerMsg = result match {
case Some(resp: JsObject) =>
(JsPath \ "details" \ "learnerMessage").read[String].reads(resp).get
case Some(x) => // shouldn't happen
"Could not parse Coursera's response:\n" + x
case None =>
"Could not parse Coursera's response:\n" + respBody
}
val failedSubmitMsg =
s"""|Submission failed.
|There was something wrong while attempting to submit.
|Coursera says:
|$learnerMsg (Status $code)""".stripMargin
s.log.error(failedSubmitMsg)
}
// Failure, Coursera responds with 5xx HTTP status code (server-side failure)
else if (response.is5xx) {
val failedSubmitMsg =
s"""|Submission failed.
|Coursera seems to be unavailable at the moment (Status $code)
|Check https://status.coursera.org/ and try again in a few minutes.
""".stripMargin
s.log.error(failedSubmitMsg)
}
// Failure, Coursera repsonds with an unexpected status code
else {
val failedSubmitMsg =
s"""|Submission failed.
|Coursera replied with an unexpected code (Status $code)
""".stripMargin
s.log.error(failedSubmitMsg)
}
}
// kick it all off, actually make request
postSubmission(json) match {
case Success(resp) => reportCourseraResponse(resp)
case Failure(e) =>
val failedConnectMsg =
s"""|Connection to Coursera failed.
|There was something wrong while attempting to connect to Coursera.
|Check your internet connection.
|${e.toString}""".stripMargin
s.log.error(failedConnectMsg)
}
}
*/
def failSubmit(): Nothing = {
sys.error("Submission failed")
}
/**
* *****************
* DEALING WITH JARS
*/
def encodeBase64(bytes: Array[Byte]): String =
new String(Base64.encodeBase64(bytes))
}
sbt.version=1.4.7
// Used for Coursera submission (StudentPlugin)
// libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.4.2"
// libraryDependencies += "com.typesafe.play" %% "play-json" % "2.7.4"
// Used for Base64 (StudentPlugin)
libraryDependencies += "commons-codec" % "commons-codec" % "1.10"
// addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.28")
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.5.3")
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
trait M7 extends Lib {
class DLLCombinerImplementation(chunk_size: Int = 3) extends DLLCombiner(chunk_size){
// Computes every other Integer element of data array, starting from the second, up to the middle
def task1(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = head
if(current != null) {
var i_from = 1
var i_to = 1
if(i_from >= current.cnt) {
current = current.next
if(current != null) {
i_from = 0
}
else i_to = cnt/2 // to stop the loop
}
copyForward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the first (index 0), up to the middle
def task2(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = head
if(current != null) {
var i_from = 0
var i_to = 0
copyForward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the last, up to the middle
def task3(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = last
if( current != null) {
var i_from = current.cnt - 1
var i_to = cnt - 1
copyBackward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the second to last, up to the middle
// This is executed on the current thread.
def task4(data: Array[Int]): Unit = {
var current = last
if( current != null) {
var i_to = cnt - 2
var i_from = current.cnt - 2
if(i_from < 0) {
current = current.previous
if(current != null) {
i_from = current.cnt - 1
}
else i_to = cnt/2 - 1 // to stop the loop
}
copyBackward(data, current, i_from, i_to, cnt/2)
}
}
def result(): Array[Int] = {
val data = new Array[Int](cnt)
val t1 = task1(data)
val t2 = task2(data)
val t3 = task3(data)
task4(data)
t1.join()
t2.join()
t3.join()
data
}
private def copyBackward(data: Array[Int], curr: Node, from: Int, to: Int, limit: Int) = {
var current = curr
var i_from = from
var i_to = to
while (i_to >= limit) {
try{
data(i_to) = current.value(i_from)
i_to -= 2
i_from -= 2
if(i_from == -1) {
current = current.previous
i_from = current.cnt - 1
}
else if(i_from == -2) {
current = current.previous
i_from = current.cnt - 2
if(current.cnt == 1) {
current = current.previous
i_from = current.cnt - 1
}
}
}
catch{
case e: Exception =>
}
}
}
private def copyForward(data: Array[Int], curr: Node, from: Int, to: Int, limit: Int) = {
var current = curr
var i_from = from
var i_to = to
while (i_to < limit) {
try {
data(i_to) = current.value(i_from)
i_to += 2
i_from += 2
if(i_from == current.cnt){
current = current.next
i_from = 0
}
else if(i_from > current.cnt) {
current = current.next
i_from = 1
if(current.cnt == 1) {
current = current.next
i_from = 0
}
}
}
catch{
case e: Exception =>
}
}
}
}
}
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
trait Lib {
class Node(val size: Int) {
var value: Array[Int] = new Array(size)
var next: Node = null
var previous: Node = null
var cnt = 0
def add(v: Int) = {
value(cnt) = v
cnt += 1
}
}
// Simplified Combiner interface
// Implements methods += and combine
// Abstract methods should be implemented in subclasses
abstract class DLLCombiner(val chunk_size: Int) {
var head: Node = null
var last: Node = null
var cnt: Int = 0
var chunks: Int = 0
// Adds an Integer to the last node of this array combiner. If the last node is full, allocates a new node.
def +=(elem: Int): Unit = {
if(cnt % chunk_size == 0) {
chunks = chunks + 1
val node = new Node(chunk_size)
if (cnt == 0) {
head = node
last = node
}
else {
last.next = node
node.previous = last
last = node
}
}
last.add(elem)
cnt += 1
}
// Combines this array combiner and another given combiner in constant O(1) complexity.
def combine(that: DLLCombiner): DLLCombiner = {
assert(this.chunk_size == that.chunk_size)
if (this.cnt == 0) {
this.head = that.head
this.last = that.last
this.cnt = that.cnt
this.chunks = that.chunks
this
}
else if (that.cnt == 0)
this
else {
this.last.next = that.head
that.head.previous = this.last
this.cnt = this.cnt + that.cnt
this.chunks = this.chunks + that.chunks
this.last = that.last
this
}
}
def task1(data: Array[Int]): ForkJoinTask[Unit]
def task2(data: Array[Int]): ForkJoinTask[Unit]
def task3(data: Array[Int]): ForkJoinTask[Unit]
def task4(data: Array[Int]): Unit
def result(): Array[Int]
}
def task[T](body: => T): ForkJoinTask[T]
}
\ No newline at end of file
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
class M7Suite extends AbstractM7Suite {
// (70+) 30 / 500 points for correct implementation, don't check parallelism
test("[Correctness] fetch result - simple combiners (5pts)") {
assertCorrectnessSimple()
}
test("[Correctness] fetch result - small combiners (5pts)") {
assertCorrectnessBasic()
}
test("[Correctness] fetch result - small combiners after combining (10pts)") {
assertCorrectnessCombined()
}
test("[Correctness] fetch result - large combiners (10pts)") {
assertCorrectnessLarge()
}
def assertCorrectnessSimple() = {
simpleCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessBasic() = {
basicCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessCombined() = {
combinedCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessLarge() = {
largeCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
// (70+30+) 50 / 500 points for correct parallel implementation, don't check if it's exactly 1/4 of the array per task
private var count = 0
private val expected = 3
override def task[T](body: => T): ForkJoinTask[T] = {
count += 1
scheduler.value.schedule(body)
}
test("[TaskCount] number of newly created tasks should be 3 (5pts)") {
assertTaskCountSimple()
}
test("[TaskCount] fetch result and check parallel - simple combiners (5pts)") {
assertTaskCountSimple()
assertCorrectnessSimple()
}
test("[TaskCount] fetch result and check parallel - small combiners (10pts)") {
assertTaskCountSimple()
assertCorrectnessBasic()
}
test("[TaskCount] fetch result and check parallel - small combiners after combining (15pts)") {
assertTaskCountSimple()
assertCorrectnessCombined()
}
test("[TaskCount] fetch result and check parallel - large combiners (15pts)") {
assertTaskCountSimple()
assertCorrectnessLarge()
}
def assertTaskCountSimple(): Unit = {
simpleCombiners.foreach(elem => assertTaskCount(elem._1, elem._2))
}
def assertTaskCount(combiner: DLLCombinerTest, array: Array[Int]): Unit = {
try {
count = 0
build(combiner, array)
combiner.result()
assertEquals(count, expected, {
s"ERROR: Expected $expected instead of $count calls to `task(...)`"
})
} finally {
count = 0
}
}
//(70+30+50+) 200 / 500 points for correct parallel implementation, exactly 1/4 of the array per task
test("[TaskFairness] each task should compute 1/4 of the result (50pts)") {
assertTaskFairness(simpleCombiners.unzip._1)
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - simple combiners (20pts)") {
assertTaskFairness(simpleCombiners.unzip._1)
assertCorrectnessSimple()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - small combiners (30pts)") {
assertTaskFairness(basicCombiners.unzip._1)
assertCorrectnessBasic()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - small combiners after combining (50pts)") {
assertTaskFairness(combinedCombiners.unzip._1)
assertCorrectnessCombined()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - large combiners (50pts)") {
assertTaskFairness(largeCombiners.unzip._1)
assertCorrectnessLarge()
}
def assertTaskFairness(combiners: List[DLLCombinerTest]): Unit = {
def assertNewTaskFairness(combiner: DLLCombinerTest, task: ForkJoinTask[Unit], data: Array[Int]) = {
var count = 0
var expected = combiner.cnt / 4
task.join
count = data.count(elem => elem != 0)
assert((count - expected).abs <= 1)
}
def assertMainTaskFairness(combiner: DLLCombinerTest, task: Unit, data: Array[Int]) = {
var count = 0
var expected = combiner.cnt / 4
count = data.count(elem => elem != 0)
assert((count - expected).abs <= 1)
}
combiners.foreach { elem =>
var data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task1(data), data)
data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task2(data), data)
data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task3(data), data)
data = Array.fill(elem.cnt)(0)
assertMainTaskFairness(elem, elem.task4(data), data)
}
}
//(70+30+50+200+) 150 / 500 points for correct parallel implementation, exactly 1/4 of the array per task, exactly the specified quarter
test("[TaskPrecision] each task should compute specified 1/4 of the result - simple combiners (20pts)") {
assertTaskPrecision(simpleCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - small combiners (30pts)") {
assertTaskPrecision(basicCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - small combiners after combining (50pts)") {
assertTaskPrecision(combinedCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - large combiners (50pts)") {
assertTaskPrecision(largeCombiners)
}
def assertTaskPrecision(combiners: List[(DLLCombinerTest, Array[Int])]): Unit = {
combiners.foreach { elem =>
var data = Array.fill(elem._1.cnt)(0)
var ref = Array.fill(elem._1.cnt)(0)
val task1 = elem._1.task1(data)
task1.join
Range(0, elem._1.cnt).foreach(i => (if (i < elem._1.cnt / 2 - 1 && i % 2 == 1) ref(i) = elem._2(i)))
assert(Range(0, elem._1.cnt / 2 - 1).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task2 = elem._1.task2(data)
task2.join
Range(0, elem._1.cnt).foreach(i => (if (i < elem._1.cnt / 2 - 1 && i % 2 == 0) ref(i) = elem._2(i)))
assert(Range(0, elem._1.cnt / 2 - 1).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task3 = elem._1.task3(data)
task3.join
Range(0, elem._1.cnt).foreach(i => (if (i > elem._1.cnt / 2 + 1 && i % 2 != elem._1.cnt % 2) ref(i) = elem._2(i)))
assert(Range(elem._1.cnt / 2 + 2, elem._1.cnt).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task4 = elem._1.task4(data)
Range(0, elem._1.cnt).foreach(i => (if (i > elem._1.cnt / 2 + 1 && i % 2 == elem._1.cnt % 2) ref(i) = elem._2(i)))
assert(Range(elem._1.cnt / 2 + 2, elem._1.cnt).forall(i => data(i) == ref(i)))
}
}
test("[Public] fetch simple result without combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
combiner1 += 3
combiner1 += 8
combiner1 += 1
combiner1 += 2
combiner1 += 3
combiner1 += 8
val result = combiner1.result()
val array = Array(7, 2, 3, 8, 1, 2, 3, 8)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result without combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
combiner1 += 3
combiner1 += 8
combiner1 += 1
val result = combiner1.result()
val array = Array(7, 2, 3, 8, 1)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result after simple combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
val combiner2 = DLLCombinerTest(2)
combiner2 += 3
combiner2 += 8
val combiner3 = DLLCombinerTest(2)
combiner2 += 1
combiner2 += 9
val combiner4 = DLLCombinerTest(2)
combiner2 += 3
combiner2 += 2
val result = combiner1.combine(combiner2).combine(combiner3).combine(combiner4).result()
val array = Array(7, 2, 3, 8, 1, 9, 3, 2)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result - empty combiner (20pts)") {
val combiner1 = DLLCombinerTest(2)
val result = combiner1.result()
assertEquals(result.size, 0)
}
test("[Public] fetch result - full single element combiner (15pts)") {
val combiner1 = DLLCombinerTest(3)
combiner1 += 4
combiner1 += 2
combiner1 += 6
val result = combiner1.result()
val array = Array(4, 2, 6)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
}
trait AbstractM7Suite extends munit.FunSuite with LibImpl {
def simpleCombiners = buildSimpleCombiners()
def basicCombiners = buildBasicCombiners()
def combinedCombiners = buildCombinedCombiners()
def largeCombiners = buildLargeCombiners()
def buildSimpleCombiners() = {
val simpleCombiners = List(
(DLLCombinerTest(4), Array(4, 2, 6, 1, 5, 4, 3, 5, 6, 3, 4, 5, 6, 3, 4, 5)),
(DLLCombinerTest(4), Array(7, 2, 2, 9, 3, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2)),
(DLLCombinerTest(4), Array.fill(16)(5))
)
simpleCombiners.foreach(elem => build(elem._1, elem._2))
simpleCombiners
}
def buildBasicCombiners() = {
val basicCombiners = List(
(DLLCombinerTest(2), Array(4, 2, 6)),
(DLLCombinerTest(5), Array(4, 2, 6)),
(DLLCombinerTest(3), Array(4, 2, 6, 1, 7, 2, 4)),
(DLLCombinerTest(4), Array(7, 2, 2, 9, 3, 2, 11, 12, 13, 14, 15, 16, 17, 22)),
(DLLCombinerTest(3), Array.fill(16)(7)),
(DLLCombinerTest(3), Array.fill(19)(7)),
(DLLCombinerTest(3), Array.fill(5)(7)),
(DLLCombinerTest(3), Array.fill(6)(7))
)
basicCombiners.foreach(elem => build(elem._1, elem._2))
basicCombiners
}
def buildCombinedCombiners() = {
var combinedCombiners = List[(DLLCombinerTest, Array[Int])]()
Range(1, 10).foreach { chunk_size =>
val array = basicCombiners.filter(elem => elem._1.chunk_size == chunk_size).foldLeft(Array[Int]()) { (acc, i) => acc ++ i._2 }
val combiner = DLLCombinerTest(chunk_size)
basicCombiners.filter(elem => elem._1.chunk_size == chunk_size).foreach(elem => combiner.combine(elem._1))
combinedCombiners = combinedCombiners :+ (combiner, array)
}
combinedCombiners
}
def buildLargeCombiners() = {
val largeCombiners = List(
(DLLCombinerTest(21), Array.fill(1321)(4) ++ Array.fill(1322)(7)),
(DLLCombinerTest(18), Array.fill(1341)(2) ++ Array.fill(1122)(5)),
(DLLCombinerTest(3), Array.fill(1321)(4) ++ Array.fill(1322)(7) ++ Array.fill(321)(4) ++ Array.fill(322)(7)),
(DLLCombinerTest(12), Array.fill(992321)(4) ++ Array.fill(99322)(7)),
(DLLCombinerTest(4), Array.fill(953211)(4) ++ Array.fill(999322)(1))
)
largeCombiners.foreach(elem => build(elem._1, elem._2))
largeCombiners
}
def build(combiner: DLLCombinerTest, array: Array[Int]): DLLCombinerTest = {
array.foreach(elem => combiner += elem)
combiner
}
def compare(combiner: DLLCombinerTest, array: Array[Int]): Boolean = {
val result = combiner.result()
Range(0,array.size).forall(i => array(i) == result(i))
}
def buildAndCompare(combiner: DLLCombinerTest, array: Array[Int]): Boolean = {
array.foreach(elem => combiner += elem)
val result = combiner.result()
Range(0,array.size).forall(i => array(i) == result(i))
}
}
trait LibImpl extends M7 {
val forkJoinPool = new ForkJoinPool
abstract class TaskScheduler {
def schedule[T](body: => T): ForkJoinTask[T]
}
class DefaultTaskScheduler extends TaskScheduler {
def schedule[T](body: => T): ForkJoinTask[T] = {
val t = new RecursiveTask[T] {
def compute = body
}
Thread.currentThread match {
case wt: ForkJoinWorkerThread =>
t.fork()
case _ =>
forkJoinPool.execute(t)
}
t
}
}
val scheduler =
new DynamicVariable[TaskScheduler](new DefaultTaskScheduler)
def task[T](body: => T): ForkJoinTask[T] = {
scheduler.value.schedule(body)
}
class DLLCombinerTest(chunk_size: Int = 3) extends DLLCombinerImplementation(chunk_size) {
override def +=(elem: Int): Unit = {
if(cnt % chunk_size == 0) {
chunks = chunks + 1
val node = new Node(chunk_size)
if (cnt == 0) {
head = node
last = node
}
else {
last.next = node
node.previous = last
last = node
}
}
last.add(elem)
cnt += 1
}
override def combine(that: DLLCombiner): DLLCombiner = {
assert(this.chunk_size == that.chunk_size)
if (this.cnt == 0) {
this.head = that.head
this.last = that.last
this.cnt = that.cnt
this.chunks = that.chunks
this
}
else if (that.cnt == 0)
this
else {
this.last.next = that.head
that.head.previous = this.last
this.cnt = this.cnt + that.cnt
this.chunks = this.chunks + that.chunks
this.last = that.last
this
}
}
}
}
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted