Move SSD OCR center geometry to typed Kotlin

ober

e64fc90fa1833e3489797e6534824c5da86eb15d

diff --git a/.build.yml b/.build.yml
index 4b7b78d..507b963 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#d1e30a246a368a56300655395010e695a6225c8c"
+  - "https://git.sr.ht/~lisp/jerboa#0bcadeaded7a3454bab6088c08aa208cf87a6213"
   # 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)" = d1e30a246a368a56300655395010e695a6225c8c
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = f66ae467c3960424c5579e4584bc5aab89b81504
+      test "$(git -C ../jerboa rev-parse HEAD)" = 0bcadeaded7a3454bab6088c08aa208cf87a6213
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = c48e016b2e63ab1b76452e04b9525ac6604bc87b
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 17bd386..16fa96d 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": "d1e30a246a368a56300655395010e695a6225c8c",
-    "tree": "f66ae467c3960424c5579e4584bc5aab89b81504"
+    "commit": "0bcadeaded7a3454bab6088c08aa208cf87a6213",
+    "tree": "c48e016b2e63ab1b76452e04b9525ac6604bc87b"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/full-kotlin.md b/full-kotlin.md
index 280b9fe..4b952a2 100644
--- a/full-kotlin.md
+++ b/full-kotlin.md
@@ -43,7 +43,7 @@ The vendored Jerboa tree already has the right starting point:
 
 Do not fork that model inside `jandroid.ss`. General Kotlin backend work belongs in Jerboa. Android project and Android DSL work belongs in `jerboa-android`.
 
-Version caveat: this repository now pins Jerboa commit `d1e30a246a368a56300655395010e695a6225c8c`, tree `f66ae467c3960424c5579e4584bc5aab89b81504`. Before editing compiler APIs, check out the exact pinned commit (or the intended replacement commit), rerun the typed parser/checker/core inventory, and record any differences. Do not assume two Jerboa snapshots expose identical IR.
+Version caveat: this repository now pins Jerboa commit `0bcadeaded7a3454bab6088c08aa208cf87a6213`, tree `c48e016b2e63ab1b76452e04b9525ac6604bc87b`. Before editing compiler APIs, check out the exact pinned commit (or the intended replacement commit), rerun the typed parser/checker/core inventory, and record any differences. Do not assume two Jerboa snapshots expose identical IR.
 
 ### 2.3 `~/sfb` source requirements
 
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 8ee9c51..6b250a8 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=d1e30a246a368a56300655395010e695a6225c8c # gitsafe:ignore
-jerboa_tree=f66ae467c3960424c5579e4584bc5aab89b81504 # gitsafe:ignore
+jerboa_commit=0bcadeaded7a3454bab6088c08aa208cf87a6213 # gitsafe:ignore
+jerboa_tree=c48e016b2e63ab1b76452e04b9525ac6604bc87b # 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 9bc1a1a..43bc003 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -416,6 +416,27 @@
                   (and (equal? status "candidate")
                        (string-starts-with? notes "Guess:")))))))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/OcrGeometry.kt"
+      (typed-library (com sfb ssdreview)
+        (export centerInside)
+        (type Float32)
+        (type FloatArray)
+        (type Int32)
+        (def (centerInside (bbox : FloatArray) (rect : FloatArray)) : Bool
+          (if (or (< (float-array-length bbox) (int32 4))
+                  (< (float-array-length rect) (int32 4)))
+            #f
+            (let ((cx (/ (+ (float-array-ref bbox (int32 0))
+                            (float-array-ref bbox (int32 2)))
+                         (float32 2.0)))
+                  (cy (/ (+ (float-array-ref bbox (int32 1))
+                            (float-array-ref bbox (int32 3)))
+                         (float32 2.0))))
+              (and (and (>= cx (float-array-ref rect (int32 0)))
+                        (<= cx (float-array-ref rect (int32 2))))
+                   (and (>= cy (float-array-ref rect (int32 1)))
+                        (<= cy (float-array-ref rect (int32 3))))))))))
+
     (kotlin-file-lines "com/sfb/ssdreview/BoxTypes.kt"
       (
        "package com.sfb.ssdreview"
@@ -3194,13 +3215,6 @@
        "            .take(240)"
        "    }"
        ""
-       "    private fun centerInside(bbox: FloatArray, rect: FloatArray): Boolean {"
-       "        if (bbox.size < 4 || rect.size < 4) return false"
-       "        val cx = (bbox[0] + bbox[2]) / 2f"
-       "        val cy = (bbox[1] + bbox[3]) / 2f"
-       "        return cx >= rect[0] && cx <= rect[2] && cy >= rect[1] && cy <= rect[3]"
-       "    }"
-       ""
        "    private fun ocrCandidates(context: Context, text: String, race: String): List<OcrCandidate> {"
        "        if (text.isBlank()) return emptyList()"
        "        val seen = mutableSetOf<String>()"