Skip to content

Commit fd0a051

Browse files
author
William Raendchen
committed
init
GitPrivacy: bEo5O1lqfvITbSRgspJwRgLm6o+nTuIdQ7Vza3c22BT+u+3sFNAJ/8p2mDz0l4iNTSLgP9a9vQA= eu6AVfO7A6HqaVB4yxON/gBUCrlljfDTxiQ3yeXz4wigqlAlpDdZT+A+QzctHxtoq6+4kDKChxo=
0 parents  commit fd0a051

16 files changed

+287
-0
lines changed

.gitignore

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/project/project/target/
2+
/project/target/
3+
/target
4+
/.idea/libraries
5+
/.idea/modules/scalaandjava.iml
6+
/.bsp/sbt.json
7+
/.idea/.name
8+
/.idea/uiDesigner.xml
9+
/.idea/modules/
10+
/.idea/vcs.xml
11+
/.idea/modules.xml
12+
/.idea/misc.xml
13+
/.idea/scala_compiler.xml
14+
/.idea/scala_settings.xml
15+
/.idea/compiler.xml

.idea/.gitignore

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/codeStyles/Project.xml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/codeStyles/codeStyleConfig.xml

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/sbt.xml

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.scalafmt.conf

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version = 3.8.2
2+
runner.dialect = scala3
3+
4+
maxColumn = 120
5+
align.preset = most
6+
danglingParentheses.preset = true
7+
rewrite.rules = [RedundantBraces, RedundantParens]
8+
binPack.parentConstructors = false
9+
trailingCommas = keep

build.sbt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.collection.Seq
2+
3+
ThisBuild / version := "0.0.1-SNAPSHOT"
4+
ThisBuild / scalaVersion := "3.6.1"
5+
ThisBuild / javacOptions ++= Seq("--release", "17")
6+
7+
libraryDependencies ++= Seq(
8+
"com.lihaoyi" %% "upickle" % "4.0.2",
9+
"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19",
10+
"org.scalatest" %% "scalatest" % "3.2.18" % Test,
11+
"org.scalatestplus" %% "mockito-5-12" % "3.2.19.0" % Test,
12+
"org.scalamock" %% "scalamock" % "6.0.0" % Test,
13+
)

project/build.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version = 1.10.4

project/plugins.sbt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2")
2+
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0")

src/main/java/JavaPlayground/App.java

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package JavaPlayground;
2+
3+
import ScalaPlayground.RomanNumerals.ScalaRomansPipeline;
4+
5+
class App {
6+
public static void main(String[] args) {
7+
JavaRomans javaRomans = new JavaRomans();
8+
System.out.println(javaRomans.toNumeral(999));
9+
10+
ScalaRomansPipeline scalaRomansPipeline = new ScalaRomansPipeline();
11+
System.out.println(scalaRomansPipeline.toNumeral(999));
12+
13+
ScalaCalculator calculator = new ScalaCalculator();
14+
System.out.println(calculator.add(1, 2));
15+
}
16+
}
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package JavaPlayground;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.concurrent.atomic.AtomicInteger;
6+
7+
public class JavaRomans {
8+
private static final List<Map.Entry<Integer, String>> valueList = List.of(
9+
Map.entry(1000, "M"),
10+
Map.entry(900, "CM"),
11+
Map.entry(500, "D"),
12+
Map.entry(400, "CD"),
13+
Map.entry(100, "C"),
14+
Map.entry(90, "XC"),
15+
Map.entry(50, "L"),
16+
Map.entry(40, "XL"),
17+
Map.entry(10, "X"),
18+
Map.entry(9, "IX"),
19+
Map.entry(5, "V"),
20+
Map.entry(4, "IV"),
21+
Map.entry(1, "I")
22+
);
23+
24+
public String toNumeral(Integer number) {
25+
StringBuilder sb = new StringBuilder();
26+
AtomicInteger remaining = new AtomicInteger(number);
27+
28+
valueList.forEach(entry -> {
29+
while (remaining.get() >= entry.getKey()) {
30+
remaining.addAndGet(-entry.getKey());
31+
sb.append(entry.getValue());
32+
}
33+
});
34+
35+
return sb.toString();
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package JavaPlayground
2+
3+
class ScalaCalculator:
4+
def add(a: Int, b: Int): Int = a + b
5+
def subtract(a: Int, b: Int): Int = a - b
6+
def multiply(a: Int, b: Int): Int = a * b
7+
def divide(a: Int, b: Int): Int = a / b
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package ScalaPlayground
2+
3+
import JavaPlayground.JavaRomans
4+
import ScalaPlayground.RomanNumerals.ScalaRomansPipeline
5+
6+
object App {
7+
def main(args: Array[String]): Unit = {
8+
val javaRomans = new JavaRomans()
9+
println(javaRomans.toNumeral(1996))
10+
11+
val scalaRomans = ScalaRomansPipeline()
12+
println(scalaRomans.toNumeral(1996))
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package ScalaPlayground.RomanNumerals
2+
3+
trait Romans:
4+
def toNumeral(number: Int): String
5+
def toInt(roman: String): Int
6+
7+
class ScalaRomansImperative extends Romans:
8+
private val valueList: Vector[(Int, String)] = Vector(
9+
1000 -> "M",
10+
900 -> "CM",
11+
500 -> "D",
12+
400 -> "CD",
13+
100 -> "C",
14+
90 -> "XC",
15+
50 -> "L",
16+
40 -> "XL",
17+
10 -> "X",
18+
9 -> "IX",
19+
5 -> "V",
20+
4 -> "IV",
21+
1 -> "I"
22+
)
23+
24+
override def toNumeral(number: Int): String = {
25+
val sb = new StringBuilder
26+
var remaining = number
27+
28+
for ((value, numeral) <- valueList) {
29+
while (remaining >= value) {
30+
sb.append(numeral)
31+
remaining -= value
32+
}
33+
}
34+
35+
sb.toString()
36+
}
37+
38+
override def toInt(roman: String): Int = ???
39+
end ScalaRomansImperative
40+
41+
class ScalaRomansPipeline extends Romans:
42+
override def toNumeral(number: Int): String = {
43+
("I" * number)
44+
.replace("IIIII", "V")
45+
.replace("IIII", "IV")
46+
.replace("VV", "X")
47+
.replace("VIV", "IX")
48+
.replace("XXXXX", "L")
49+
.replace("XXXX", "XL")
50+
.replace("LL", "C")
51+
.replace("LXL", "XC")
52+
.replace("CCCCC", "D")
53+
.replace("CCCC", "CD")
54+
.replace("DD", "M")
55+
.replace("DCD", "CM")
56+
// custom edge cases
57+
//.replace("CMXCIX", "IM") // 999
58+
//.replace("XCIX", "IC") // 99
59+
}
60+
61+
override def toInt(roman: String): Int =
62+
roman
63+
.replace("CM", "DCD")
64+
.replace("M", "DD")
65+
.replace("CD", "CCCC")
66+
.replace("D", "CCCCC")
67+
.replace("XC", "LXL")
68+
.replace("C", "LL")
69+
.replace("XL", "XXXX")
70+
.replace("L", "XXXXX")
71+
.replace("IX", "VIV")
72+
.replace("X", "VV")
73+
.replace("IV", "IIII")
74+
.replace("V", "IIIII")
75+
.length
76+
end ScalaRomansPipeline
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import JavaPlayground.JavaRomans
2+
import ScalaPlayground.RomanNumerals.{ScalaRomansImperative, ScalaRomansPipeline}
3+
import org.scalatest.funsuite.AnyFunSuite
4+
import org.scalatest.prop.TableDrivenPropertyChecks.*
5+
import org.scalatest.prop.TableFor2
6+
7+
class IntToRomanNumeralTest extends AnyFunSuite {
8+
val testCases: TableFor2[Int, String] = Table(
9+
("input", "expected"),
10+
(0, ""),
11+
(1, "I"),
12+
(4, "IV"),
13+
(9, "IX"),
14+
(58, "LVIII"),
15+
(1999, "MCMXCIX"),
16+
(2023, "MMXXIII"),
17+
(1987, "MCMLXXXVII"),
18+
(3999, "MMMCMXCIX"),
19+
)
20+
21+
val javaRomans = new JavaRomans
22+
val scalaRomansPipeline = new ScalaRomansPipeline
23+
val scalaRomansImperative = new ScalaRomansImperative
24+
25+
testCases.foreach { case (input, expected) =>
26+
test(s"Java and Scala implementations return $expected for input $input") {
27+
assert(javaRomans.toNumeral(input) == expected)
28+
assert(scalaRomansPipeline.toNumeral(input) == expected)
29+
assert(scalaRomansImperative.toNumeral(input) == expected)
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import ScalaPlayground.RomanNumerals.ScalaRomansPipeline
2+
import org.scalatest.funsuite.AnyFunSuite
3+
import org.scalatest.prop.TableDrivenPropertyChecks._
4+
5+
class RomanNumeralsBenchmarkTest extends AnyFunSuite {
6+
test("benchmark the performance of Roman Numerals implementations") {
7+
def checkDuration(action: Int => String, input: Int, iterations: Int, message: String): Unit = {
8+
val now = System.nanoTime()
9+
for (_ <- 1 to iterations) action(input)
10+
val elapsed = System.nanoTime() - now
11+
12+
println(message)
13+
println(elapsed / 1_000_000 + " ms")
14+
}
15+
16+
val input = 3999
17+
val iterations = 1_000_000
18+
19+
val javaRomans = new JavaPlayground.JavaRomans()
20+
val scalaRomans = ScalaRomansPipeline()
21+
22+
checkDuration(scalaRomans.toNumeral, input, iterations, "Scala Romans Imperative")
23+
checkDuration(javaRomans.toNumeral, input, iterations, "Java Romans Imperative")
24+
}
25+
}

0 commit comments

Comments
 (0)