Move SSD geometry helpers to typed Kotlin

ober

e88768b564fae4a535de8c3dbba1d26bbbad464c

diff --git a/.build.yml b/.build.yml
index b54b3f8..d0f3e11 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#b4469164cf8a3707a4bae1272ccd645828b08695"
+  - "https://git.sr.ht/~lisp/jerboa#5f11ec65196844516307d5196364331364295a9c"
   # 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)" = b4469164cf8a3707a4bae1272ccd645828b08695
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 9b0d7a8d2f2a70394b86a1ce6de43901094d10f7
+      test "$(git -C ../jerboa rev-parse HEAD)" = 5f11ec65196844516307d5196364331364295a9c
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = b07a69be351dbe1d44f0fd4b74c0af522f071f17
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 373b0d4..1b0c270 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": "b4469164cf8a3707a4bae1272ccd645828b08695",
-    "tree": "9b0d7a8d2f2a70394b86a1ce6de43901094d10f7"
+    "commit": "5f11ec65196844516307d5196364331364295a9c",
+    "tree": "b07a69be351dbe1d44f0fd4b74c0af522f071f17"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/full-kotlin.md b/full-kotlin.md
index a6c6e05..3656167 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 `b4469164cf8a3707a4bae1272ccd645828b08695`, tree `9b0d7a8d2f2a70394b86a1ce6de43901094d10f7`. 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 `5f11ec65196844516307d5196364331364295a9c`, tree `b07a69be351dbe1d44f0fd4b74c0af522f071f17`. 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 59e1e91..f6ac140 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=b4469164cf8a3707a4bae1272ccd645828b08695 # gitsafe:ignore
-jerboa_tree=9b0d7a8d2f2a70394b86a1ce6de43901094d10f7 # gitsafe:ignore
+jerboa_commit=5f11ec65196844516307d5196364331364295a9c # gitsafe:ignore
+jerboa_tree=b07a69be351dbe1d44f0fd4b74c0af522f071f17 # 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 3cb8204..5c0ae12 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -30,15 +30,70 @@
                 SsdCell-y SsdCell-y-set!
                 SsdCell-w SsdCell-w-set!
                 SsdCell-h SsdCell-h-set!
-                SsdCell-detector)
+                SsdCell-detector
+                ssdCellCx ssdCellCy ssdCellRect
+                rectOverlapArea rectCenterInside)
         (type Float32)
+        (type FloatArray)
+        (type Int32)
         (record SsdCell
           ((id : String)
            (mut x : Float32)
            (mut y : Float32)
            (mut w : Float32)
            (mut h : Float32)
-           (detector : String)))))
+           (detector : String)))
+        (def (ssdCellCx (cell : SsdCell)) : Float32
+          (+ (SsdCell-x cell)
+             (/ (SsdCell-w cell) (float32 2.0))))
+        (def (ssdCellCy (cell : SsdCell)) : Float32
+          (+ (SsdCell-y cell)
+             (/ (SsdCell-h cell) (float32 2.0))))
+        (def (ssdCellRect (cell : SsdCell)) : FloatArray
+          (float-array
+            (SsdCell-x cell)
+            (SsdCell-y cell)
+            (+ (SsdCell-x cell) (SsdCell-w cell))
+            (+ (SsdCell-y cell) (SsdCell-h cell))))
+        (def (rectOverlapArea (a : FloatArray) (b : FloatArray)) : Float32
+          (let ((x (if (< (float-array-ref a (int32 2))
+                          (float-array-ref b (int32 2)))
+                     (- (float-array-ref a (int32 2))
+                        (if (> (float-array-ref a (int32 0))
+                               (float-array-ref b (int32 0)))
+                          (float-array-ref a (int32 0))
+                          (float-array-ref b (int32 0))))
+                     (- (float-array-ref b (int32 2))
+                        (if (> (float-array-ref a (int32 0))
+                               (float-array-ref b (int32 0)))
+                          (float-array-ref a (int32 0))
+                          (float-array-ref b (int32 0))))))
+                (y (if (< (float-array-ref a (int32 3))
+                          (float-array-ref b (int32 3)))
+                     (- (float-array-ref a (int32 3))
+                        (if (> (float-array-ref a (int32 1))
+                               (float-array-ref b (int32 1)))
+                          (float-array-ref a (int32 1))
+                          (float-array-ref b (int32 1))))
+                     (- (float-array-ref b (int32 3))
+                        (if (> (float-array-ref a (int32 1))
+                               (float-array-ref b (int32 1)))
+                          (float-array-ref a (int32 1))
+                          (float-array-ref b (int32 1)))))))
+            (if (or (<= x (float32 0.0)) (<= y (float32 0.0)))
+              (float32 0.0)
+              (* x y))))
+        (def (rectCenterInside (rect : FloatArray) (area : FloatArray)) : Bool
+          (let ((cx (/ (+ (float-array-ref rect (int32 0))
+                          (float-array-ref rect (int32 2)))
+                       (float32 2.0)))
+                (cy (/ (+ (float-array-ref rect (int32 1))
+                          (float-array-ref rect (int32 3)))
+                       (float32 2.0))))
+            (and (>= cx (float-array-ref area (int32 0)))
+                 (<= cx (float-array-ref area (int32 2)))
+                 (>= cy (float-array-ref area (int32 1)))
+                 (<= cy (float-array-ref area (int32 3))))))))
 
     (typed-kotlin-file "com/sfb/ssdreview/Component.kt"
       (typed-library (com sfb ssdreview)
@@ -2196,12 +2251,6 @@
        "import kotlin.math.max"
        "import kotlin.math.min"
        ""
-       "fun ssdCellCx(cell: SsdCell): Float = cell.x + cell.w / 2f"
-       ""
-       "fun ssdCellCy(cell: SsdCell): Float = cell.y + cell.h / 2f"
-       ""
-       "fun ssdCellRect(cell: SsdCell): FloatArray = floatArrayOf(cell.x, cell.y, cell.x + cell.w, cell.y + cell.h)"
-       ""
        "fun ssdCellToJson(cell: SsdCell): JSONObject = JSONObject()"
        "    .put(\"id\", cell.id)"
        "    .put(\"x\", cell.x.toDouble())"
@@ -2375,12 +2424,6 @@
        "    )"
        "}"
        ""
-	       "fun rectOverlapArea(a: FloatArray, b: FloatArray): Float {"
-	       "    val x = max(0f, min(a[2], b[2]) - max(a[0], b[0]))"
-	       "    val y = max(0f, min(a[3], b[3]) - max(a[1], b[1]))"
-	       "    return x * y"
-	       "}"
-	       ""
 	       "fun floatArrayToJson(rect: FloatArray): JSONArray = JSONArray().apply {"
 	       "    rect.forEach { put(it.toDouble()) }"
 	       "}"
@@ -2395,11 +2438,6 @@
 	       "    )"
 	       "}"
 	       ""
-	       "fun rectCenterInside(rect: FloatArray, area: FloatArray): Boolean {"
-	       "    val cx = (rect[0] + rect[2]) / 2f"
-	       "    val cy = (rect[1] + rect[3]) / 2f"
-	       "    return cx >= area[0] && cx <= area[2] && cy >= area[1] && cy <= area[3]"
-	       "}"
 	       ))
     (kotlin-file-lines "com/sfb/ssdreview/SsdDetector.kt"
       (
@@ -3658,7 +3696,7 @@
        "        private const val RATIO_FLOOR_BYTES = 64L * 1024L"
        "        private const val MAX_STORAGE_BYTES = 128L * 1024L * 1024L"
        "        private const val MAX_STORAGE_FILES = 16384"
-       "        private const val MAX_LEARNED_EXAMPLES = 50000"
+       "        private const val MAX_LEARNED_EXAMPLES = 10000"
        "        private const val MAX_JSONL_LINES = 51200"
        "        private const val MAX_SOURCE_KEY_CHARS = 96"
        "        private const val MAX_AUTH_TOKEN_CHARS = 4096"