Skip to content

Commit 6d422e7

Browse files
committed
Merge branch 'main' into renovate/protobuf
# Conflicts: # gradle/libs.versions.toml
2 parents a7da771 + d4c69b3 commit 6d422e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2431
-443
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
run: chmod +x gradlew
3939

4040
- name: Cache Gradle dependencies
41-
uses: actions/cache@v4.2.2
41+
uses: actions/cache@v4.2.3
4242
with:
4343
path: ${{ env.GRADLE_HOME }}/caches
4444
key: |

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,22 @@
2222

2323
### Metrics
2424
```text
25-
14883 number of properties
26-
10262 number of functions
27-
8797 number of classes
28-
227 number of packages
29-
3461 number of kt files
25+
14991 number of properties
26+
10385 number of functions
27+
8856 number of classes
28+
229 number of packages
29+
3486 number of kt files
3030
```
3131

3232

3333
### Complexity Report
3434
```text
35-
257847 lines of code (loc)
36-
157813 source lines of code (sloc)
37-
115157 logical lines of code (lloc)
38-
72253 comment lines of code (cloc)
39-
24539 cyclomatic complexity (mcc)
40-
20171 cognitive complexity
35+
259507 lines of code (loc)
36+
158943 source lines of code (sloc)
37+
115975 logical lines of code (lloc)
38+
72503 comment lines of code (cloc)
39+
24705 cyclomatic complexity (mcc)
40+
20233 cognitive complexity
4141
0 number of total code smells
4242
45 comment source ratio
4343
213 mcc per 1,000 lloc

build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ plugins {
5151
}
5252

5353
jacoco {
54-
toolVersion = "0.8.12"
54+
toolVersion = "0.8.13"
5555
}
5656

5757
repositories {
@@ -330,8 +330,8 @@ dependencies {
330330
implementation("org.openjdk.jol:jol-core:0.17")
331331
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
332332
implementation(jsoup)
333-
implementation("com.google.protobuf:protobuf-java:4.30.0")
334-
implementation("com.google.protobuf:protobuf-kotlin-lite:4.30.0")
333+
implementation("com.google.protobuf:protobuf-java:4.30.2")
334+
implementation("com.google.protobuf:protobuf-kotlin-lite:4.30.2")
335335
implementation("io.grpc:grpc-stub:1.71.0")
336336
implementation("io.grpc:grpc-protobuf:1.71.0")
337337

gradle/libs.versions.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[versions]
22
coroutines = "1.10.1" # https://github.com/Kotlin/kotlinx.coroutines
3-
junit = "5.12.0" # https://junit.org/junit5 | https://central.sonatype.com/artifact/org.junit.jupiter/junit-jupiter
4-
kotlin = "2.1.10" # https://github.com/JetBrains/kotlin
3+
junit = "5.12.1" # https://junit.org/junit5 | https://central.sonatype.com/artifact/org.junit.jupiter/junit-jupiter
4+
kotlin = "2.1.20" # https://github.com/JetBrains/kotlin
55
detekt = "1.23.8" # https://github.com/detekt/detekt
66
dokka = "2.0.0" # https://github.com/Kotlin/dokka
77
spotless = "7.0.2" # https://github.com/diffplug/spotless
@@ -13,17 +13,17 @@ slf4j = "2.0.17" # https://www.slf4j.org TODO remove
1313
rxjava = "3.1.10" # https://github.com/ReactiveX/RxJava TODO remove
1414
rxkotlin = "3.0.1" # https://github.com/ReactiveX/RxKotlin TODO remove
1515
lincheck = "2.36" # https://github.com/JetBrains/lincheck
16-
serialization = "1.8.0" # https://github.com/Kotlin/kotlinx.serialization
16+
serialization = "1.8.1" # https://github.com/Kotlin/kotlinx.serialization
1717
retrofit = "2.11.0" # https://github.com/square/retrofit TODO remove
1818
okhttp = "5.0.0-alpha.14" # https://square.github.io/okhttp/changelogs/changelog TODO remove
19-
dagger = "2.55" # https://dagger.dev TODO remove
19+
dagger = "2.56.1" # https://dagger.dev TODO remove
2020
jmh = "1.37" # https://github.com/openjdk/jmh
2121
kotlintest = "3.4.2" # https://mvnrepository.com/artifact/io.kotlintest/kotlintest-core
2222
kotest = "5.9.1" # https://kotest.io
2323
assertj = "3.27.3" # https://mvnrepository.com/artifact/org.assertj/assertj-core | https://assertj.github.io/doc
24-
mockito = "5.16.0" # https://mvnrepository.com/artifact/org.mockito/mockito-core
24+
mockito = "5.17.0" # https://mvnrepository.com/artifact/org.mockito/mockito-core
2525
mockito_kotlin = "5.4.0" # https://github.com/mockito/mockito-kotlin
26-
logback = "1.5.17" # https://logback.qos.ch
26+
logback = "1.5.18" # https://logback.qos.ch
2727
hamcrest = "3.0" # https://hamcrest.org/JavaHamcrest/distributables
2828
mockk = "1.13.17" # https://mockk.io
2929
kover = "0.9.1" # https://github.com/Kotlin/kotlinx-kover
@@ -33,7 +33,7 @@ turbine = "1.2.0" # https://github.com/cashapp/turbine/releases
3333
truth = "1.4.4" # https://central.sonatype.com/artifact/com.google.truth/truth/overview
3434
sandwich = "2.1.0" # https://github.com/skydoves/sandwich
3535
protobuf = "0.9.4"
36-
protoc = "4.30.0"
36+
protoc = "4.30.2"
3737

3838
[libraries]
3939
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8", version.ref = "coroutines" }

src/main/kotlin/dev/shtanko/algorithms/img/call_stack_visualization.svg

Lines changed: 1 addition & 0 deletions
Loading

src/main/kotlin/dev/shtanko/algorithms/img/understanding_recursion.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package dev.shtanko.algorithms.recursion
2+
3+
internal interface PrettyPrintFib {
4+
operator fun invoke(n: Int, depth: Int = 0, log: StringAppender = StringAppenderImpl()): Int
5+
}
6+
7+
internal class PrettyPrintFibRecursion : PrettyPrintFib {
8+
9+
private fun logCall(n: Int, depth: Int, log: StringAppender) {
10+
log.append(" ".repeat(depth) + "Call: fib($n)\n")
11+
}
12+
13+
private fun logReturn(n: Int, depth: Int, result: Int, log: StringAppender) {
14+
log.append(" ".repeat(depth) + "Return: fib($n) = $result\n")
15+
}
16+
17+
override fun invoke(n: Int, depth: Int, log: StringAppender): Int {
18+
logCall(n, depth, log)
19+
20+
// Base case: return 0 or 1 for n = 0 or n = 1
21+
if (n == 0) {
22+
logReturn(n, depth, 0, log)
23+
return 0
24+
}
25+
if (n == 1) {
26+
logReturn(n, depth, 1, log)
27+
return 1
28+
}
29+
30+
// Recursive calls to calculate fib(n-1) and fib(n-2)
31+
val result = invoke(n - 1, depth + 1, log) + invoke(n - 2, depth + 1, log)
32+
33+
logReturn(n, depth, result, log)
34+
return result
35+
}
36+
}
37+
38+
internal class PrettyPrintFibOptimized : PrettyPrintFib {
39+
40+
private val memo = mutableMapOf<Int, Int>() // Cache to store Fibonacci results
41+
42+
private fun logCall(n: Int, depth: Int, log: StringAppender) {
43+
log.append(" ".repeat(depth) + "Call: fib($n)\n")
44+
}
45+
46+
private fun logReturn(n: Int, depth: Int, result: Int, log: StringAppender) {
47+
log.append(" ".repeat(depth) + "Return: fib($n) = $result\n")
48+
}
49+
50+
override fun invoke(n: Int, depth: Int, log: StringAppender): Int {
51+
logCall(n, depth, log)
52+
53+
// Check the memoization cache before proceeding with recursion
54+
memo[n]?.let {
55+
logReturn(n, depth, it, log)
56+
return it // Return cached value
57+
}
58+
59+
// Base case: return 0 or 1 for n = 0 or n = 1
60+
if (n == 0) {
61+
logReturn(n, depth, 0, log)
62+
return 0
63+
}
64+
if (n == 1) {
65+
logReturn(n, depth, 1, log)
66+
return 1
67+
}
68+
69+
// Recursive calls to calculate fib(n-1) and fib(n-2), and store the result in the cache
70+
val result = invoke(n - 1, depth + 1, log) + invoke(n - 2, depth + 1, log)
71+
72+
memo[n] = result // Cache the result
73+
logReturn(n, depth, result, log)
74+
return result
75+
}
76+
}
77+
78+
internal class PrettyPrintFibTailRec : PrettyPrintFib {
79+
80+
// Tail-recursive function for Fibonacci
81+
private tailrec fun fibTailRec(n: Int, depth: Int, log: StringAppender, prev: Int = 0, current: Int = 1): Int {
82+
// Log the current call with depth
83+
log.append(" ".repeat(depth) + "Call: fib($n, prev=$prev, current=$current)\n")
84+
85+
// Base case: return the current value when n reaches 0
86+
if (n == 0) {
87+
log.append(" ".repeat(depth) + "Return: fib($n) = $prev\n")
88+
return prev
89+
}
90+
91+
// Recursive case: move to the next Fibonacci number
92+
return fibTailRec(n - 1, depth + 1, log, current, prev + current)
93+
}
94+
95+
override fun invoke(n: Int, depth: Int, log: StringAppender): Int {
96+
return fibTailRec(n, depth, log)
97+
}
98+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dev.shtanko.algorithms.recursion
2+
3+
internal interface PrettyPrintPalindrome {
4+
operator fun invoke(
5+
str: String,
6+
left: Int = 0,
7+
right: Int = str.length - 1,
8+
depth: Int = 0,
9+
log: StringAppender = StringAppenderImpl(),
10+
): Boolean
11+
}
12+
13+
internal class PrettyPrintPalindromeRecursion : PrettyPrintPalindrome {
14+
15+
private fun logCall(str: String, left: Int, right: Int, depth: Int, log: StringAppender) {
16+
log.append(" ".repeat(depth) + "Call: isPalindrome(\"$str\", left=$left, right=$right)\n")
17+
}
18+
19+
private fun logReturn(left: Int, right: Int, result: Boolean, depth: Int, log: StringAppender) {
20+
log.append(" ".repeat(depth) + "Return: isPalindrome(left=$left, right=$right) = $result\n")
21+
}
22+
23+
override fun invoke(str: String, left: Int, right: Int, depth: Int, log: StringAppender): Boolean {
24+
logCall(str, left, right, depth, log)
25+
26+
// Base case: if pointers have met or crossed, it's a palindrome
27+
if (left >= right) {
28+
logReturn(left, right, true, depth, log)
29+
return true
30+
}
31+
32+
// If characters at left and right don't match, return false
33+
if (str[left] != str[right]) {
34+
logReturn(left, right, false, depth, log)
35+
return false
36+
}
37+
38+
// Recursive call moving inward
39+
val result = invoke(str, left + 1, right - 1, depth + 1, log)
40+
41+
logReturn(left, right, result, depth, log)
42+
return result
43+
}
44+
}
45+
46+
fun main() {
47+
val palindromeChecker = PrettyPrintPalindromeRecursion()
48+
val log = StringAppenderImpl()
49+
50+
val input = "racecar"
51+
val result = palindromeChecker.invoke(input, 0, input.length - 1, 0, log)
52+
53+
println("Is \"$input\" a palindrome? $result")
54+
println("Log:\n${log.get()}")
55+
}

src/main/kotlin/dev/shtanko/algorithms/recursion/RECURSION.md

Whitespace-only changes.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package dev.shtanko.algorithms.recursion
2+
3+
/**
4+
* An interface defining a contract for appending strings and retrieving the final result.
5+
*/
6+
internal interface StringAppender {
7+
8+
/**
9+
* Appends the given [str] to the internal string representation.
10+
*
11+
* @param str The string to append.
12+
*/
13+
fun append(str: String)
14+
15+
/**
16+
* Retrieves the current concatenated string.
17+
*
18+
* @return The complete string built so far.
19+
*/
20+
fun get(): String
21+
22+
/**
23+
* Clears the internal string representation, resetting it to an empty state.
24+
*/
25+
fun clear()
26+
}
27+
28+
/**
29+
* A concrete implementation of [StringAppender] using [StringBuilder] for efficient string operations.
30+
*/
31+
internal class StringAppenderImpl : StringAppender {
32+
private val sb = StringBuilder()
33+
34+
/**
35+
* Appends the given [str] to the internal [StringBuilder].
36+
*
37+
* @param str The string to append.
38+
*/
39+
override fun append(str: String) {
40+
sb.append(str)
41+
}
42+
43+
/**
44+
* Retrieves the concatenated string from the internal [StringBuilder].
45+
*
46+
* @return The complete string built so far.
47+
*/
48+
override fun get(): String {
49+
return sb.toString()
50+
}
51+
52+
/**
53+
* Clears the internal [StringBuilder], resetting it to an empty state.
54+
*/
55+
override fun clear() {
56+
sb.setLength(0)
57+
}
58+
}

0 commit comments

Comments
 (0)