Move SSD initial guessing loop to typed Kotlin

ober

f95fa4747505434cbbda6baa912dc925d078abd6

diff --git a/.build.yml b/.build.yml
index 30a6b9f..31f5114 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#9638f2f8238c8ee30d2be26002eb2fb91898a84b"
+  - "https://git.sr.ht/~lisp/jerboa#3b6f07f1a2810e03b9d6f489567f8819b54880b8"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 9638f2f8238c8ee30d2be26002eb2fb91898a84b
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 532ac5bf0da6a25b66733936493e312f10524c98
+      test "$(git -C ../jerboa rev-parse HEAD)" = 3b6f07f1a2810e03b9d6f489567f8819b54880b8
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 8b3a95435b0fd0966103db550fb4fbe5a0bc5597
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index f6859e3..a927e3e 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "9638f2f8238c8ee30d2be26002eb2fb91898a84b",
-    "tree": "532ac5bf0da6a25b66733936493e312f10524c98"
+    "commit": "3b6f07f1a2810e03b9d6f489567f8819b54880b8",
+    "tree": "8b3a95435b0fd0966103db550fb4fbe5a0bc5597"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 057ac2f..24ee8f4 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=9638f2f8238c8ee30d2be26002eb2fb91898a84b # gitsafe:ignore
-jerboa_tree=532ac5bf0da6a25b66733936493e312f10524c98 # gitsafe:ignore
+jerboa_commit=3b6f07f1a2810e03b9d6f489567f8819b54880b8 # gitsafe:ignore
+jerboa_tree=8b3a95435b0fd0966103db550fb4fbe5a0bc5597 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index fa067a9..a930927 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -2479,6 +2479,53 @@
                     "."
                     (string-append (int->string tenths) " s")))))))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/Guessing.kt"
+      (kotlin-imports (android content Context))
+      (typed-library (com sfb ssdreview)
+        (export applyInitialGuesses)
+        (type Context)
+        (type Int32)
+        (extern (boxTypesResolve (context : Context) (raw : String)) : (Nullable BoxType)
+          (kotlin-call BoxTypes resolve))
+        (def (nullableBoxTypeId (boxType : (Nullable BoxType))) : (Nullable String)
+          (if (nullable-null? boxType)
+              (nullable-none String)
+              (nullable-some (BoxType-id (nullable-get boxType)))))
+        (def (applyInitialGuessIfNeeded (context : Context)
+                                        (session : SsdSession)
+                                        (race : String)
+                                        (group : SsdGroup)) : Unit
+          (if (shouldReplaceGuessLabel (SsdGroup-status group) (SsdGroup-label group))
+              (let ((guess
+                      (guessForGroup
+                        group
+                        (SsdSession-imageWidth session)
+                        (SsdSession-imageHeight session)
+                        race)))
+                (applyInitialGuessToGroup
+                  group
+                  guess
+                  (nullableBoxTypeId
+                    (boxTypesResolve context (Guess-boxType guess)))))
+              (begin)))
+        (def (applyInitialGuesses (context : Context) (session : SsdSession)) : Unit
+          (let ((race
+                  (raceKey
+                    (string-append
+                      (SsdSession-sourceName session)
+                      (string-append " " (SsdSession-sourceUri session))))))
+            (begin
+              (for/fold ((ignored (int32 0)))
+                        ((i (in-range (int32 0) (list-size (SsdSession-groups session)))))
+                (begin
+                  (applyInitialGuessIfNeeded
+                    context
+                    session
+                    race
+                    (list-ref (SsdSession-groups session) i))
+                  ignored))
+              (begin))))))
+
     (typed-kotlin-file "com/sfb/ssdreview/TruthMatching.kt"
       (kotlin-imports (org json JSONObject))
       (typed-library (com sfb ssdreview)
@@ -2712,26 +2759,6 @@
        ""
        "}"
        ))
-    (kotlin-file-lines "com/sfb/ssdreview/Guessing.kt"
-      (
-       "package com.sfb.ssdreview"
-       ""
-       "import android.content.Context"
-       ""
-       "object Guessing {"
-       "    fun applyInitialGuesses(context: Context, session: SsdSession) {"
-       "        val race = raceKey(session.sourceName + \" \" + session.sourceUri)"
-       "        session.groups.forEach { group ->"
-       "            if (!shouldReplaceGuessLabel(group.status, group.label)) return@forEach"
-       "            val guess = guessForGroup(group, session.imageWidth, session.imageHeight, race)"
-       "            val boxType = BoxTypes.resolve(context, guess.boxType)"
-       "            applyInitialGuessToGroup(group, guess, boxType?.id)"
-       "        }"
-       "    }"
-       ""
-       "}"
-       ""
-       ))
     (kotlin-file-lines "com/sfb/ssdreview/MainActivity.kt"
       (
        "package com.sfb.ssdreview"
@@ -3448,7 +3475,7 @@
        "                        truthStore.applyTruth(session)"
        "                        ssdSessionPruneGroupsOutsideSsdArea(session)"
        "                    }"
-       "                    Guessing.applyInitialGuesses(this, session)"
+       "                    applyInitialGuesses(this, session)"
        "                    session.ocrWords.addAll(SsdOcr.recognizeWords(bitmap))"
        "                    ocrGuesses = SsdOcr.applyOcrGuesses(this, session)"
        "                }"