Move SSD session ID generation to typed Kotlin

ober

7d00deaa71ecfdbcc950dcc9052770a967ea8333

diff --git a/.build.yml b/.build.yml
index 8db2d89..236875c 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#1388ed603520d35b9228fbdc525294ae496e3635"
+  - "https://git.sr.ht/~lisp/jerboa#d5742f47587e045650cbfaebd2e11484e45772dc"
   # 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)" = 1388ed603520d35b9228fbdc525294ae496e3635
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 14389e832902111f5a8434d25f3ed6744f1f0112
+      test "$(git -C ../jerboa rev-parse HEAD)" = d5742f47587e045650cbfaebd2e11484e45772dc
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 3f7ddb8a9e1e72c81768f8a8b090ffd8dc0fe015
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 6631f5e..64b92dc 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": "1388ed603520d35b9228fbdc525294ae496e3635",
-    "tree": "14389e832902111f5a8434d25f3ed6744f1f0112"
+    "commit": "d5742f47587e045650cbfaebd2e11484e45772dc",
+    "tree": "3f7ddb8a9e1e72c81768f8a8b090ffd8dc0fe015"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 9eb4bd8..620c8bc 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=1388ed603520d35b9228fbdc525294ae496e3635 # gitsafe:ignore
-jerboa_tree=14389e832902111f5a8434d25f3ed6744f1f0112 # gitsafe:ignore
+jerboa_commit=d5742f47587e045650cbfaebd2e11484e45772dc # gitsafe:ignore
+jerboa_tree=3f7ddb8a9e1e72c81768f8a8b090ffd8dc0fe015 # 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 6b0d0d4..40d1576 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -331,7 +331,8 @@
                 SsdSession-suppressedGroups
                 SsdSession-ssdArea SsdSession-ssdArea-set!
                 ssdSessionCellInsideSsdArea ssdSessionGroupInsideSsdArea
-                ssdSessionFindCellById ssdSessionRecomputeGroup)
+                ssdSessionFindCellById ssdSessionRecomputeGroup
+                ssdSessionNextCellId ssdSessionNextGroupId)
         (type Int32)
         (type FloatArray)
         (type SsdCell)
@@ -415,7 +416,49 @@
               (SsdGroup-count-set! group (list-size groupCells))
               (if (= (list-size groupCells) (int32 0))
                 (SsdGroup-count-set! group (SsdGroup-count group))
-                (SsdGroup-bbox-set! group (bboxForMutableCells groupCells))))))))
+                (SsdGroup-bbox-set! group (bboxForMutableCells groupCells))))))
+        (def (maxCellIdNumber (cells : (MutableList SsdCell))) : Int32
+          (for/fold ((maxId (int32 0)))
+                    ((i (in-range (int32 0) (list-size cells))))
+            (let ((parsed
+                    (string->int32-or-null
+                      (string-remove-prefix
+                        (SsdCell-id (list-ref cells i))
+                        "c"))))
+              (if (nullable-null? parsed)
+                maxId
+                (let ((value (nullable-get parsed)))
+                  (if (> value maxId) value maxId))))))
+        (def (maxGroupIdNumber (groups : (MutableList SsdGroup))) : Int32
+          (for/fold ((maxId (int32 0)))
+                    ((i (in-range (int32 0) (list-size groups))))
+            (let ((parsed
+                    (string->int32-or-null
+                      (string-remove-prefix
+                        (SsdGroup-id (list-ref groups i))
+                        "g"))))
+              (if (nullable-null? parsed)
+                maxId
+                (let ((value (nullable-get parsed)))
+                  (if (> value maxId) value maxId))))))
+        (def (ssdSessionNextCellId (session : SsdSession)) : String
+          (string-append
+            "c"
+            (string-pad-start
+              (int32->string (+ (maxCellIdNumber (SsdSession-cells session)) (int32 1)))
+              (int32 4)
+              #\0)))
+        (def (ssdSessionNextGroupId (session : SsdSession)) : String
+          (let ((activeMax (maxGroupIdNumber (SsdSession-groups session)))
+                (suppressedMax (maxGroupIdNumber (SsdSession-suppressedGroups session))))
+            (string-append
+              "g"
+              (string-pad-start
+                (int32->string
+                  (+ (if (> activeMax suppressedMax) activeMax suppressedMax)
+                     (int32 1)))
+                (int32 4)
+                #\0))))))
 
     (typed-kotlin-file "com/sfb/ssdreview/SourceKey.kt"
       (typed-library (com sfb ssdreview)
@@ -3452,18 +3495,6 @@
        "    return outside.size"
        "}"
        ""
-       "fun ssdSessionNextCellId(session: SsdSession): String {"
-       "    val maxId = session.cells.mapNotNull { it.id.removePrefix(\"c\").toIntOrNull() }.maxOrNull() ?: 0"
-       "    return \"c%04d\".format(maxId + 1)"
-       "}"
-       ""
-       "fun ssdSessionNextGroupId(session: SsdSession): String {"
-       "    val maxId = (session.groups + session.suppressedGroups)"
-       "        .mapNotNull { it.id.removePrefix(\"g\").toIntOrNull() }"
-       "        .maxOrNull() ?: 0"
-       "    return \"g%04d\".format(maxId + 1)"
-       "}"
-       ""
        "fun ssdSessionToTruthJson(session: SsdSession): JSONObject {"
        "    val cellsById = ssdSessionCellsById(session)"
        "    val groupsJson = JSONArray()"