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
  • lara/cs206-demos
  • gcharles/cs206-demos
  • gambhir/cs206-demos
3 results
Show changes
Showing
with 930 additions and 0 deletions
package benchmarks
import org.openjdk.jmh.annotations.*
@State(Scope.Benchmark)
class AppendBenchmark:
@Param(Array("List", "Array"))
var seqType: String = _
var bigSeq: Seq[Int] = _
@Setup(Level.Trial)
def setup =
bigSeq = seqType match
case "List" => (1 to 10000).toList
case "Array" => (1 to 10000).toArray.toIndexedSeq
@Benchmark
def bench00_prepend = 0 +: bigSeq
@Benchmark
def bench01_append = bigSeq :+ 0
package benchmarks
import org.openjdk.jmh.annotations.*
@State(Scope.Benchmark)
class SumBenchmark:
val bigArray: Array[Int] = (1 to 10000).toArray
val bigList: List[Int] = (1 to 10000).toList
@Benchmark
def bench00_arraySumMethod =
bigArray.sum
@Benchmark
def bench01_arrayWhileLoopIndex(): Int =
val length = bigArray.length
var i = 0
var sum = 0
while i < length do
sum += bigArray(i)
i = i + 1
sum
@Benchmark
def bench02_arrayForLoopIndex(): Int =
var sum = 0
for i <- 0 until bigArray.length do sum += bigArray(i)
sum
final class OptimizedRange(from: Int, to: Int):
inline def foreach(inline f: Int => Unit): Unit =
var i = from
while i < to do
f(i)
i = i + 1
@Benchmark
def bench03_arrayOptimizedForLoopIndex() =
var sum = 0
for i <- OptimizedRange(0, bigArray.length) do sum += bigArray(i)
sum
@Benchmark
def bench04_arrayForLoop() =
var sum = 0
for n <- bigArray do sum += n
sum
extension (array: Array[Int])
inline def optimizedForeach(f: Int => Unit): Unit =
val to = array.length
var i = 0
while i < to do
f(i)
i = i + 1
@Benchmark
def bench05_arrayOptimizedForeach() =
var sum = 0
bigArray.optimizedForeach(sum += _)
sum
extension (array: Array[Int])
inline def optimizedForeachArgInlined(inline f: Int => Unit): Unit =
val to = array.length
var i = 0
while i < to do
f(i)
i = i + 1
@Benchmark
def bench06_arrayOptimizedForeachArgInlined() =
var sum = 0
bigArray.optimizedForeach(sum += _)
sum
@Benchmark
def bench07_listSumMethod =
bigList.sum
@Benchmark
def bench08_listForLoopIndex(): Int =
var sum = 0
for i <- 0 until bigList.length do sum += bigList(i)
sum
@Benchmark
def bench09_listForLoop() =
var sum = 0
for n <- bigList do sum += n
sum
extension (list: List[Int])
inline def optimizedForeach(f: Int => Unit): Unit =
var curr = list
while curr != Nil do
f(curr.head)
curr = curr.tail
@Benchmark
def bench10_listOptimizedForeach() =
var sum = 0
bigList.optimizedForeach(sum += _)
sum
extension (list: List[Int])
inline def optimizedForeachArgInlined(inline f: Int => Unit): Unit =
var curr = list
while curr != Nil do
f(curr.head)
curr = curr.tail
@Benchmark
def bench11_listOptimizedForeachArgInlined() =
var sum = 0
bigList.optimizedForeach(sum += _)
sum
package benchmarks.midterm22
import org.openjdk.jmh.annotations.*
@State(Scope.Benchmark)
//@Fork(jvmArgsAppend = Array("-Djava.util.concurrent.ForkJoinPool.common.parallelism=4"))
abstract class AbstractCollectionBenchmark:
@Param(Array("10000", "100000", "1000000", "10000000"))
var size: Int = _
@Param(Array("Vector", "Array", "ArrayBuffer", "List"))
var collection: String = _
var haystack: Iterable[Int] = _
@Setup(Level.Invocation)
def setup() =
val gen = (1 to (size * 2) by 2)
haystack = collection match
case "Vector" => gen.toVector
case "Array" => gen.toArray
case "ArrayBuffer" => gen.toBuffer
case "List" => gen.toList
package benchmarks.midterm22
import org.openjdk.jmh.annotations.*
class CollectionBenchmark extends AbstractCollectionBenchmark:
@Benchmark
def take() =
haystack.take(size / 2)
@Benchmark
def drop() =
haystack.drop(size / 2)
package benchmarks.midterm22
import midterm22.contains
import org.openjdk.jmh.annotations.*
class MidtermPart2Benchmark extends AbstractCollectionBenchmark:
val needle = 10
@Param(Array("true", "false"))
var parallel: Boolean = _
@Setup(Level.Invocation)
override def setup() =
super.setup()
midterm22.parallelismEnabled = parallel
@Benchmark
def containsBenchmark() =
contains(haystack, needle)
package concpar20final03
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
def sequence1[A](fs: List[Future[A]]): Future[List[A]] =
fs match
case f :: fs =>
for
x <- f
xs <- sequence1(fs)
yield x :: xs
case Nil =>
Future.successful(Nil)
def sequence2[A](fs: List[Future[A]]): Future[List[A]] =
fs match
case f :: fs =>
f.flatMap(x => sequence2(fs).map(xs => x :: xs))
case Nil =>
Future.successful(Nil)
def sequence3[A](fs: List[Future[A]]): Future[List[A]] =
fs match
case f :: fs =>
f.transformWith {
case Failure(e) => Future.failed(e)
case Success(value) =>
sequence3(fs).transform {
case res: Failure[List[A]] => res
case Success(values) => Success(value :: values)
}
}
case Nil =>
Future.successful(Nil)
def sequence4[A](fs: List[Future[A]]): Future[List[A]] =
fs match
case f :: fs =>
val fsFuture: Future[List[A]] = sequence4(fs)
f.zip(fsFuture).map((fValue, fsValue) => fValue :: fsValue)
case Nil =>
Future.successful(Nil)
@main def concpar20final03 =
val xs = List(Future { 1 }, Future { 2 }, Future { 3 })
val ys = List(Future { 1 }, Future.failed(Error("BOOM")), Future { 3 })
println(Await.result(sequence1(xs), Duration.Inf))
// println(Await.result(sequence1(xs), Duration.Inf)) // Throws exception
println(Await.result(sequence2(xs), Duration.Inf))
// println(Await.result(sequence2(ys), Duration.Inf)) // Throws exception
println(Await.result(sequence3(xs), Duration.Inf))
// println(Await.result(sequence3(ys), Duration.Inf)) // Throws exception
println(Await.result(sequence4(xs), Duration.Inf))
// println(Await.result(sequence4(ys), Duration.Inf)) // Throws exception
package concpar21final01
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
case class Grade(sciper: Int, grade: Double)
trait Problem1:
/** Retrieve the list of student grades, sorted such that maximum grades
* appear at the head of the list.
*/
def leaderboard(): Future[List[Grade]] =
getScipers()
.flatMap { scipers =>
Future.sequence(scipers.map(getGrade))
}
.map(_.flatten.sortBy(_.grade).reverse)
/** Retrieve a student's grade using GitLab's API. The result is wrapped in an
* option, where `Future(None)` indicates either:
* - the student is not registered to the class
* - the student did not push his/her solution to GitLab
*/
def getGrade(sciper: Int): Future[Option[Grade]]
/** Retrieve the list of enrolled students from IS-academia
*/
def getScipers(): Future[List[Int]]
package concpar21final01
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.Random
class Problem1MockData extends Problem1:
def getGrade(sciper: Int): Future[Option[Grade]] =
Future {
// In an actual implementation, this is where we would make a call to
// the GitLab APIs. This mock returns a random grade after a short delay.
Thread.sleep(15) // GitLab is pretty fast today...
val rand = new Random(sciper)
val grade = rand.nextInt(6).toDouble + rand.nextDouble()
if sciper < 100000 || sciper > 999999 || sciper % 10 == 0 then None
else Some(Grade(sciper, grade))
}
/** Retrieve the list of enrolled students from IS-academia
*/
def getScipers(): Future[List[Int]] =
Future {
Thread.sleep(100)
List( // A fake list of SCIPER numbers
301425, 207372, 320658, 300217, 224523, 301068, 331020, 331095, 320270,
320742, 299310, 300974, 322202, 343357, 302632, 343366, 320229, 269364,
320004, 321830, 219188, 300834, 320992, 299237, 298016, 300397, 269857,
300492, 300481, 279254, 320967, 300443, 300329, 300305, 331158, 310402,
279067, 300682, 259825, 351616, 310869, 301215, 299481, 269375, 351249,
310866, 351141, 301530, 361378, 351661, 351524, 311081, 331137, 332319,
301045, 300393, 300308, 310889, 310064, 310841, 351333, 310382, 333887,
333837, 320832, 321397, 351691, 269125, 312732, 351546, 301783, 351698,
310775, 331388, 311139, 301992, 301578, 361760, 351174, 310298, 300666,
259778, 301554, 301278, 301669, 321372, 311347, 321129, 351490, 321189,
301336, 341560, 331220, 331129, 333927, 279186, 310596, 299135, 279226,
310507, 269049, 300309, 341524, 351143, 300785, 310612, 320338, 259980,
269952, 310397, 320246, 310959, 301454, 301835, 301802, 301649, 301170,
301908, 351708, 321046, 361490, 311070, 351830, 311054, 311912, 301913,
361232, 301030, 351723, 311472, 311166, 321057, 310793, 269462, 311948,
321693, 321056, 361765, 301453, 321626, 341490, 320892, 269871, 269580,
320199, 320908, 320830, 269071, 380542, 253768, 311204, 269127, 351073,
341327, 301792, 299789, 361424, 301525, 311637, 321423, 279111, 330126,
310371, 259888, 269525, 299585, 300147, 341402, 330067, 311796, 279037,
248517, 301436, 269965, 259963, 320720, 248583, 259709, 361204, 341500,
311803, 299981, 311832, 301088, 259649, 279183, 341760, 311844, 279079,
390997, 311917, 390999, 361122, 301208, 311538, 272943, 361570, 390959)
}
package concpar21final02
import akka.actor.*
import scala.collection.mutable
import akka.testkit.*
object Problem2:
//////////////////////////////
// NOTIFICATION SERVICE //
//////////////////////////////
object NotificationService:
enum Protocol:
/** Notify all registered actors */
case NotifyAll
/** Register the actor that sent the `Register` request */
case Register //
/** Un-register the actor that sent the `Register` request */
case UnRegister
enum Responses:
/** Message sent to an actor when it is notified */
case Notification
/** Response sent to an actor after a `Register` or `UnRegister` */
case Registered(registered: Boolean)
class NotificationService extends Actor:
import NotificationService.Protocol.*
import NotificationService.Responses.*
private val registeredUsers = mutable.Set.empty[ActorRef]
def receive: Receive = {
case Register =>
registeredUsers += sender()
sender() ! Registered(true)
case UnRegister =>
registeredUsers -= sender()
sender() ! Registered(false)
case NotifyAll =>
for user <- registeredUsers do user ! Notification
}
/////////////////////////
// DISCORD CHANNEL //
/////////////////////////
object DiscordChannel:
enum Protocol:
/** Post a message in the channel */
case Post(msg: String)
/** Ask for the list of most recent posts starting from the most recent
* one. The list must have at most `limit` posts.
*/
case GetLastPosts(limit: Int)
/** Activates the service channel using the provided notification service.
*/
case Init(notificationService: ActorRef)
enum Responses:
/** Response to `GetLastPosts` if active */
case Posts(msgs: List[String])
/** Response after `Init` if non-active */
case Active
/** Response `Post` and `GetLastPosts` if non-active */
case NotActive
/** Response after `Init` if active */
case AlreadyActive
class DiscordChannel extends Actor:
import DiscordChannel.Protocol.*
import DiscordChannel.Responses.*
import NotificationService.Protocol.*
private var messages: List[String] = Nil
def receive: Receive = nonActive
def nonActive: Receive = {
case Init(service) =>
context.become(active(service))
sender() ! Active
case Post(_) | GetLastPosts(_) =>
sender() ! NotActive
}
def active(notificationService: ActorRef): Receive = {
case Post(msg) =>
messages = msg :: messages
notificationService ! NotifyAll
case GetLastPosts(limit) =>
sender() ! Posts(messages.take(limit))
case Init(_) =>
sender() ! AlreadyActive
}
/////////////////////////
// DEBUG //
/////////////////////////
/** Infrastructure to help debugging. In sbt use `run` to execute this code. The
* TestKit is an actor that can send messages and check the messages it
* receives (or not).
*/
@main def debug() = new TestKit(ActorSystem("DebugSystem")) with ImplicitSender:
import Problem2.*
import DiscordChannel.Protocol.*
import DiscordChannel.Responses.*
import NotificationService.Protocol.*
import NotificationService.Responses.*
import concurrent.duration.*
try
val notificationService = system.actorOf(Props[NotificationService]())
val channel = system.actorOf(Props[DiscordChannel]())
notificationService ! NotifyAll
expectNoMessage(
200.millis
) // expects no message is received in the next 200 milliseconds
notificationService ! Register
expectMsg(
200.millis,
Registered(true)
) // expects to receive `Registered(true)` in the next 200 milliseconds
finally shutdown(system)
package concpar21final03
import instrumentation.*
import scala.collection.mutable
import scala.collection.concurrent.TrieMap
type FileName = String
/** An API for manipulating files. */
trait FileSystem:
/** Create a new file named `file` with the passed `content`. */
def createFile(file: FileName, content: String): Unit
/** If `file` exists, return its content, otherwise crashes. */
def readFile(file: FileName): String
/** If `file` exists, delete it, otherwise crash. */
def deleteFile(file: FileName): Unit
end FileSystem
/** An in-memory file system for testing purposes implemented using a Map.
*
* Every method in this class is thread-safe.
*/
class InMemoryFileSystem extends FileSystem:
val fsMap: mutable.Map[FileName, String] = TrieMap()
def createFile(file: FileName, content: String): Unit =
assert(!fsMap.contains(file), s"$file already exists")
fsMap(file) = content
def readFile(file: FileName): String =
fsMap.get(file) match
case Some(content) => content
case None => assert(false, s"Attempt to read non-existing $file")
def deleteFile(file: FileName): Unit =
fsMap.remove(file)
end InMemoryFileSystem
package concpar21final03
import instrumentation.*
/** A synchronization mechanism allowing multiple reads to proceed concurrently
* with an update to the state.
*/
class RCU extends Monitor:
protected val latestVersion: AtomicLong = AtomicLong(0)
protected val readersVersion: ThreadMap[Long] = ThreadMap()
/** This method must be called before accessing shared data for reading. */
def startRead(): Unit =
assert(
!readersVersion.currentThreadHasValue,
"startRead() cannot be called multiple times without an intervening stopRead()"
)
readersVersion.setCurrentThreadValue(latestVersion.get)
/** Once a thread which has previously called `startRead` has finished reading
* shared data, it must call this method.
*/
def stopRead(): Unit =
assert(
readersVersion.currentThreadHasValue,
"stopRead() cannot be called without a preceding startRead()"
)
readersVersion.deleteCurrentThreadValue()
/** Wait until all reads started before this method was called have finished,
* then return.
*/
def waitForOldReads(): Unit =
val newVersion = latestVersion.incrementAndGet()
readersVersion.waitForall(_ >= newVersion)
package concpar21final03
import instrumentation.*
import scala.collection.mutable
/** A map which associates every thread to at most one value of type A.
*
* Every method in this class is thread-safe.
*/
class ThreadMap[A] extends Monitor:
protected val theMap: mutable.Map[Thread, A] = mutable.Map()
/** Return the value in the map entry for the current thread if it exists,
* otherwise None.
*/
def currentThreadValue: Option[A] = synchronized {
theMap.get(Thread.currentThread)
}
/** Is there a map entry for the current thread? */
def currentThreadHasValue: Boolean =
synchronized {
theMap.contains(Thread.currentThread)
}
/** Set the map entry of the current thread to `value` and notify any thread
* waiting on `waitForall`.
*/
def setCurrentThreadValue(value: A): Unit =
synchronized {
theMap(Thread.currentThread) = value
notifyAll()
}
/** Delete the map entry associated with this thread (if it exists) and notify
* all threads waiting in `waitForall`.
*/
def deleteCurrentThreadValue(): Unit =
synchronized {
theMap.remove(Thread.currentThread)
notifyAll()
}
/** Wait until `predicate` returns true for all map entries, then return. */
def waitForall(predicate: A => Boolean): Unit =
synchronized {
while !theMap.forall((_, value) => predicate(value)) do wait()
}
end ThreadMap
package concpar21final03
import instrumentation.*
class UpdateServer(fs: FileSystem) extends Monitor:
val rcu = new RCU
/** The name of the file containing the latest update.
*
* This is `@volatile` to guarantee that `fetchUpdate` always sees the latest
* filename.
*/
@volatile private var updateFile: Option[FileName] = None
/** Return the content of the latest update if one is available, otherwise
* None.
*
* This method is thread-safe.
*/
def fetchUpdate(): Option[String] =
// TODO: use `rcu`
rcu.startRead()
val value = updateFile.map(fs.readFile)
rcu.stopRead()
value
/** Define a new update, more precisely this will:
* - Create a new update file called `newName` with content `newContent`
* - Ensure that any future call to `fetchUpdate` returns the new update
* content.
* - Delete the old update file.
*
* This method is _NOT_ thread-safe, it cannot be safely called from multiple
* threads at once.
*/
def newUpdate(newName: FileName, newContent: String): Unit =
// TODO: use `rcu`
val oldFile = updateFile
fs.createFile(newName, newContent)
updateFile = Some(newName)
rcu.waitForOldReads()
oldFile.foreach(fs.deleteFile)
end UpdateServer
package concpar21final03.instrumentation
/** A long value that may be updated atomically. */
class AtomicLong(initial: Long):
private val atomic = new java.util.concurrent.atomic.AtomicLong(initial)
/** Get the current value. */
def get: Long = atomic.get()
/** Set to the given `value`. */
def set(value: Long): Unit = atomic.set(value)
/** Atomically increment by one the current value and return the _original_
* value.
*/
def getAndIncrement(): Long =
atomic.getAndIncrement()
/** Atomically increment by one the current value and return the _updated_
* value.
*/
def incrementAndGet(): Long =
atomic.incrementAndGet()
/** Atomically set the value to `newValue` if the current value == `expected`.
*
* Return true if successful, otherwise return false to indicate that the
* actual value was not equal to the expected value.
*/
def compareAndSet(expected: Long, newValue: Long): Boolean =
atomic.compareAndSet(expected, newValue)
end AtomicLong
package concpar21final03.instrumentation
class Dummy
trait Monitor:
implicit val dummy: Dummy = new Dummy
def wait()(implicit i: Dummy) = waitDefault()
def synchronized[T](e: => T)(implicit i: Dummy) = synchronizedDefault(e)
def notify()(implicit i: Dummy) = notifyDefault()
def notifyAll()(implicit i: Dummy) = notifyAllDefault()
private val lock = new AnyRef
// Can be overridden.
def waitDefault(): Unit = lock.wait()
def synchronizedDefault[T](toExecute: => T): T = lock.synchronized(toExecute)
def notifyDefault(): Unit = lock.notify()
def notifyAllDefault(): Unit = lock.notifyAll()
package concpar22final01
trait Problem1 extends Lib:
class DLLCombinerImplementation extends DLLCombiner:
// Copies every other Integer element of data array, starting from the first (index 0), up to the middle
def task1(data: Array[Int]) = task {
var current = first
var i = 0
while current != null && i < size / 2 do
data(i) = current.value
i += 2
current = current.getNext2
}
// Copies every other Integer element of data array, starting from the second, up to the middle
def task2(data: Array[Int]) = task {
var current = second
var i = 1
while current != null && i < size / 2 do
data(i) = current.value
i += 2
current = current.getNext2
}
// Copies every other Integer element of data array, starting from the second to last, up to the middle
def task3(data: Array[Int]) = task {
var current = secondToLast
var i = size - 2
while current != null && i >= size / 2 do
data(i) = current.value
i -= 2
current = current.getPrevious2
}
// Copies every other Integer element of data array, starting from the last, up to the middle
// This is executed on the current thread.
def task4(data: Array[Int]) =
var current = last
var i = size - 1
while current != null && i >= size / 2 do
data(i) = current.value
i -= 2
current = current.getPrevious2
def result(): Array[Int] =
val data = new Array[Int](size)
val t1 = task1(data)
val t2 = task2(data)
val t3 = task3(data)
task4(data)
t1.join()
t2.join()
t3.join()
data
package concpar22final01
import java.util.concurrent.*
import scala.util.DynamicVariable
trait Lib:
class Node(val value: Int):
protected var next: Node = null // null for last node.
protected var next2: Node = null // null for last node.
protected var previous: Node = null // null for first node.
protected var previous2: Node = null // null for first node.
def getNext: Node = next // do NOT use in the result method
def getNext2: Node = next2
def getPrevious: Node = previous // do NOT use in the result method
def getPrevious2: Node = previous2
def setNext(n: Node): Unit = next = n
def setNext2(n: Node): Unit = next2 = n
def setPrevious(n: Node): Unit = previous = n
def setPrevious2(n: Node): Unit = previous2 = n
// Simplified Combiner interface
// Implements methods += and combine
// Abstract methods should be implemented in subclasses
abstract class DLLCombiner:
var first: Node = null // null for empty lists.
var last: Node = null // null for empty lists.
var second: Node = null // null for empty lists.
var secondToLast: Node = null // null for empty lists.
var size: Int = 0
// Adds an Integer to this array combiner.
def +=(elem: Int): Unit =
val node = new Node(elem)
if size == 0 then
first = node
last = node
size = 1
else
last.setNext(node)
node.setPrevious(last)
node.setPrevious2(last.getPrevious)
if size > 1 then last.getPrevious.setNext2(node)
else second = node
secondToLast = last
last = node
size += 1
// Combines this array combiner and another given combiner in constant O(1) complexity.
def combine(that: DLLCombiner): DLLCombiner =
if this.size == 0 then that
else if that.size == 0 then this
else
this.last.setNext(that.first)
this.last.setNext2(that.first.getNext)
if this.last.getPrevious != null then
this.last.getPrevious.setNext2(that.first) // important
that.first.setPrevious(this.last)
that.first.setPrevious2(this.last.getPrevious)
if that.first.getNext != null then
that.first.getNext.setPrevious2(this.last) // important
if this.size == 1 then second = that.first
this.size = this.size + that.size
this.last = that.last
this.secondToLast = that.secondToLast
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]
package concpar22final02
import instrumentation.Monitor
abstract class AbstractBarrier(val numThreads: Int) extends Monitor:
var count = numThreads
def awaitZero(): Unit
def countDown(): Unit
package concpar22final02
class Barrier(numThreads: Int) extends AbstractBarrier(numThreads):
def awaitZero(): Unit =
synchronized {
while count > 0 do wait()
}
def countDown(): Unit =
synchronized {
count -= 1
if count <= 0 then notifyAll()
}
package concpar22final02
import scala.collection.mutable.ArrayBuffer
class ImageLib(size: Int):
val buffer1: ArrayBuffer[ArrayBuffer[Int]] = ArrayBuffer.fill(size, size)(1)
val buffer2: ArrayBuffer[ArrayBuffer[Int]] = ArrayBuffer.fill(size, size)(0)
enum Filter(val kernel: Array[Array[Int]]):
case Outline extends Filter(Array(
Array(-1, -1, -1),
Array(-1, 8, -1),
Array(-1, -1, -1)
))
case Sharpen extends Filter(Array(
Array(0, -1, 0),
Array(-1, 5, -1),
Array(0, -1, 0)
))
case Emboss
extends Filter(Array(Array(-2, -1, 0), Array(-1, 1, 1), Array(0, 1, 2)))
case Identity
extends Filter(Array(Array(0, 0, 0), Array(0, 1, 0), Array(0, 0, 0)))
def init(input: ArrayBuffer[ArrayBuffer[Int]]) =
for i <- 0 to size - 1 do
for j <- 0 to size - 1 do
buffer1(i)(j) = input(i)(j)
def computeConvolution(
kernel: Array[Array[Int]],
input: ArrayBuffer[ArrayBuffer[Int]],
row: Int,
column: Int
): Int =
val displacement = Array(-1, 0, 1)
var output = 0
for i <- 0 to 2 do
for j <- 0 to 2 do
val newI = row + displacement(i)
val newJ = column + displacement(j)
if newI < 0 || newI >= size || newJ < 0 || newJ >= size then output += 0
else output += (kernel(i)(j) * input(newI)(newJ))
output
def applyFilter(
kernel: Array[Array[Int]],
input: ArrayBuffer[ArrayBuffer[Int]],
output: ArrayBuffer[ArrayBuffer[Int]],
row: Int
): Unit =
for i <- 0 to input(row).size - 1 do
output(row)(i) = computeConvolution(kernel, input, row, i)