diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46f15666..ac752cbb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -161,6 +161,30 @@ jobs: cd ${OLC_PATH} && ruby test/plus_codes_test.rb rubocop --config rubocop.yml + # Kotoba implementation. Lives in kotoba/. Compiles .kotoba to wasm with + # kotoba CLI v0.7.2 and runs vendored Plus Code fixtures. + test-kotoba: + runs-on: ubuntu-latest + env: + OLC_PATH: kotoba + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Install kotoba CLI v0.7.2 + run: | + curl -fsSL -o /tmp/kotoba-linux-amd64.tar.gz \ + https://github.com/kotoba-lang/kotoba/releases/download/v0.7.2/kotoba-linux-amd64.tar.gz + echo "95e225461e1b8a21849b251e8c8b654693d2c8a516b258532771651e978e1977 /tmp/kotoba-linux-amd64.tar.gz" | sha256sum -c + mkdir -p /tmp/kotoba-cli + tar -xzf /tmp/kotoba-linux-amd64.tar.gz -C /tmp/kotoba-cli + sudo install -m 755 /tmp/kotoba-cli/kotoba /usr/local/bin/kotoba + - name: test + run: | + cd ${OLC_PATH} + bash checks.sh + # Rust implementation. Lives in rust/. test-rust: runs-on: ubuntu-latest diff --git a/kotoba/README.md b/kotoba/README.md new file mode 100644 index 00000000..a9631722 --- /dev/null +++ b/kotoba/README.md @@ -0,0 +1,51 @@ +# Kotoba library for Open Location Code + +This is a Plus Code (Open Location Code) encode/decode binding for +[Kotoba](https://github.com/kotoba-lang/kotoba) CLI v0.7.2. + +Kotoba cannot FFI. The implementation is self-contained in +`openlocationcode.kotoba` and compiles to host-independent wasm32 (`i64-v1`). + +## Integers only (microdegrees) + +The v0.7.2 wasm32 backend has no IEEE floats. Coordinates are **microdegrees** +(`i64`): 1 unit = 1e-6 degree. A Plus Code is two little-endian i64 words of +ASCII (word 0 = chars 0–7, word 1 = chars 8–15; unused bytes are 0). + +This matches the integer path in `go/encode.go` / `go/decode.go`, not the +float `Encode`/`Decode` wrappers in other language directories. + +## Public API + +| Function | Arguments | Result | +| --- | --- | --- | +| `encode-word` | `lat-udeg`, `lng-udeg`, `code-len`, `word-ix` | packed ASCII word (`0` or `1`) | +| `encode-char` | `lat-udeg`, `lng-udeg`, `code-len`, `pos` | ASCII code unit | +| `decode-lat-lo` / `decode-lng-lo` | `w0`, `w1` | south-west corner, microdegrees | +| `decode-lat-hi` / `decode-lng-hi` | `w0`, `w1` | north-east corner, microdegrees | +| `decode-len` | `w0`, `w1` | significant digit count | +| `main` | (none) | `0` if vendored fixtures pass | + +v1 covers full-code encode/decode only (no shorten/recover). + +## Testing + +From this directory, with kotoba CLI v0.7.2 and Node.js on `PATH`: + +``` +bash checks.sh +``` + +`checks.sh` compiles `openlocationcode.kotoba` to wasm, checks the module has +wasm magic and no imports, instantiates it twice (wasm fuel is per-instance), +requires `main() === 0` for encode fixtures, then calls the decode exports +against the vendored vectors. + +Fixtures in `testdata/` are a subset of `../test_data/encoding.csv` and +`../test_data/decoding.csv`, converted to microdegrees. + +## Compile + +``` +kotoba compile openlocationcode.kotoba --target wasm --output openlocationcode.wasm --json +``` diff --git a/kotoba/checks.sh b/kotoba/checks.sh new file mode 100755 index 00000000..b07208dd --- /dev/null +++ b/kotoba/checks.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Compile the Kotoba Open Location Code binding and run vendored fixtures. +# Must run from within the kotoba directory. +if [ "$(basename "$PWD")" != "kotoba" ]; then + echo "$0: must be run from within the kotoba directory!" + exit 1 +fi + +set -euo pipefail + +KOTOBA_BIN="${KOTOBA:-kotoba}" +OUT="${TMPDIR:-/tmp}/openlocationcode.wasm" + +"$KOTOBA_BIN" compile openlocationcode.kotoba --target wasm --output "$OUT" --json +node run_wasm.mjs "$OUT" diff --git a/kotoba/openlocationcode.kotoba b/kotoba/openlocationcode.kotoba new file mode 100644 index 00000000..daa9540d --- /dev/null +++ b/kotoba/openlocationcode.kotoba @@ -0,0 +1,354 @@ +;; Copyright 2026 Open Location Code authors. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; +;; Open Location Code (Plus Code) for Kotoba. +;; +;; kotoba CLI v0.7.2 wasm32 uses an i64-only value profile (no IEEE floats). +;; Coordinates are microdegrees (i64): 1 unit = 1e-6 degree. Plus Codes are +;; two little-endian i64 words of ASCII (word 0 = chars 0-7, word 1 = chars +;; 8-15; unused bytes are 0). Kotoba cannot FFI, so encode/decode stay in +;; this file. +;; +;; Public API: +;; (encode-word lat-udeg lng-udeg code-len word-ix) -> i64 +;; (encode-char lat-udeg lng-udeg code-len pos) -> i64 ASCII +;; (decode-lat-lo w0 w1) (decode-lng-lo w0 w1) +;; (decode-lat-hi w0 w1) (decode-lng-hi w0 w1) +;; (decode-len w0 w1) +;; +;; `main` runs vendored encode fixtures and returns 0 on success. +;; Decode fixtures are driven from run_wasm.mjs on a fresh instance +;; (wasm fuel is per-instance; encode+decode together overflow it). + +(ns openlocationcode + (:export [main encode-word encode-char decode-lat-lo decode-lng-lo decode-lat-hi decode-lng-hi decode-len])) + +(defn alph [idx :i64] :i64 + (if (< idx 8) (+ 50 idx) + (if (= idx 8) 67 + (if (= idx 9) 70 + (if (= idx 10) 71 + (if (= idx 11) 72 + (if (= idx 12) 74 + (if (= idx 13) 77 + (if (= idx 14) 80 + (if (= idx 15) 81 + (if (= idx 16) 82 + (if (= idx 17) 86 + (if (= idx 18) 87 88))))))))))))) + +(defn p256 [p :i64] :i64 + (if (< p 1) 1 + (if (< p 2) 256 + (if (< p 3) 65536 + (if (< p 4) 16777216 + (if (< p 5) 4294967296 + (if (< p 6) 1099511627776 + (if (< p 7) 281474976710656 + 72057594037927936)))))))) + +(defn encode-word [lat-udeg :i64 lng-udeg :i64 code-len :i64 word-ix :i64] :i64 + (loop [i 16 + lat lat-udeg + lng lng-udeg + acc word-ix + packed code-len] + (if (= i 16) + (let [n (if (<= packed 0) 10 + (if (and (< packed 10) (= (- packed (* (quot packed 2) 2)) 1)) + (+ packed 1) + (if (> packed 15) 15 packed))) + wix acc] + (let [latu (if (> lat 90000000) 90000000 (if (< lat -90000000) -90000000 lat)) + latv (+ (* latu 25) 2250000000) + lat2 (if (< latv 0) 0 (if (>= latv 4500000000) 4499999999 latv)) + num (* lng 8192)] + (let [lq (quot num 1000) + lr (- num (* lq 1000)) + lsigned (if (and (< lng 0) (not= lr 0)) (- lq 1) lq) + raw (+ lsigned 1474560000)] + (let [period 2949120000 + lng2 (if (<= raw 0) + (+ (- raw (* (quot raw period) period)) period) + (if (>= raw period) + (- raw (* (quot raw period) period)) + raw)) + lat3 (if (> n 10) lat2 (quot lat2 3125)) + lng3 (if (> n 10) lng2 (quot lng2 1024)) + acc2 (if (= wix 1) 43 0)] + (recur (if (> n 10) 15 10) lat3 lng3 acc2 (+ (* n 2) wix)))))) + (if (< i 0) + acc + (if (> i 10) + (let [n (quot packed 2) + wix (- packed (* n 2)) + last (if (>= n 8) n 8) + ld0 (- lat (* (quot lat 5) 5))] + (let [ld (if (< ld0 0) (+ ld0 5) ld0) + gd0 (- lng (* (quot lng 4) 4)) + gd (if (< gd0 0) (+ gd0 4) gd0) + keep (if (and (= wix 1) (<= i last)) 1 0)] + (let [acc2 (if (= keep 0) acc (+ acc (* (alph (+ (* ld 4) gd)) (p256 (- i 8)))))] + (recur (- i 1) (quot lat 5) (quot lng 4) acc2 packed)))) + (if (= i 8) + (recur 7 lat lng acc packed) + (let [n (quot packed 2) + wix (- packed (* n 2)) + last (if (>= n 8) n 8) + odd (if (= (- i (* (quot i 2) 2)) 1) 1 0)] + (let [use-lng (if (= i 10) 1 (if (and (= odd 1) (< i 8)) 1 0)) + coord (if (= use-lng 1) lng lat) + r0 (- coord (* (quot coord 20) 20)) + idx (if (< r0 0) (+ r0 20) r0)] + (let [pad (if (and (< n 8) (>= i n) (<= i 7)) 1 0) + wpos (if (< i 8) i (- i 8)) + inw (if (< i 8) (if (= wix 0) 1 0) (if (= wix 1) 1 0)) + keep (if (and (= inw 1) (<= i last)) 1 0)] + (let [ch (if (= keep 0) 0 (if (= pad 1) 48 (alph idx))) + acc2 (if (= keep 0) acc (+ acc (* ch (p256 wpos)))) + lat2 (if (= use-lng 1) lat (quot lat 20)) + lng2 (if (= use-lng 1) (quot lng 20) lng)] + (recur (- i 1) lat2 lng2 acc2 packed))))))))))) + +(defn word-byte [word :i64 p :i64] :i64 + (let [mul (p256 p) + q (quot word mul) + r (- q (* (quot q 256) 256))] + (if (< r 0) (+ r 256) r))) + +(defn encode-char [lat-udeg :i64 lng-udeg :i64 code-len :i64 pos :i64] :i64 + (let [w (if (< pos 8) 0 1) + word (encode-word lat-udeg lng-udeg code-len w) + p (if (< pos 8) pos (- pos 8))] + (word-byte word p))) + +(defn stripped-len [w0 :i64 w1 :i64] :i64 + (loop [p 0 n 0] + (if (>= p 16) + n + (let [word (if (< p 8) w0 w1) + pp (if (< p 8) p (- p 8)) + mul (if (< pp 1) 1 + (if (< pp 2) 256 + (if (< pp 3) 65536 + (if (< pp 4) 16777216 + (if (< pp 5) 4294967296 + (if (< pp 6) 1099511627776 + (if (< pp 7) 281474976710656 + 72057594037927936)))))))] + (let [q (quot word mul) + r (- q (* (quot q 256) 256)) + c (if (< r 0) (+ r 256) r)] + (if (or (= c 0) (= c 43) (= c 48)) + (recur (+ p 1) n) + (recur (+ p 1) (+ n 1)))))))) + +(defn digit-ix [w0 :i64 w1 :i64 k :i64 n :i64] :i64 + (let [raw (if (< n 8) k (if (< k 8) k (+ k 1))) + word (if (< raw 8) w0 w1) + pp (if (< raw 8) raw (- raw 8))] + (let [mul (if (< pp 1) 1 + (if (< pp 2) 256 + (if (< pp 3) 65536 + (if (< pp 4) 16777216 + (if (< pp 5) 4294967296 + (if (< pp 6) 1099511627776 + (if (< pp 7) 281474976710656 + 72057594037927936)))))))] + (let [q (quot word mul) + r (- q (* (quot q 256) 256)) + c (if (< r 0) (+ r 256) r)] + (if (< c 50) -1 + (if (< c 58) (- c 50) + (if (= c 67) 8 + (if (= c 70) 9 + (if (= c 71) 10 + (if (= c 72) 11 + (if (= c 74) 12 + (if (= c 77) 13 + (if (= c 80) 14 + (if (= c 81) 15 + (if (= c 82) 16 + (if (= c 86) 17 + (if (= c 87) 18 + (if (= c 88) 19 -1)))))))))))))))))) + +(defn floor-div [num :i64 den :i64] :i64 + (let [q (quot num den) + r (- num (* q den))] + (if (and (< num 0) (not= r 0)) (- q 1) q))) + +(defn decode-lat-val [w0 :i64 w1 :i64 n :i64] :i64 + (loop [step 0 val 0] + (if (>= step 10) + val + (let [pair (if (< step 5) 1 0) + i (if (< step 5) (* step 2) (+ step 5)) + base (if (= pair 1) 20 5)] + (if (< i n) + (let [ix (digit-ix w0 w1 i n) + add (if (= pair 1) ix (quot ix 4))] + (recur (+ step 1) (+ (* val base) add))) + (recur (+ step 1) (* val base))))))) + +(defn decode-lng-val [w0 :i64 w1 :i64 n :i64] :i64 + (loop [step 0 val 0] + (if (>= step 10) + val + (let [pair (if (< step 5) 1 0) + i (if (< step 5) (* step 2) (+ step 5)) + base (if (= pair 1) 20 4)] + (if (< i n) + (let [k (if (= pair 1) (+ i 1) i) + ix (digit-ix w0 w1 k n) + add (if (= pair 1) ix (- ix (* (quot ix 4) 4)))] + (recur (+ step 1) (+ (* val base) add))) + (recur (+ step 1) (* val base))))))) + +(defn unused-height [n :i64 is-lat :i64] :i64 + (let [up (if (>= n 10) 0 (- 5 (quot (+ n 1) 2))) + ug (if (> n 10) (- 15 n) 5) + gb (if (= is-lat 1) 5 4)] + (loop [e (+ up ug) acc 1] + (if (<= e 0) + acc + (let [b (if (> e ug) 20 gb)] + (recur (- e 1) (* acc b))))))) + +(defn decode-lat-lo [w0 :i64 w1 :i64] :i64 + (floor-div (- (decode-lat-val w0 w1 (stripped-len w0 w1)) 2250000000) 25)) + +(defn decode-lng-lo [w0 :i64 w1 :i64] :i64 + (floor-div (* (- (decode-lng-val w0 w1 (stripped-len w0 w1)) 1474560000) 125) 1024)) + +(defn decode-lat-hi [w0 :i64 w1 :i64] :i64 + (let [n (stripped-len w0 w1)] + (+ (floor-div (- (decode-lat-val w0 w1 n) 2250000000) 25) + (floor-div (unused-height n 1) 25)))) + +(defn decode-lng-hi [w0 :i64 w1 :i64] :i64 + (let [n (stripped-len w0 w1)] + (+ (floor-div (* (- (decode-lng-val w0 w1 n) 1474560000) 125) 1024) + (floor-div (* (unused-height n 0) 125) 1024)))) + +(defn decode-len [w0 :i64 w1 :i64] :i64 + (stripped-len w0 w1)) + +(defn fixture-encode [i :i64] :i64 + (case i + 0 (if (and (= (encode-word 20375000 2775000 6 0) 3472364618834724407) + (= (encode-word 20375000 2775000 6 1) 43)) 1 0) + 1 (if (and (= (encode-word 500000 -179500000 4 0) 3472328296262742582) + (= (encode-word 500000 -179500000 4 1) 43)) 1 0) + 2 (if (and (= (encode-word -89500000 -179500000 4 0) 3472328296261366322) + (= (encode-word -89500000 -179500000 4 1) 43)) 1 0) + 3 (if (and (= (encode-word 20500000 2500000 4 0) 3472328296296302135) + (= (encode-word 20500000 2500000 4 1) 43)) 1 0) + 4 (if (and (= (encode-word 1000000 1000000 11 0) 3617008641922057782) + (= (encode-word 1000000 1000000 11 1) 842150443)) 1 0) + 5 (if (and (= (encode-word 90000000 1000000 4 0) 3472328296280639043) + (= (encode-word 90000000 1000000 4 1) 43)) 1 0) + 6 (if (and (= (encode-word 92000000 1000000 4 0) 3472328296280639043) + (= (encode-word 92000000 1000000 4 1) 43)) 1 0) + 7 (if (and (= (encode-word 90000000 1000000 10 0) 3627704854246868547) + (= (encode-word 90000000 1000000 10 1) 3299371)) 1 0) + 8 (if (and (= (encode-word 1000000 180000000 4 0) 3472328296262808118) + (= (encode-word 1000000 180000000 4 1) 43)) 1 0) + 9 (if (and (= (encode-word 1000000 181000000 4 0) 3472328296279585334) + (= (encode-word 1000000 181000000 4 1) 43)) 1 0) + 10 (if (and (= (encode-word 1200000 3400000 10 0) 3617027350833153590) + (= (encode-word 1200000 3400000 10 1) 3289643)) 1 0) + 11 (if (and (= (encode-word -70000000 163700000 8 0) 3617041627303007795) + (= (encode-word -70000000 163700000 8 1) 43)) 1 0) + 12 (if (and (= (encode-word 40600000 129700000 8 0) 3617041730668810552) + (= (encode-word 40600000 129700000 8 1) 43)) 1 0) + 13 (if (and (= (encode-word -14000000 106900000 6 0) 3472371185907552309) + (= (encode-word -14000000 106900000 6 1) 43)) 1 0) + 14 (if (and (= (encode-word 17600000 -44400000 6 0) 3472356995753392183) + (= (encode-word 17600000 -44400000 6 1) 43)) 1 0) + 15 (if (and (= (encode-word 37539669 -122375070 2 0) 3472328296227681336) + (= (encode-word 37539669 -122375070 2 1) 43)) 1 0) + 0)) + +(defn fixture-decode [i :i64] :i64 + (case i + 0 (if (and (= (decode-lat-lo 3472364618834724407 43) 20350000) + (= (decode-lng-lo 3472364618834724407 43) 2750000) + (= (decode-lat-hi 3472364618834724407 43) 20400000) + (= (decode-lng-hi 3472364618834724407 43) 2800000) + (= (decode-len 3472364618834724407 43) 6)) 1 0) + 1 (if (and (= (decode-lat-lo 3617008642191410744 3289643) 47000000) + (= (decode-lng-lo 3617008642191410744 3289643) 8000000) + (= (decode-lat-hi 3617008642191410744 3289643) 47000125) + (= (decode-lng-hi 3617008642191410744 3289643) 8000125) + (= (decode-len 3617008642191410744 3289643) 10)) 1 0) + 2 (if (and (= (decode-lat-lo 3472328296262742582 43) 0) + (= (decode-lng-lo 3472328296262742582 43) -180000000) + (= (decode-lat-hi 3472328296262742582 43) 1000000) + (= (decode-lng-hi 3472328296262742582 43) -179000000) + (= (decode-len 3472328296262742582 43) 4)) 1 0) + 3 (if (and (= (decode-lat-lo 3472328296261366322 43) -90000000) + (= (decode-lng-lo 3472328296261366322 43) -180000000) + (= (decode-lat-hi 3472328296261366322 43) -89000000) + (= (decode-lng-hi 3472328296261366322 43) -179000000) + (= (decode-len 3472328296261366322 43) 4)) 1 0) + 4 (if (and (= (decode-lat-lo 3472328296296302135 43) 20000000) + (= (decode-lng-lo 3472328296296302135 43) 2000000) + (= (decode-lat-hi 3472328296296302135 43) 21000000) + (= (decode-lng-hi 3472328296296302135 43) 3000000) + (= (decode-len 3472328296296302135 43) 4)) 1 0) + 5 (if (and (= (decode-lat-lo 3472328296900286006 43) 0) + (= (decode-lng-lo 3472328296900286006 43) 179000000) + (= (decode-lat-hi 3472328296900286006 43) 1000000) + (= (decode-lng-hi 3472328296900286006 43) 180000000) + (= (decode-len 3472328296900286006 43) 4)) 1 0) + 6 (if (and (= (decode-lat-lo 3617008641922057782 842150443) 1000000) + (= (decode-lng-lo 3617008641922057782 842150443) 1000000) + (= (decode-lat-hi 3617008641922057782 842150443) 1000025) + (= (decode-lng-hi 3617008641922057782 842150443) 1000031) + (= (decode-len 3617008641922057782 842150443) 11)) 1 0) + 7 (if (and (= (decode-lat-lo 3472328296280639043 43) 89000000) + (= (decode-lng-lo 3472328296280639043 43) 1000000) + (= (decode-lat-hi 3472328296280639043 43) 90000000) + (= (decode-lng-hi 3472328296280639043 43) 2000000) + (= (decode-len 3472328296280639043 43) 4)) 1 0) + 8 (if (and (= (decode-lat-lo 3472328296262808118 43) 1000000) + (= (decode-lng-lo 3472328296262808118 43) -180000000) + (= (decode-lat-hi 3472328296262808118 43) 2000000) + (= (decode-lng-hi 3472328296262808118 43) -179000000) + (= (decode-len 3472328296262808118 43) 4)) 1 0) + 9 (if (and (= (decode-lat-lo 3472328296279585334 43) 1000000) + (= (decode-lng-lo 3472328296279585334 43) -179000000) + (= (decode-lat-hi 3472328296279585334 43) 2000000) + (= (decode-lng-hi 3472328296279585334 43) -178000000) + (= (decode-len 3472328296279585334 43) 4)) 1 0) + 10 (if (and (= (decode-lat-lo 3627704854246868547 3299371) 89999875) + (= (decode-lng-lo 3627704854246868547 3299371) 1000000) + (= (decode-lat-hi 3627704854246868547 3299371) 90000000) + (= (decode-lng-hi 3627704854246868547 3299371) 1000125) + (= (decode-len 3627704854246868547 3299371) 10)) 1 0) + 11 (if (and (= (decode-lat-lo 3472328296227681336 43) 30000000) + (= (decode-lng-lo 3472328296227681336 43) -140000000) + (= (decode-lat-hi 3472328296227681336 43) 50000000) + (= (decode-lng-hi 3472328296227681336 43) -120000000) + (= (decode-len 3472328296227681336 43) 2)) 1 0) + 0)) + +(defn main [] :i64 + (loop [i 0] + (if (>= i 16) + 0 + (if (= (fixture-encode i) 0) + (+ i 1) + (recur (+ i 1)))))) diff --git a/kotoba/run_wasm.mjs b/kotoba/run_wasm.mjs new file mode 100755 index 00000000..22bef461 --- /dev/null +++ b/kotoba/run_wasm.mjs @@ -0,0 +1,71 @@ +#!/usr/bin/env node +// Instantiate a kotoba-compiled wasm module and run vendored fixtures. +// Two instances: wasm fuel is per-instance, and encode+decode together overflow it. +import { readFileSync } from "node:fs"; + +const path = process.argv[2]; +if (!path) { + console.error("usage: node run_wasm.mjs "); + process.exit(2); +} + +const buf = readFileSync(path); +if (buf.length < 4 || buf[0] !== 0x00 || buf[1] !== 0x61 || buf[2] !== 0x73 || buf[3] !== 0x6d) { + console.error("not a wasm module (missing magic)"); + process.exit(1); +} + +const module = new WebAssembly.Module(buf); +const imports = WebAssembly.Module.imports(module); +if (imports.length !== 0) { + console.error("wasm module has imports (not host-independent):", imports); + process.exit(1); +} + +const encodeInst = new WebAssembly.Instance(module); +if (typeof encodeInst.exports.main !== "function") { + console.error("wasm module has no main export"); + process.exit(1); +} + +const encodeCode = encodeInst.exports.main(); +if (encodeCode !== 0n && encodeCode !== 0) { + console.error("encode fixtures failed, main returned", encodeCode); + process.exit(1); +} +const seven = encodeInst.exports["encode-char"](20375000n, 2775000n, 6n, 0n); +if (seven !== 55n && seven !== 55) { + console.error("encode-char fixture failed, got", seven, "expected 55 (7)"); + process.exit(1); +} +console.log("encode fixtures passed (main === 0)"); + +const decode = [ + [3472364618834724407n, 43n, 20350000n, 2750000n, 20400000n, 2800000n, 6n], + [3617008642191410744n, 3289643n, 47000000n, 8000000n, 47000125n, 8000125n, 10n], + [3472328296262742582n, 43n, 0n, -180000000n, 1000000n, -179000000n, 4n], + [3472328296261366322n, 43n, -90000000n, -180000000n, -89000000n, -179000000n, 4n], + [3472328296296302135n, 43n, 20000000n, 2000000n, 21000000n, 3000000n, 4n], + [3472328296900286006n, 43n, 0n, 179000000n, 1000000n, 180000000n, 4n], + [3617008641922057782n, 842150443n, 1000000n, 1000000n, 1000025n, 1000031n, 11n], + [3472328296280639043n, 43n, 89000000n, 1000000n, 90000000n, 2000000n, 4n], + [3472328296262808118n, 43n, 1000000n, -180000000n, 2000000n, -179000000n, 4n], + [3472328296279585334n, 43n, 1000000n, -179000000n, 2000000n, -178000000n, 4n], + [3627704854246868547n, 3299371n, 89999875n, 1000000n, 90000000n, 1000125n, 10n], + [3472328296227681336n, 43n, 30000000n, -140000000n, 50000000n, -120000000n, 2n], +]; + +const decodeInst = new WebAssembly.Instance(module); +const fns = ["decode-lat-lo", "decode-lng-lo", "decode-lat-hi", "decode-lng-hi", "decode-len"]; +for (let i = 0; i < decode.length; i++) { + const [w0, w1, ...exp] = decode[i]; + const got = fns.map((name) => decodeInst.exports[name](w0, w1)); + for (let j = 0; j < exp.length; j++) { + if (got[j] !== exp[j]) { + console.error(`decode fixture ${i} field ${fns[j]}: got ${got[j]}, expected ${exp[j]}`); + process.exit(1); + } + } +} +console.log("decode fixtures passed"); +console.log("kotoba wasm fixtures passed"); diff --git a/kotoba/testdata/decoding.csv b/kotoba/testdata/decoding.csv new file mode 100644 index 00000000..e219a512 --- /dev/null +++ b/kotoba/testdata/decoding.csv @@ -0,0 +1,20 @@ +################################################################################ +# +# Vendored subset of ../../test_data/decoding.csv for the Kotoba binding. +# Corners here are degrees; the .kotoba fixtures use microdegrees (i64). +# +# Fields: code, length, latLo, lngLo, latHi, lngHi +# +################################################################################ +7FG49Q00+,6,20.35,2.75,20.4,2.8 +8FVC2222+22,10,47.0,8.0,47.000125,8.000125 +62G20000+,4,0.0,-180.0,1,-179 +22220000+,4,-90,-180,-89,-179 +7FG40000+,4,20.0,2.0,21.0,3.0 +6VGX0000+,4,0,179,1,180 +6FH32222+222,11,1,1,1.000025,1.00003125 +CFX30000+,4,89,1,90,2 +62H20000+,4,1,-180,2,-179 +62H30000+,4,1,-179,2,-178 +CFX3X2X2+X2,10,89.9998750,1,90,1.0001250 +84000000+,2,30,-140,50,-120 diff --git a/kotoba/testdata/encoding.csv b/kotoba/testdata/encoding.csv new file mode 100644 index 00000000..8a5ce1f4 --- /dev/null +++ b/kotoba/testdata/encoding.csv @@ -0,0 +1,24 @@ +################################################################################ +# +# Vendored subset of ../../test_data/encoding.csv for the Kotoba binding. +# Lat/lng here are degrees; the .kotoba fixtures use microdegrees (i64). +# +# Fields: latitude, longitude, latInt, lngInt, code length, expected code +# +################################################################################ +20.375,2.775,2759375000,1497292800,6,7FG49Q00+ +0.5,-179.5,2262500000,4096000,4,62G20000+ +-89.5,-179.5,12500000,4096000,4,22220000+ +20.5,2.5,2762500000,1495040000,4,7FG40000+ +1,1,2275000000,1482752000,11,6FH32222+222 +90,1,4499999999,1482752000,4,CFX30000+ +92,1,4499999999,1482752000,4,CFX30000+ +90,1,4499999999,1482752000,10,CFX3X2X2+X2 +1,180,2275000000,0,4,62H20000+ +1,181,2275000000,8192000,4,62H30000+ +1.2,3.4,2280000000,1502412800,10,6FH56C22+22 +-70,163.7,500000000,2815590400,8,3V252P22+ +40.6,129.7,3265000000,2537062400,8,8QGFJP22+ +-14,106.9,1900000000,2350284800,6,5PR82W00+ +17.6,-44.4,2690000000,1110835200,6,789QJJ00+ +37.539669125,-122.375069724,3188491728,472063428,2,84000000+