diff options
| author | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2026-10-26 20:05:20 +0100 |
|---|---|---|
| committer | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2026-10-26 20:05:20 +0100 |
| commit | 375d87a9b0ec1cf2fffd705adb870c86144d2400 (patch) | |
| tree | 1e8e05e7cd58686628d0f4622622456613a42e4d | |
| parent | 45425dd047b791fe63f13a46a3251c529cfffc94 (diff) | |
Replace the C++ implementation with the Rust port
The Rust port (formerly gipfel-rs) is now the official successor to the
FLTK/GSL C++ code base. This squashes the port into a single commit and
removes the C++ sources (they remain in the git history below).
Layout:
crates/gipfel-core headless core: hills, panorama geometry, projections
(maxima-generated derivatives), pure-Rust LM fit,
gipfel: JPEG comment + EXIF I/O, profiles, stitching
crates/gipfel-cli command-line tools (export, info, stitch, profiles)
crates/gipfel-app Cosmic DE (libcosmic) GUI, binary name `gipfel`
codegen/ maxima scripts + regen.sh for the projection code
tests/ golden files + C++ reference harnesses (historical)
The original README is kept for now; it will be updated to describe the
Rust build. gipfel.dat, AUTHORS, ChangeLog and NEWS are unchanged.
103 files changed, 27234 insertions, 5258 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7804fde --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +harness/build +*.tmp +gipfel-app-bin.tar.gz diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f8992c7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,134 @@ +# AGENTS.md — gipfel-rs + +Guidance for AI coding agents working in this repository. + +## What this is + +`gipfel-rs` is a Rust port of **gipfel** (a mountain-photo annotator: you load a +photo, mark known peaks, and it fits the camera viewpoint + lens distortion from +the hill database). The original is C++/FLTK (`https://git.flpsed.org/gipfel`, +cloned at `~/gipfel`). This port targets **Cosmic DE** (libcosmic) on Linux. + +The port is organized as milestones (M1 core → M5 polish); see `~/gipfel/plan.md` +for the full plan and `~/gipfel-spikes/MEMO.md` for early UI-spike decisions. + +## Workspace layout + +``` +crates/gipfel-core/ headless computational core (no GUI, no native C deps) + hill.rs hill DB: CSV load (Latin-1), duplicate marking + panorama.rs viewpoint, geodesy, refraction, occlusion, visibility + projection/ rectilinear + cylindrical projections + generated/ maxima-generated symbolic functions (DO NOT hand-edit) + lsq.rs pure-Rust Levenberg-Marquardt fit (was GSL; GSL removed) + metadata.rs `gipfel:` JPEG comment + EXIF read/write + profile.rs distortion profiles (FLTK prefs format, XDG data dir) + stitch.rs cylindrical panorama resampling (ScanImage/Stitch port) + TIFF/JPEG out + track.rs GPX track parser (hardened: no XXE, size cap) +crates/gipfel-cli/ CLI: export-position, export-hills, info, stitch, profiles +crates/gipfel-app/ libcosmic GUI app (binary name: `gipfel`) + main.rs app state + Elm-style update/view + sidebar + viewport.rs custom canvas Widget (image + markers + labels) + compass.rs compass-strip orientation widget + dump.rs off-screen image renderer (screen dump) +codegen/ maxima scripts + regen.sh (regenerates projection/generated/) +tests/ C++ reference harnesses (.cxx) + golden files +``` + +## Build & test + +```sh +cargo build --release # app binary: target/release/gipfel +cargo test --lib --test golden # unit + golden tests (the main gate) +cargo test --workspace # everything incl. app-crate tests +cargo clippy --workspace --all-targets +``` + +- **Rust edition 2021.** No pinned toolchain file. +- **No native C dependencies** since GSL was replaced by a pure-Rust LM. The only + system libs are what libcosmic/wgpu need (Vulkan/GL, fontconfig). +- The dev environment has **no reachable display server** — the GUI app compiles + but cannot be run here. Runtime UI verification is done by the user on a real + Wayland desktop. Do not assume you can launch `gipfel` to test it. + +## Golden tests — the critical invariant + +`crates/gipfel-core/tests/golden.rs` compares output against files in +`tests/golden/` produced by the **original C++ build**. This is the contract that +keeps the math faithful. + +- Fit tests (`fit_rectilinear_matches_c`, `fit_cylindrical_matches_c`): fitted + params within **1e-6 relative**, final projected coords within **1e-9 relative**. +- Function-grid tests: ~1e-15 agreement on the maxima-generated derivatives. +- Several golden tests **skip silently** if the hill DB is missing. They look for + the data file via `$GIPFEL_DATA`, then `$HOME/gipfel/gipfel.dat`. Set + `GIPFEL_DATA=/path/to/gipfel.dat` so they actually run (a skipped golden test + is a green test that verified nothing). +- **Never edit `tests/golden/*` to make a failing test pass.** They are ground + truth from C++. If a golden test fails, your change altered behavior — fix the + code or understand the deviation. + +Regenerating goldens (only when the C++ reference changes): +`tests/gen_golden.sh` — needs the built C++ tree (`$GIPFEL_SRC`, default +`~/gipfel/src`), `$GIPFEL_DATA`, g++, GSL, maxima. The `.cxx` harnesses still use +C++ GSL; that is intentional (they are the reference, not part of the Rust build). + +## Code generation (maxima) + +`crates/gipfel-core/src/projection/generated/*_funcs.rs` are **generated** from +`codegen/lsq_{rectilinear,cylindrical}.mac` via `codegen/regen.sh` (needs maxima; +optional rustfmt). The mathematical models (refraction, cone-visibility, +distortion) and their symbolic derivatives are **frozen** — do not change the +models. If you regenerate, re-run the golden tests immediately. + +## Hard compatibility constraints + +- **`gipfel.dat` CSV format** and the **`gipfel:` JPEG comment parameter string** + must stay byte-compatible with the C++ version (keys `nick`/`tilt` are kept in + the file even though the code renamed them to pitch/roll). +- **Hill indices are stable handles.** `Panorama::hills` keeps insertion order; + `mark_duplicates` sorts an index permutation, never the vec. Track lists and + known-hill sets index into `hills`. Do not reorder `hills` in place. +- **Terminology:** UI says "Pitch"/"Roll"; internal fields are `a_pitch`/`a_roll`; + hill elevation is `a_elev` (distinct from camera pitch). JPEG keys stay + `nick`/`tilt`. +- Distortion profiles live in `$XDG_DATA_HOME/gipfel/distortion-profiles.prefs` + (FLTK prefs format, legacy auto-import). Keep the `%g`-style float formatting + (`profile::fmt_g`) matching C. + +## Code conventions + +- **Elm architecture** in the app: `update(&mut self, Message) -> Task` is one + flat, **exhaustive** `match` (no catch-all) — keep it exhaustive so new + `Message` variants are forced to be handled. Prefer extracting small helper + methods over a catch-all arm. +- Shared helpers to reuse (don't duplicate): `slider_item()`, `pick_open`/ + `pick_save` (file dialogs), `compass::newton_inverse` (scalar inverse + projection), `viewport::layout_label_y` (label layout shared by screen + dump). +- Sidebar rows use `settings::item_row` with fixed-width labels when fields must + align to equal widths (see the Distortion section). +- libcosmic is **pinned to git rev `430f3d4`** (see `crates/gipfel-app/Cargo.toml`) + and vendors iced 0.14.2. The correct nav-bar return type is + `cosmic::Action<Message>` (crate root), not `cosmic::app::Action`. +- Rendering gotchas: the wgpu renderer draws in fixed layer order (use + `start_layer`/`end_layer` to force overlays above the image); keep strong + `Plain<P>` paragraph refs alive across the frame in widget `Tree` state. + +## Platform notes + +- **Linux / Cosmic first.** No Windows/macOS support yet. +- A standalone Windows build is an open experiment. The main blocker (GSL) is + already removed. Remaining work: swap libcosmic's `xdg-portal` feature for + `rfd` (file dialogs), port the hill-DB search path (`/usr/share`, `$HOME` → + `dirs` + exe-relative) and font fallback, then get libcosmic itself to compile + for `x86_64-pc-windows-gnu` (upstream does not test Windows). + +## Do / Don't + +- ✅ Run `cargo test --lib --test golden` (with `GIPFEL_DATA` set) after any + change to the core math, projection, fit, metadata, or stitch code. +- ✅ Keep the build warning-free (rustc `dead_code`/`unused_*` lints are the + signal; clippy may not be installed in every environment). +- ❌ Don't hand-edit `projection/generated/` or `tests/golden/`. +- ❌ Don't change the mathematical models or file formats. +- ❌ Don't assume the GUI can be launched in this environment. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7518c3d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6477 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "accesskit" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.15.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.3", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.18.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "atspi", + "futures-lite", + "serde", + "tokio", + "tokio-stream", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "accesskit_winit" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "almost" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa2999eb46af81abb65c2d30d446778d7e613b60bbf4e174a027e80f90a3c14" + +[[package]] +name = "android-activity" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +dependencies = [ + "android-properties", + "bitflags 2.13.2", + "cc", + "jni", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 2.0.21", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "apply" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47b57fc4521e3cae26a4d45b5227f8fadee4c345be0fefd8d5d1711afb8aeb9" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.5", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.5", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.5", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.5", +] + +[[package]] +name = "async-recursion" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.5", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomicwrites" +version = "0.4.2" +source = "git+https://github.com/jackpot51/rust-atomicwrites#043ab4859d53ffd3d55334685303d8df39c9f768" +dependencies = [ + "rustix 0.38.44", + "tempfile", + "windows-sys 0.48.0", +] + +[[package]] +name = "atspi" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" +dependencies = [ + "atspi-common", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-proxies" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] + +[[package]] +name = "auto_enums" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3091d68264354f211516b91dce6f71046e444fab1867716035f736667243affb" +dependencies = [ + "derive_utils", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.3", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.5", +] + +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "borsh" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +dependencies = [ + "bytes", + "cfg_aliases", +] + +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + +[[package]] +name = "btoi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5ab9db53bcda568284df0fd39f6eac24ad6f7ba7ff1168b9e76eba6576b976" +dependencies = [ + "num-traits", +] + +[[package]] +name = "build_helpers" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "cfg_aliases", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "calloop" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" +dependencies = [ + "bitflags 2.13.2", + "polling", + "rustix 1.1.5", + "tracing", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop", + "rustix 1.1.5", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "clipboard-win" +version = "5.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052af7878466feccda12ba7a6a67d019d712f296809559b47c12c8cc196d3a9a" +dependencies = [ + "error-code", +] + +[[package]] +name = "clipboard_macos" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "objc", + "objc-foundation", + "objc_id", +] + +[[package]] +name = "clipboard_wayland" +version = "0.2.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "dnd", + "mime 0.1.0", + "smithay-clipboard", +] + +[[package]] +name = "clipboard_x11" +version = "0.4.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "thiserror 1.0.69", + "x11rb", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.4", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "configparser" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031ad3d167622974ed636505ae4556267ed4aa2aea0464122fa6bc9698a32825" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cosmic-config" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "atomicwrites", + "cosmic-config-derive", + "cosmic-settings-daemon", + "dirs", + "futures-util", + "iced_futures", + "known-folders", + "notify", + "ron", + "serde", + "tokio", + "tracing", + "xdg", + "zbus", +] + +[[package]] +name = "cosmic-config-derive" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "cosmic-freedesktop-icons" +version = "0.4.0" +source = "git+https://github.com/pop-os/freedesktop-icons#ab4c57b8e416c6af9297cb04d101889896fd9a92" +dependencies = [ + "bstr", + "btoi", + "memchr", + "memmap2", + "thiserror 2.0.21", + "tracing", + "xdg", +] + +[[package]] +name = "cosmic-settings-daemon" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +dependencies = [ + "zbus", +] + +[[package]] +name = "cosmic-text" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" +dependencies = [ + "bitflags 2.13.2", + "fontdb", + "harfrust", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 2.1.3", + "self_cell", + "skrifa 0.40.0", + "smol_str", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cosmic-theme" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "almost", + "configparser", + "cosmic-config", + "csscolorparser", + "dirs", + "hex_color", + "palette", + "ron", + "serde", + "serde_json", + "thiserror 2.0.21", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cryoglyph" +version = "0.1.0" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.3", + "wgpu", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "css-color" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" + +[[package]] +name = "csscolorparser" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168dbdf4dced8f57fa4246ac9080598452b49d70bb2d0e46422142f113140dd8" +dependencies = [ + "num-traits", + "phf", + "serde", + "uncased", +] + +[[package]] +name = "ctor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "dtor", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "defmt" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "defmt-parser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" +dependencies = [ + "thiserror 2.0.21", +] + +[[package]] +name = "derive_setters" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e6f6fa1f03c14ae082120b84b3c7fbd7b8588d924cf2d7c3daf9afd49df8b9" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derive_utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc05a5d33db20c784f873e84934ad94bb209a090987ac5f62fede2c178234f23" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "libc", + "objc2 0.6.5", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "dnd" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "bitflags 2.13.2", + "mime 0.1.0", + "raw-window-handle", + "smithay-client-toolkit", + "smithay-clipboard", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" + +[[package]] +name = "drm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix 0.38.44", +] + +[[package]] +name = "drm-ffi" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" +dependencies = [ + "drm-sys", + "rustix 0.38.44", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + +[[package]] +name = "dtor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide 0.9.1", + "zlib-rs", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + +[[package]] +name = "fluent" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash 2.1.3", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" +dependencies = [ + "memchr", + "thiserror 2.0.21", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-types" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "font-types" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + +[[package]] +name = "fontdue" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e796cb6894e6418daacea84b28dd4f98d3b32c74d5bb452ba1ecfbf5ad8e84ca" +dependencies = [ + "hashbrown 0.15.5", + "ttf-parser", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.5", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gipfel-app" +version = "0.1.0" +dependencies = [ + "cosmic-text", + "fontdb", + "fontdue", + "gipfel-core", + "image", + "libcosmic", + "tokio", +] + +[[package]] +name = "gipfel-cli" +version = "0.1.0" +dependencies = [ + "gipfel-core", +] + +[[package]] +name = "gipfel-core" +version = "0.1.0" +dependencies = [ + "dirs", + "image", + "kamadak-exif", + "quick-xml", + "tiff", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.21", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.13.2", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "grid" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "harfrust" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "read-fonts 0.37.0", + "smallvec", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex_color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" +dependencies = [ + "arrayvec", + "rand 0.8.8", + "serde", +] + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "i18n-config" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror 1.0.69", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "log", + "parking_lot", + "rust-embed", + "sys-locale", + "thiserror 1.0.69", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602e6bd30c3db2749e13e38b363a3d98d9d41de1d8de7a79c31bb69e45b47cda" +dependencies = [ + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "proc-macro-error3", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "iced" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "build_helpers", + "dnd", + "iced_accessibility", + "iced_core", + "iced_debug", + "iced_futures", + "iced_program", + "iced_renderer", + "iced_runtime", + "iced_widget", + "iced_winit", + "image", + "mime 0.1.0", + "thiserror 2.0.21", + "window_clipboard", +] + +[[package]] +name = "iced_accessibility" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "accesskit", + "accesskit_winit", +] + +[[package]] +name = "iced_core" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "bitflags 2.13.2", + "build_helpers", + "bytes", + "dnd", + "glam", + "iced_accessibility", + "lilt", + "log", + "mime 0.1.0", + "num-traits", + "palette", + "raw-window-handle", + "rustc-hash 2.1.3", + "serde", + "smol_str", + "thiserror 2.0.21", + "unicode-segmentation", + "web-time", + "window_clipboard", +] + +[[package]] +name = "iced_debug" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "iced_core", + "iced_futures", + "log", +] + +[[package]] +name = "iced_futures" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "futures", + "iced_core", + "log", + "rustc-hash 2.1.3", + "tokio", + "wasm-bindgen-futures", + "wasmtimer", +] + +[[package]] +name = "iced_graphics" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "cosmic-text", + "half", + "iced_core", + "iced_futures", + "image", + "kamadak-exif", + "log", + "lyon_path", + "raw-window-handle", + "rustc-hash 2.1.3", + "thiserror 2.0.21", + "unicode-segmentation", +] + +[[package]] +name = "iced_program" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "iced_graphics", + "iced_runtime", +] + +[[package]] +name = "iced_renderer" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "iced_graphics", + "iced_tiny_skia", + "iced_wgpu", + "log", + "thiserror 2.0.21", +] + +[[package]] +name = "iced_runtime" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "build_helpers", + "bytes", + "dnd", + "iced_accessibility", + "iced_core", + "iced_futures", + "raw-window-handle", + "thiserror 2.0.21", + "window_clipboard", +] + +[[package]] +name = "iced_tiny_skia" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "bytemuck", + "cosmic-text", + "iced_debug", + "iced_graphics", + "kurbo 0.10.4", + "log", + "resvg", + "rustc-hash 2.1.3", + "softbuffer", + "tiny-skia 0.11.4", +] + +[[package]] +name = "iced_wgpu" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "as-raw-xcb-connection", + "bitflags 2.13.2", + "build_helpers", + "bytemuck", + "cryoglyph", + "futures", + "glam", + "guillotiere", + "iced_debug", + "iced_graphics", + "log", + "lyon", + "raw-window-handle", + "resvg", + "rustc-hash 2.1.3", + "rustix 0.38.44", + "thiserror 2.0.21", + "tiny-xlib", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-sys", + "wgpu", + "x11rb", +] + +[[package]] +name = "iced_widget" +version = "0.14.2" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "build_helpers", + "dnd", + "iced_accessibility", + "iced_renderer", + "iced_runtime", + "log", + "num-traits", + "ouroboros", + "rustc-hash 2.1.3", + "thiserror 2.0.21", + "unicode-segmentation", + "window_clipboard", +] + +[[package]] +name = "iced_winit" +version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "build_helpers", + "cursor-icon", + "dnd", + "iced_accessibility", + "iced_debug", + "iced_futures", + "iced_graphics", + "iced_program", + "iced_runtime", + "log", + "rustc-hash 2.1.3", + "rustix 0.38.44", + "thiserror 2.0.21", + "wasm-bindgen-futures", + "wayland-client", + "web-sys", + "winapi", + "window_clipboard", + "winit", + "winit-core", +] + +[[package]] +name = "icu_collections" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" + +[[package]] +name = "icu_properties" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" + +[[package]] +name = "icu_provider" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", + "tiff", + "zune-core 0.5.3", + "zune-jpeg 0.5.15", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inotify" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +dependencies = [ + "bitflags 2.13.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" +dependencies = [ + "libc", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.21", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "kamadak-exif" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837" +dependencies = [ + "mutate_once", +] + +[[package]] +name = "keyboard-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" +dependencies = [ + "bitflags 2.13.2", + "serde", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "known-folders" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "kqueue" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags 2.13.2", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "libcosmic" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git?rev=430f3d48a97d8779f9c1985473912e594ccca1ae#430f3d48a97d8779f9c1985473912e594ccca1ae" +dependencies = [ + "apply", + "ashpd 0.12.3", + "auto_enums", + "build_helpers", + "cosmic-config", + "cosmic-freedesktop-icons", + "cosmic-settings-daemon", + "cosmic-theme", + "css-color", + "derive_setters", + "enumflags2", + "float-cmp 0.10.0", + "futures", + "i18n-embed", + "i18n-embed-fl", + "iced", + "iced_accessibility", + "iced_core", + "iced_futures", + "iced_renderer", + "iced_runtime", + "iced_tiny_skia", + "iced_wgpu", + "iced_widget", + "iced_winit", + "image", + "jiff", + "log", + "palette", + "phf", + "rfd", + "roxmltree", + "rust-embed", + "serde", + "slotmap", + "taffy", + "thiserror 2.0.21", + "tokio", + "tracing", + "unicode-segmentation", + "url", + "zbus", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" +dependencies = [ + "bitflags 2.13.2", + "libc", + "plain", + "redox_syscall 0.9.5", +] + +[[package]] +name = "lilt" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "337d4c256f7d9f2dbd633891d48ace853efa5b1554122d9220b172ad9c03c3a9" +dependencies = [ + "web-time", +] + +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "litrs" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" + +[[package]] +name = "lyon" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0578bdecb7d6d88987b8b2b1e3a4e2f81df9d0ece1078623324a567904e7b7" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a6b9d4e8b48acd531fbf1241ad9b270df6e60b6b92bdea1469f4f40899d805" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82dd26aa4dc0b5b70b9115de59824842bcb43cf4a28e1b1e6d0ad148c93f93e" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c463f9c428b7fc5ec885dcd39ce4aa61e29111d0e33483f6f98c74e89d8621e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd4872650e70dc907dd4f7ebc449d7d657a0d2658e423b2e724970870bbbf75b" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memmap2" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +dependencies = [ + "bitflags 2.13.2", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "smithay-clipboard", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime 0.3.17", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "mutate_once" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" + +[[package]] +name = "naga" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.21", + "unicode-ident", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.2", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "notify" +version = "8.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f812702fbc75d35416d3f884e04925be7e9498f4eceea2e24ba34559284674" +dependencies = [ + "bitflags 2.13.2", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "objc2-core-foundation", + "objc2-core-services", + "walkdir", + "windows-sys 0.61.2", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d5490aaf8f1d7cf7688dfa9b0ce07900e168852c45cd2c03f534dfd27cfd0b" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "dispatch2", + "objc2 0.6.5", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.2", + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-core-services" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.13.2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orbclient" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "ordered-float" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "palette" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64" +dependencies = [ + "approx", + "palette_derive", + "palette_math", + "phf", + "serde", +] + +[[package]] +name = "palette_derive" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "palette_math" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", + "uncased", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", + "uncased", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bee6c73da26345c729282832b60b0363cf3dd9f4bfd81d8551b7a1c889a113" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.5", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr3" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e564d14133360e1ae169ffde5da25881b5fa47261665b8e5713c212c27799da" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error3" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0d4471b3436c22106b21913b1dda531558918ae9b7ec55d58aa84b43552233" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "version_check", + "yansi", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "rangemap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "core_maths", + "font-types 0.11.3", +] + +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types 0.12.7", + "once_cell", +] + +[[package]] +name = "redox_event" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" +dependencies = [ + "bitflags 2.13.2", + "libredox", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "redox_syscall" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "580c875845b41ecb1564ad1ae4703044cef774145e3d7a94dc2b3fa7c97868fd" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "redox_users" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" +dependencies = [ + "libredox", + "thiserror 2.0.21", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "gif", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia 0.11.4", + "usvg", + "zune-jpeg 0.4.21", +] + +[[package]] +name = "rfd" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fe518b6db89699d59862e4d1e3a0226316090f4e4ec09f7dd686be25634c7c" +dependencies = [ + "ashpd 0.11.1", + "block2 0.6.2", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ron" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3" +dependencies = [ + "bitflags 2.13.2", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rust-embed" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19afa5b4b6a611de00bd1bdae6ae6f39084c9399f0679c3f52d8469cf335cc23" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d8afda6374eac59e066abee06d265247ebbaf3006cf878e2879e8356e34053" +dependencies = [ + "mime_guess", + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.119", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d84e8ba78bd384263e5922f084cbe1b081c3b7e69add59c8fb097b879ba968a" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia 0.12.0", +] + +[[package]] +name = "self_cell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "foldhash 0.2.0", + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sha2" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts 0.37.0", +] + +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts 0.41.0", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" + +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.13.2", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 1.1.5", + "thiserror 2.0.21", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.8.0" +source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" +dependencies = [ + "libc", + "raw-window-handle", + "smithay-client-toolkit", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb7109d23f319619d4cc0de5464613c8b7b1a09db2774a58101459cf6be2f10" +dependencies = [ + "borsh", + "serde_core", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.1" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases", + "cocoa", + "core-graphics", + "drm", + "fastrand", + "foreign-types", + "js-sys", + "log", + "memmap2", + "objc", + "raw-window-handle", + "redox_syscall 0.5.18", + "rustix 0.38.44", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", + "x11rb", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp 0.9.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" +dependencies = [ + "skrifa 0.44.0", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "taffy" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ba83ebaf2954d31d05d67340fd46cebe99da2b7133b0dd68d70c65473a437b" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix 1.1.5", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg 0.5.15", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path 0.11.4", +] + +[[package]] +name = "tiny-skia" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.12.0", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-skia-path" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4" +dependencies = [ + "as-raw-xcb-connection", + "ctor", + "libloading", + "pkg-config", + "tracing", +] + +[[package]] +name = "tinystr" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.17+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.3", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unic-langid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unicase" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" + +[[package]] +name = "unicode-ccc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path 0.11.4", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmtimer" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + +[[package]] +name = "wayland-backend" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.5", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" +dependencies = [ + "bitflags 2.13.2", + "rustix 1.1.5", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.13.2", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +dependencies = [ + "rustix 1.1.5", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" +dependencies = [ + "arrayvec", + "bitflags 2.13.2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "28.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags 2.13.2", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.21", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "28.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.13.2", + "block", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.21", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", +] + +[[package]] +name = "wgpu-types" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "js-sys", + "log", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window_clipboard" +version = "0.4.1" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "clipboard-win", + "clipboard_macos", + "clipboard_wayland", + "clipboard_x11", + "dnd", + "mime 0.1.0", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "cfg_aliases", + "cursor-icon", + "dpi", + "libc", + "raw-window-handle", + "rustix 1.1.5", + "smol_str", + "tracing", + "winit-android", + "winit-appkit", + "winit-common", + "winit-core", + "winit-orbital", + "winit-uikit", + "winit-wayland", + "winit-web", + "winit-win32", + "winit-x11", +] + +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "android-activity", + "bitflags 2.13.2", + "dpi", + "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "dispatch2", + "dpi", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation 0.3.2", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "memmap2", + "objc2 0.6.5", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core", + "x11-dl", + "xkbcommon-dl", +] + +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "cursor-icon", + "dpi", + "keyboard-types", + "raw-window-handle", + "smol_str", + "web-time", +] + +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "dpi", + "libredox", + "orbclient", + "raw-window-handle", + "redox_event", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "dispatch2", + "dpi", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-ui-kit", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "ahash", + "bitflags 2.13.2", + "calloop", + "cursor-icon", + "dpi", + "libc", + "memmap2", + "raw-window-handle", + "rustix 1.1.5", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "atomic-waker", + "bitflags 2.13.2", + "concurrent-queue", + "cursor-icon", + "dpi", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winit-core", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "cursor-icon", + "dpi", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core", +] + +[[package]] +name = "winit-x11" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "calloop", + "cursor-icon", + "dpi", + "libc", + "percent-encoding", + "raw-window-handle", + "rustix 1.1.5", + "smol_str", + "tracing", + "winit-common", + "winit-core", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.5", + "x11rb-protocol", + "xcursor", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" + +[[package]] +name = "xdg" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.13.2", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "yoke" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.5", + "serde", + "serde_repr", + "tokio", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus-lockstep" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.9", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "5.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" +dependencies = [ + "serde", + "winnow", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core 0.5.3", +] + +[[package]] +name = "zvariant" +version = "5.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow", + "zcheapstr", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.9", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.9", + "winnow", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3a99088 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +resolver = "2" +members = ["crates/gipfel-core", "crates/gipfel-cli", "crates/gipfel-app"] + +[workspace.dependencies] +kamadak-exif = "0.6" +quick-xml = { version = "0.41", default-features = false } +tiff = "0.11" +dirs = "6" +image = { version = "0.25", default-features = false, features = ["jpeg", "tiff", "pnm"] } + +[profile.release] +lto = true +debug = 1 diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index c8ee547..0000000 --- a/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -SUBDIRS = src -dist_doc_DATA = README NEWS - -EXTRA_DIST = gipfel.dat gipfel.spec -gpsdir = $(datadir)/gipfel -gps_DATA = gipfel.dat diff --git a/README.md b/README.md new file mode 100644 index 0000000..6099395 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# gipfel-rs + +Rust port of [gipfel](https://git.flpsed.org/gipfel)'s computational core. + +- `crates/gipfel-core` — hills, panorama geometry, projections (maxima-generated + symbolic derivatives), pure-Rust Levenberg-Marquardt fit, `gipfel:` JPEG comment + EXIF I/O +- `crates/gipfel-cli` — `export-position`, `export-hills`, `info` +- `codegen/` — maxima scripts (`expr2rust.mac`) and `regen.sh` +- `tests/` — C reference harnesses + golden files from the original C++ build + +```sh +cargo test --lib --test golden # unit + golden tests +cargo run -p gipfel-cli -- info <img.jpg> +``` + +The Rust build has no native C dependencies. Regenerate goldens with +`tests/gen_golden.sh` (needs the built C++ tree, which uses GSL; see +`GIPFEL_SRC`/`GIPFEL_DATA` in the script). + +## gipfel-app (M2/M3) + +COSMIC desktop application: `cargo build --release -p gipfel-app` → +`target/release/gipfel [image.jpg] [--data gipfel.dat]`. + +- Viewport: ctrl+wheel zoom (cursor-anchored), wheel/drag pan, fit/zoom buttons +- Left-drag a flag to reposition (refits on release); middle-click or + double-click toggles a hill "known"; right-click → Find Peak / Hide-Unhide +- Viewpoint / Find Peak pickers are searchable dialogs +- Sidebar: compass dial, focal/pitch/roll/visibility/track sliders, viewpoint + lat/lon/height, distortion k0/k1/x0, projection, show-hidden +- File menu: Open/Save (writes the `gipfel:` comment), Screen Dump (PNG/JPG + with markers burned in), Load Track (CSV: lat,lon,height rows) +- Install `crates/gipfel-app/data/org.gipfel.App.desktop` per freedesktop diff --git a/codegen/expr2rust.mac b/codegen/expr2rust.mac new file mode 100644 index 0000000..e4e6be8 --- /dev/null +++ b/codegen/expr2rust.mac @@ -0,0 +1,31 @@ +/* + * Rust code generation for gipfel-rs. + * + * Port of gipfel's expr2c.mac (Copyright 2006-2009 Johannes Hofmann). + * Emits one Rust function per line (grep "^pub fn"); integer literals are + * converted to float literals by fixup.py afterwards. + */ + +load("stringproc")$ + +varsubst(v) := ssubst("_", "%", string(v))$ + +printdecl(vars) := ( + sprint("let mut", varsubst(first(vars)), ": f64 = 0.0;"), + for v in rest(vars) do sprint("let mut", varsubst(v), ": f64 = 0.0;"))$ + +printdef(expr) := sprint(varsubst(first(expr)), "=", varsubst(second(expr)), ";")$ + +block2rust(expr) := + if ?equal(op(expr), block) then ( + printdecl(first(expr)), + for d in reverse(rest(reverse(rest(expr)))) do printdef(d), + sprint("return", varsubst(last(expr)), ";")) + else + sprint("return", varsubst(expr), ";")$ + +expr2rust(funcname, argstr, expr) := ( + sprint("pub fn", funcname, "(", argstr, ") -> f64 {"), + block2rust(subst(pow, "^", optimize(expr))), + sprint("}", " +"))$ diff --git a/codegen/fixup.py b/codegen/fixup.py new file mode 100644 index 0000000..1131f11 --- /dev/null +++ b/codegen/fixup.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +"""Post-process maxima output for Rust: + - convert bare integer literals to float literals (1 -> 1.0) + - drop maxima noise lines (keep only 'pub fn' lines) +""" +import re +import sys + +pat = re.compile(r"(?<![\w.])-?\d+(?![\w.])") + +for line in sys.stdin: + if not line.startswith("pub fn"): + continue + sys.stdout.write(pat.sub(lambda m: m.group(0) + ".0", line)) diff --git a/codegen/lsq_cylindrical.mac b/codegen/lsq_cylindrical.mac new file mode 100644 index 0000000..316d3f9 --- /dev/null +++ b/codegen/lsq_cylindrical.mac @@ -0,0 +1,38 @@ +/* + * Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> + */ + +/* + * cylindrical projection model + */ + +x : m_view - c_view$ +y : tan(c_pitch - m_elev)$ +x_rot : y * sin(c_roll) + x * cos(c_roll)$ +y_rot : y * cos(c_roll) - x * sin(c_roll)$ +x_scale : x_rot * scale$ +y_scale : y_rot * scale$ + +/* + * Some mangling for C code generation + */ + +load("expr2rust.mac")$ + +x_expand : trigexpand(x_scale)$ +y_expand : trigexpand(y_scale)$ + +args: "c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64"$ + +expr2rust("mac_x", args, x_expand)$ +expr2rust("mac_y", args, y_expand)$ + +expr2rust("mac_x_dc_view", args, diff(x_expand, c_view))$ +expr2rust("mac_x_dc_pitch", args, diff(x_expand, c_pitch))$ +expr2rust("mac_x_dc_roll", args, diff(x_expand, c_roll))$ +expr2rust("mac_x_dscale", args, diff(x_expand, scale))$ +expr2rust("mac_y_dc_view", args, diff(y_expand, c_view))$ +expr2rust("mac_y_dc_pitch", args, diff(y_expand, c_pitch))$ +expr2rust("mac_y_dc_roll", args, diff(y_expand, c_roll))$ +expr2rust("mac_y_dscale", args, diff(y_expand, scale))$ + diff --git a/codegen/lsq_rectilinear.mac b/codegen/lsq_rectilinear.mac new file mode 100644 index 0000000..4dbb36a --- /dev/null +++ b/codegen/lsq_rectilinear.mac @@ -0,0 +1,60 @@ +/* + * Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> + */ + +/* + * rectilinear (pinhole) projection model with distortion correction + */ + +/* switch to cartesian coordinates */ +c_x : cos(m_elev) * cos(m_view - c_view)$ +c_y : cos(m_elev) * sin(m_view - c_view)$ +c_z : sin(m_elev)$ + +/* pitch rotation */ +c_x_rot : cos(c_pitch) * c_x + sin(c_pitch) * c_z$ +c_y_rot : c_y$ +c_z_rot : -sin(c_pitch) * c_x + cos(c_pitch) * c_z$ + +/* pinhole projection */ +x : c_y_rot / c_x_rot$ +y : -c_z_rot / c_x_rot$ + +/* rotation around view axis */ +x_rot : y * sin(c_roll) + x * cos(c_roll) + x0$ +y_rot : y * cos(c_roll) - x * sin(c_roll)$ + +/* distortion correction */ +d : sqrt(x_rot ^ 2 + y_rot ^ 2)$ +dist_fact : d ^ 2 * k1 + d * k0$ +x_dist : x_rot * (1 + dist_fact) * scale$ +y_dist : y_rot * (1 + dist_fact) * scale$ + +/* + * Some mangling for C code generation + */ + +load("expr2rust.mac")$ + +x_expand : trigexpand(x_dist)$ +y_expand : trigexpand(y_dist)$ + +args: "c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64"$ + +expr2rust("mac_x", args, x_expand)$ +expr2rust("mac_y", args, y_expand)$ + +expr2rust("mac_x_dc_view", args, diff(x_expand, c_view))$ +expr2rust("mac_x_dc_pitch", args, diff(x_expand, c_pitch))$ +expr2rust("mac_x_dc_roll", args, diff(x_expand, c_roll))$ +expr2rust("mac_x_dscale", args, diff(x_expand, scale))$ +expr2rust("mac_x_dk0", args, diff(x_expand, k0))$ +expr2rust("mac_x_dk1", args, diff(x_expand, k1))$ +expr2rust("mac_x_dx0", args, diff(x_expand, x0))$ +expr2rust("mac_y_dc_view", args, diff(y_expand, c_view))$ +expr2rust("mac_y_dc_pitch", args, diff(y_expand, c_pitch))$ +expr2rust("mac_y_dc_roll", args, diff(y_expand, c_roll))$ +expr2rust("mac_y_dscale", args, diff(y_expand, scale))$ +expr2rust("mac_y_dk0", args, diff(y_expand, k0))$ +expr2rust("mac_y_dk1", args, diff(y_expand, k1))$ +expr2rust("mac_y_dx0", args, diff(y_expand, x0))$ diff --git a/codegen/regen.sh b/codegen/regen.sh new file mode 100755 index 0000000..dbf9f6b --- /dev/null +++ b/codegen/regen.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Regenerate the symbolic projection functions with maxima. +# Run from anywhere; requires maxima (and rustfmt if available). +set -e +cd "$(dirname "$0")" +OUT=../crates/gipfel-core/src/projection/generated + +for p in rectilinear cylindrical; do + maxima -b lsq_${p}.mac | python3 fixup.py > ${OUT}/${p}_funcs.rs +done + +if command -v rustfmt >/dev/null; then + rustfmt ${OUT}/rectilinear_funcs.rs ${OUT}/cylindrical_funcs.rs 2>/dev/null || true +fi +echo "generated:" +wc -l ${OUT}/*_funcs.rs diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 1766ac2..0000000 --- a/configure.ac +++ /dev/null @@ -1,68 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) -AC_INIT(gipfel, 0.4.1, Johannes.Hofmann@gmx.de) -AM_INIT_AUTOMAKE -AC_CONFIG_SRCDIR([src/Panorama.H]) -AC_CONFIG_HEADER(config.h) - -CPPFLAGS="-Wall -g -I/usr/local/include -DSTD_DATADIR=\\\"$datadir\\\" -DDOCDIR=\\\"$docdir\\\" $CPPFLAGS" -LDFLAGS="-g $LDFLAGS" - -# Checks for programs. -AC_PROG_CXX -AC_PROG_CC - -AC_LANG_CPLUSPLUS - -# Checks for libraries. - -# Checks for header files. -AC_HEADER_STDC -AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_C_CONST -AC_TYPE_OFF_T -AC_TYPE_PID_T - -# Checks for library functions. -AC_FUNC_FORK -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_TYPE_SIGNAL -AC_CHECK_FUNCS([strchr strdup strrchr strstr mkstemp fsync strsep]) - -# Check for fltk -AC_PATH_PROG(FLTKCONFIG,fltk-config) -if test "x$FLTKCONFIG" = x; then - echo "fltk-config not found" - exit 1 -fi - -CXXFLAGS="`$FLTKCONFIG --use-images --cflags` $CXXFLAGS" -LIBS="`$FLTKCONFIG --use-images --ldflags` $LIBS" - -# Check for gsl -AC_PATH_PROG(GSLCONFIG,gsl-config) -if test "x$GSLCONFIG" = x; then - echo "gsl-config not found" - exit 1 -fi - -CXXFLAGS="`$GSLCONFIG --cflags` $CXXFLAGS" -LIBS="`$GSLCONFIG --libs` $LIBS" - -# Check for libtiff -AC_CHECK_HEADERS([tiffio.h], [], [echo "Error: tiffio.h not found."; exit 1;]) -AC_CHECK_LIB([tiff], [TIFFOpen], [], [echo "Error: libtiff.so not found."; exit 1;]) - -# Check for exiv2 -AC_CHECK_HEADERS([exiv2/exif.hpp], [], [echo "Error: exiv2/exif.hpp not found."; exit 1;]) -LIBS="-lexiv2 $LIBS" - -AC_CONFIG_FILES([Makefile src/Makefile]) -AC_OUTPUT diff --git a/crates/gipfel-app/Cargo.toml b/crates/gipfel-app/Cargo.toml new file mode 100644 index 0000000..22589c1 --- /dev/null +++ b/crates/gipfel-app/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "gipfel-app" +version = "0.1.0" +edition = "2021" +description = "gipfel port to Cosmic DE" +license = "GPL-2.0-or-later" + +[[bin]] +name = "gipfel" +path = "src/main.rs" + +[dependencies] +gipfel-core = { path = "../gipfel-core" } +libcosmic = { git = "https://github.com/pop-os/libcosmic.git", rev = "430f3d48a97d8779f9c1985473912e594ccca1ae", default-features = false, features = ["winit", "wgpu", "tokio", "a11y", "xdg-portal"] } +image.workspace = true +fontdue = "0.9" +cosmic-text = "0.19" +fontdb = "0.23" +tokio = { version = "1", features = ["time"] } diff --git a/crates/gipfel-app/data/org.gipfel.App.desktop b/crates/gipfel-app/data/org.gipfel.App.desktop new file mode 100644 index 0000000..b20c468 --- /dev/null +++ b/crates/gipfel-app/data/org.gipfel.App.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +Name=gipfel +GenericName=Panorama annotator +Comment=Annotate mountain panoramas +Exec=gipfel %U +TryExec=gipfel +Icon=org.gipfel.App +Terminal=false +Categories=Graphics;Photography; +Keywords=mountain;panorama;photo;annotation; +MimeType=image/jpeg;image/png; +StartupNotify=true diff --git a/crates/gipfel-app/src/compass.rs b/crates/gipfel-app/src/compass.rs new file mode 100644 index 0000000..a17cd5c --- /dev/null +++ b/crates/gipfel-app/src/compass.rs @@ -0,0 +1,611 @@ +//! Experimental compass strip: shows N/E/S/W at their projected positions +//! relative to the view direction; drag horizontally to change the direction. +//! +//! Two experimental styles (switchable from the View menu): +//! - `Aligned`: the strip shares the image's horizontal transform, so each +//! direction appears exactly above its position in the image. Grab-drag +//! keeps the direction under the cursor fixed. +//! - `Ruler`: a fixed 0..360° ruler across the widget width; press or drag +//! sets the direction to the value under the cursor. The shaded band marks +//! the current horizontal field of view. +//! +//! Layout (top to bottom): direction labels, center marker, tick scale on a +//! baseline. The whole strip sits on a secondary-container background so it +//! is visually distinct from the image (pan) area. + +use std::cell::RefCell; +use std::rc::Rc; + +use cosmic::iced::advanced::graphics::geometry::Renderer as _; +use cosmic::iced::advanced::renderer::Style as RendererStyle; +use cosmic::iced::advanced::text::{self as adv_text, paragraph::Plain, LineHeight}; +use cosmic::iced::advanced::widget::{tree, Tree, Widget as WidgetTrait}; +use cosmic::iced::advanced::{Clipboard, Layout, Shell}; +use cosmic::iced::mouse::{self, Button, Cursor, Interaction}; +use cosmic::iced::widget::canvas::{Frame, Path, Stroke}; +use cosmic::iced::advanced::graphics::geometry::Style as GeoStyle; +use cosmic::iced::{alignment, Color, Event, Length, Pixels, Point, Rectangle, Size}; +use cosmic::Renderer as R; +use cosmic::Theme; + +use gipfel_core::projection::{ + projection_from_type, Projection, ProjArgs, ProjectionType, ViewParams, +}; + +pub const STRIP_H: f32 = 56.0; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CompassStyle { + Off, + Aligned, + Ruler, +} + +/// inverse: azimuth whose image x is `x_img`, measured with `c_ref` as +/// center angle. Analytic base inverse + damped Newton refinement so lens +/// distortion is honoured. +/// Damped, clamped Newton iteration for a scalar inverse projection: find +/// `a` such that `f(a) ≈ target`. `f` returns `None` where the forward +/// projection is undefined (the iteration stops there). The step is clamped +/// to ±0.3 rad and `a` kept in `[lo, hi]` to stay off the projection poles. +pub(crate) fn newton_inverse( + target: f64, + mut f: impl FnMut(f64) -> Option<f64>, + seed: f64, + lo: f64, + hi: f64, +) -> f64 { + let mut a = seed.clamp(lo, hi); + for _ in 0..6 { + let Some(y) = f(a) else { break }; + let resid = y - target; + let h = 1e-5; + let (Some(y1), Some(y2)) = (f(a + h), f(a - h)) else { + break; + }; + let d = (y1 - y2) / (2.0 * h); + if !d.is_finite() || d.abs() < 1e-9 { + break; + } + let step = (resid / d).clamp(-0.3, 0.3); + a = (a - step).clamp(lo, hi); + if step.abs() < 1e-10 { + break; + } + } + a +} + +pub fn alph_at_img_x( + ptype: ProjectionType, + proj: &dyn Projection, + parms: &ViewParams, + x_img: f64, + c_ref: f64, +) -> f64 { + let mut p = *parms; + p.a_center = c_ref; + // analytic base inverse (ignores distortion); Newton refines it + let seed = match ptype { + ProjectionType::Rectilinear => c_ref + (x_img / p.scale).atan(), + ProjectionType::Cylindrical => c_ref + x_img / p.scale, + }; + // rectilinear has a pole at |alph - center| = 90°; keep Newton inside + let (lo, hi) = match ptype { + ProjectionType::Rectilinear => (c_ref - 1.55, c_ref + 1.55), + ProjectionType::Cylindrical => (c_ref - 3.1, c_ref + 3.1), + }; + newton_inverse( + x_img, + |a| Some(proj.mac_x(ProjArgs::from_params(&p, a, 0.0))), + seed, + lo, + hi, + ) +} + +pub struct Compass<M: Clone + Send + 'static> { + pub style: CompassStyle, + pub center_deg: f64, + pub zoom: f32, + pub pan_x: f32, + pub parms: ViewParams, + pub ptype: ProjectionType, + /// horizontal field of view of the image, degrees (Ruler shading) + pub fov_deg: f64, + /// lens field-of-view half-angle limit in radians (0 = none). Ticks and + /// direction labels beyond this are dropped: a distortion model can fold + /// out-of-view directions back onto the strip, showing bogus ticks. + pub max_view_angle: f64, + pub on_change: Rc<dyn Fn(f64) -> M>, +} + +impl<M: Clone + Send + 'static> Clone for Compass<M> { + fn clone(&self) -> Self { + Compass { + style: self.style, + center_deg: self.center_deg, + zoom: self.zoom, + pan_x: self.pan_x, + parms: self.parms, + ptype: self.ptype, + fov_deg: self.fov_deg, + max_view_angle: self.max_view_angle, + on_change: self.on_change.clone(), + } + } +} + +#[derive(Default)] +struct CompassState { + /// (press x, center angle at press) + drag: Option<(f32, f64)>, + /// strong refs: the text pipeline only keeps a Weak to each paragraph + paragraphs: RefCell<Vec<Plain<<R as adv_text::Renderer>::Paragraph>>>, +} + +const DIRS: [(f64, &str); 8] = [ + (0.0, "N"), + (45.0, "NE"), + (90.0, "E"), + (135.0, "SE"), + (180.0, "S"), + (225.0, "SW"), + (270.0, "W"), + (315.0, "NW"), +]; + +/// one text to draw: content, x, y, center around x?, color +struct Txt { + text: String, + x: f32, + y: f32, + centered: bool, + color: Color, +} + +fn stroke_tick(frame: &mut Frame<R>, x: f32, y0: f32, h: f32, w: f32, color: Color) { + if !x.is_finite() { + return; + } + // snap thin lines to integer coords for crisp 1 px strokes + let x = if w < 1.5 { x.round() } else { x }; + let p = Path::new(|p| { + p.move_to(Point::new(x, y0)); + p.line_to(Point::new(x, y0 - h)); + }); + frame.stroke( + &p, + Stroke { + width: w, + style: GeoStyle::Solid(color), + ..Default::default() + }, + ); +} + +impl<M: Clone + Send + 'static> Compass<M> { + /// image-space x of azimuth `alph` (on the horizon), current parms + fn x_img_at(&self, proj: &dyn Projection, alph: f64) -> f64 { + proj.get_coordinates(alph, 0.0, &self.parms).0 + } + + fn alph_at_img_x( + &self, + proj: &dyn Projection, + x_img: f64, + c_ref: f64, + ) -> f64 { + alph_at_img_x(self.ptype, proj, &self.parms, x_img, c_ref) + } + + /// screen x of the image center for the Aligned style + fn center_x(&self, b: Rectangle) -> f32 { + b.x + b.width / 2.0 + self.pan_x + } +} + +impl<M: Clone + Send + 'static> WidgetTrait<M, Theme, R> for Compass<M> { + fn size(&self) -> Size<Length> { + Size::new(Length::Fill, Length::Fixed(STRIP_H)) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::<CompassState>() + } + + fn state(&self) -> tree::State { + tree::State::new(CompassState::default()) + } + + fn layout( + &mut self, + _tree: &mut Tree, + _renderer: &R, + limits: &cosmic::iced::advanced::layout::Limits, + ) -> cosmic::iced::advanced::layout::Node { + cosmic::iced::advanced::layout::Node::new(Size::new(limits.max().width, STRIP_H)) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut R, + theme: &Theme, + _style: &RendererStyle, + layout: Layout<'_>, + _cursor: Cursor, + _viewport: &Rectangle, + ) { + use cosmic::iced::advanced::text::Renderer as _; + + let b = layout.bounds(); + let _ = theme; + // dark instrument panel, clearly distinct from the image area + let on: Color = Color::from_rgb(0.85, 0.85, 0.85); + let bg: Color = Color::from_rgb(0.16, 0.16, 0.16); + let divider: Color = Color::from_rgb(0.32, 0.32, 0.32); + let red = Color::from_rgb(1.0, 0.4, 0.25); + + // geometry rows (snapped so lines stay crisp) + let label_y = (b.y + 5.0).round(); + let y0 = (b.y + STRIP_H - 10.0).round(); // baseline + let tri_y = (b.y + 25.0).round(); + let z = self.zoom; + let proj = projection_from_type(self.ptype); + + let mut texts: Vec<Txt> = Vec::new(); + let mut frame = Frame::with_bounds(renderer, b); + + // panel background + top divider: clearly a separate, draggable area + let panel = Path::new(|p| p.rectangle(b.position(), b.size())); + frame.fill(&panel, bg); + let sep = Path::new(|p| { + p.move_to(Point::new(b.x, b.y + 0.5)); + p.line_to(Point::new(b.x + b.width, b.y + 0.5)); + }); + frame.stroke( + &sep, + Stroke { + width: 1.0, + style: GeoStyle::Solid(divider), + ..Default::default() + }, + ); + + match self.style { + CompassStyle::Off => return, + CompassStyle::Aligned => { + let cx = self.center_x(b); + let c = self.parms.a_center; + // pixels per degree at the center + let ppd = ((self.x_img_at(&*proj, c + 1f64.to_radians()) + - self.x_img_at(&*proj, c - 1f64.to_radians())) + * z as f64 + / 2.0) + .abs(); + let step = [1.0f64, 2.0, 5.0, 10.0, 15.0, 30.0, 45.0, 90.0] + .into_iter() + .find(|s| s * ppd >= 6.0) + .unwrap_or(90.0); + + let a_lo = self + .alph_at_img_x(&*proj, ((b.x - cx) / z) as f64, c) + .min(c - std::f64::consts::PI); + let a_hi = self + .alph_at_img_x(&*proj, ((b.x + b.width - cx) / z) as f64, c) + .max(c + std::f64::consts::PI); + // the rectilinear projection folds directions behind the + // camera onto the same pixels (tan has 180 deg period); + // never draw beyond +-88 deg of the center. Cylindrical is + // linear in azimuth and may legitimately span > 180 deg. + let fold = match self.ptype { + ProjectionType::Rectilinear => 1.535, + ProjectionType::Cylindrical => std::f64::consts::PI, + }; + // additionally clamp to the lens FOV so distortion-folded + // out-of-view directions do not get ticks/labels + let fold = if self.max_view_angle > 0.0 { + fold.min(self.max_view_angle) + } else { + fold + }; + let a_lo = a_lo.max(c - fold); + let a_hi = a_hi.min(c + fold); + + // baseline + let line = Path::new(|p| { + p.move_to(Point::new(b.x, y0 + 0.5)); + p.line_to(Point::new(b.x + b.width, y0 + 0.5)); + }); + frame.stroke( + &line, + Stroke { + width: 1.0, + style: GeoStyle::Solid(on), + ..Default::default() + }, + ); + + // ticks (rise from the baseline, stay below the labels); + // `a` is radians, `step` degrees -> convert once + let step_rad = step.to_radians(); + let mut a = (a_lo / step_rad).floor() * step_rad; + while a <= a_hi { + let x = cx + self.x_img_at(&*proj, a) as f32 * z; + let rem = a.to_degrees().rem_euclid(45.0); + let major = rem.min(45.0 - rem) < step / 2.0; + stroke_tick(&mut frame, x, y0, if major { 12.0 } else { 6.0 }, 1.0, on); + a += step_rad; + } + + // direction labels on the top row; skip directions outside + // the +-88 deg window (they would fold onto the same pixel) + for (deg, name) in DIRS { + let alph = deg.to_radians(); + let rel = (alph - c + std::f64::consts::PI).rem_euclid(std::f64::consts::TAU) + - std::f64::consts::PI; + if rel.abs() > fold { + continue; + } + let x = cx + self.x_img_at(&*proj, alph) as f32 * z; + if !x.is_finite() || x < b.x - 20.0 || x > b.x + b.width + 20.0 { + continue; + } + let color = if deg == 0.0 { red } else { on }; + texts.push(Txt { + text: name.to_string(), + x, + y: label_y, + centered: true, + color, + }); + } + + // center marker: triangle + hairline (tick area only) + let tri = Path::new(|p| { + p.move_to(Point::new(cx - 6.0, tri_y)); + p.line_to(Point::new(cx + 6.0, tri_y)); + p.line_to(Point::new(cx, tri_y + 8.0)); + p.close(); + }); + frame.fill(&tri, red); + let hair = Path::new(|p| { + p.move_to(Point::new(cx.round(), tri_y + 8.0)); + p.line_to(Point::new(cx.round(), y0)); + }); + frame.stroke( + &hair, + Stroke { + width: 1.0, + style: GeoStyle::Solid(red), + ..Default::default() + }, + ); + + let deg = self.center_deg.rem_euclid(360.0); + texts.push(Txt { + text: format!("{deg:05.1}°"), + x: cx + 10.0, + y: tri_y - 3.0, + centered: false, + color: red, + }); + } + CompassStyle::Ruler => { + let bx = b.x; + let f = |alph_deg: f64| { + bx + (alph_deg.rem_euclid(360.0) / 360.0) as f32 * b.width + }; + let center = self.center_deg.rem_euclid(360.0); + let mx = f(center); + + // FOV band (between label row and baseline) + let half = (self.fov_deg / 2.0 / 360.0) as f32 * b.width; + let x0 = (mx - half).max(bx); + let x1 = (mx + half).min(bx + b.width); + if x1 > x0 { + let band = Path::new(|p| { + p.rectangle( + Point::new(x0, tri_y), + Size::new(x1 - x0, y0 - tri_y), + ); + }); + frame.fill(&band, Color::from_rgba(0.4, 0.65, 1.0, 0.20)); + } + + // baseline + ticks every 5 deg + let line = Path::new(|p| { + p.move_to(Point::new(bx, y0 + 0.5)); + p.line_to(Point::new(bx + b.width, y0 + 0.5)); + }); + frame.stroke( + &line, + Stroke { + width: 1.0, + style: GeoStyle::Solid(on), + ..Default::default() + }, + ); + for deg in (0..360).step_by(5) { + let major = deg % 45 == 0; + stroke_tick( + &mut frame, + f(deg as f64), + y0, + if major { 12.0 } else { 6.0 }, + 1.0, + on, + ); + } + for (deg, name) in DIRS { + let color = if deg == 0.0 { red } else { on }; + texts.push(Txt { + text: name.to_string(), + x: f(deg), + y: label_y, + centered: true, + color, + }); + } + + // marker + let tri = Path::new(|p| { + p.move_to(Point::new(mx - 6.0, tri_y)); + p.line_to(Point::new(mx + 6.0, tri_y)); + p.line_to(Point::new(mx, tri_y + 8.0)); + p.close(); + }); + frame.fill(&tri, red); + texts.push(Txt { + text: format!("{center:05.1}°"), + x: mx + 10.0, + y: tri_y - 3.0, + centered: false, + color: red, + }); + } + } + + renderer.draw_geometry(frame.into_geometry()); + + // text (kept alive in state until next frame; positions rounded + // so glyphs land on pixel boundaries) + let font = renderer.default_font(); + let state = tree.state.downcast_ref::<CompassState>(); + { + let mut paras = state.paragraphs.borrow_mut(); + paras.clear(); + for t in texts.iter() { + paras.push(Plain::<<R as adv_text::Renderer>::Paragraph>::new(adv_text::Text { + content: t.text.clone(), + bounds: Size::new(120.0, 20.0), + size: Pixels(13.0), + line_height: LineHeight::default(), + font, + align_x: alignment::Horizontal::Left.into(), + align_y: alignment::Vertical::Top.into(), + shaping: adv_text::Shaping::Basic, + wrapping: adv_text::Wrapping::None, + ellipsize: adv_text::Ellipsize::None, + })); + } + } + let paras = state.paragraphs.borrow(); + for (gi, t) in texts.iter().enumerate() { + let para = ¶s[gi]; + let w = para.min_bounds().width; + let x = if t.centered { t.x - w / 2.0 } else { t.x }; + renderer.fill_paragraph( + para.raw(), + Point::new(x.round(), t.y.round()), + t.color, + b, + ); + } + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: Cursor, + _renderer: &R, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, M>, + _viewport: &Rectangle, + ) { + let state = tree.state.downcast_mut::<CompassState>(); + let b = layout.bounds(); + let proj = projection_from_type(self.ptype); + + match event { + Event::Mouse(mouse::Event::ButtonPressed(Button::Left)) => { + let Some(p) = cursor.position_over(b) else { return }; + state.drag = Some((p.x, self.center_deg)); + if self.style == CompassStyle::Ruler { + let alph = ((p.x - b.x) / b.width * 360.0) as f64; + shell.publish((self.on_change)(alph)); + } + } + Event::Mouse(mouse::Event::ButtonReleased(Button::Left)) => { + state.drag = None; + } + Event::Mouse(mouse::Event::CursorMoved { .. }) => { + let Some((x0, c0)) = state.drag else { return }; + let Some(p) = cursor.position() else { return }; + match self.style { + CompassStyle::Aligned => { + let cx = self.center_x(b); + let z = self.zoom; + let a0 = + self.alph_at_img_x(&*proj, ((x0 - cx) / z) as f64, c0.to_radians()); + let a1 = + self.alph_at_img_x(&*proj, ((p.x - cx) / z) as f64, c0.to_radians()); + let new_c = (c0.to_radians() - (a1 - a0)).to_degrees(); + shell.publish((self.on_change)(new_c)); + } + CompassStyle::Ruler => { + let alph = ((p.x - b.x) / b.width * 360.0) as f64; + shell.publish((self.on_change)(alph)); + } + CompassStyle::Off => {} + } + } + _ => {} + } + } + + fn mouse_interaction( + &self, + _tree: &Tree, + layout: Layout<'_>, + cursor: Cursor, + _viewport: &Rectangle, + _renderer: &R, + ) -> Interaction { + if cursor.position_over(layout.bounds()).is_some() { + Interaction::Grab + } else { + Interaction::None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn roundtrip(ptype: ProjectionType, k0: f64, k1: f64, x0: f64) { + let proj = projection_from_type(ptype); + let mut parms = ViewParams::new(); + parms.a_center = 0.3; + parms.scale = 3000.0; + parms.k0 = k0; + parms.k1 = k1; + parms.x0 = x0; + for x in [-2500.0, -800.0, -50.0, 0.0, 120.0, 900.0, 2400.0] { + let a = alph_at_img_x(ptype, &*proj, &parms, x, parms.a_center); + let (x2, _) = proj.get_coordinates(a, 0.0, &parms); + assert!( + (x2 - x).abs() < 1e-6, + "{ptype:?} x={x}: round-trips to {x2} (alph {a})" + ); + } + } + + #[test] + fn inverse_rectilinear() { + roundtrip(ProjectionType::Rectilinear, 0.0, 0.0, 0.0); + } + #[test] + fn inverse_rectilinear_distorted() { + // realistic magnitudes: x0 is in normalized (pre-scale) units + roundtrip(ProjectionType::Rectilinear, 0.002, -0.0003, 0.01); + } + #[test] + fn inverse_cylindrical() { + roundtrip(ProjectionType::Cylindrical, 0.0, 0.0, 0.0); + } +} diff --git a/crates/gipfel-app/src/dump.rs b/crates/gipfel-app/src/dump.rs new file mode 100644 index 0000000..0bcfe56 --- /dev/null +++ b/crates/gipfel-app/src/dump.rs @@ -0,0 +1,285 @@ +//! Screen dump: render markers/labels onto the photo and save as PNG/JPEG. +//! Replaces FLTK's window capture from the original "Screen Dump" menu item. + +use image::RgbaImage; +use std::path::Path; + +pub struct DumpMarker { + pub x: f64, + pub y: f64, + pub name: String, + pub known: bool, + pub hidden: bool, +} + +pub struct DumpTrackPt { + pub x: f64, + pub y: f64, + pub hidden: bool, + pub width: f64, +} + +fn px(img: &mut RgbaImage, x: i64, y: i64, c: [u8; 4]) { + if x >= 0 && y >= 0 && (x as u32) < img.width() && (y as u32) < img.height() { + img.put_pixel(x as u32, y as u32, image::Rgba(c)); + } +} + +fn line(img: &mut RgbaImage, x0: f64, y0: f64, x1: f64, y1: f64, c: [u8; 4], w: i64) { + let dx = x1 - x0; + let dy = y1 - y0; + let n = (dx.hypot(dy) as i64).max(1); + for i in 0..=n { + let t = i as f64 / n as f64; + let (x, y) = (x0 + dx * t, y0 + dy * t); + for oy in -w..=w { + for ox in -w..=w { + px(img, x.round() as i64 + ox, y.round() as i64 + oy, c); + } + } + } +} + +fn circle(img: &mut RgbaImage, cx: f64, cy: f64, r: f64, c: [u8; 4]) { + let n = (2.0 * std::f64::consts::PI * r.max(1.0)) as i64; + for i in 0..=n { + let a = i as f64 / n as f64 * 2.0 * std::f64::consts::PI; + px(img, (cx + r * a.cos()).round() as i64, (cy + r * a.sin()).round() as i64, c); + px(img, (cx + (r + 1.0) * a.cos()).round() as i64, (cy + (r + 1.0) * a.sin()).round() as i64, c); + } +} + +fn tri(img: &mut RgbaImage, p: [(f64, f64); 3], c: [u8; 4]) { + let ymin = p.iter().map(|q| q.y() as i64).min().unwrap(); + let ymax = p.iter().map(|q| q.y() as i64).max().unwrap(); + for y in ymin..=ymax { + let mut xmin = i64::MAX; + let mut xmax = i64::MIN; + for i in 0..3 { + let a = p[i]; + let b = p[(i + 1) % 3]; + if (a.y() as i64) == (b.y() as i64) { + continue; + } + if y >= (a.y() as i64).min(b.y() as i64) && y <= (a.y() as i64).max(b.y() as i64) { + let t = (y as f64 - a.y()) / (b.y() - a.y()); + let x = a.x() + t * (b.x() - a.x()); + xmin = xmin.min(x.round() as i64); + xmax = xmax.max(x.round() as i64); + } + } + for x in xmin..=xmax { + px(img, x, y, c); + } + } +} + +trait XY { + fn x(self) -> f64; + fn y(self) -> f64; +} +impl XY for (f64, f64) { + fn x(self) -> f64 { + self.0 + } + fn y(self) -> f64 { + self.1 + } +} + +fn load_font(family: &str) -> Option<fontdue::Font> { + // Resolve the COSMIC interface font (or system sans-serif fallback) through + // the same font discovery cosmic's text engine uses, then hand the bytes to + // fontdue for the bitmap dump. + static FS: std::sync::OnceLock<std::sync::Mutex<cosmic_text::FontSystem>> = + std::sync::OnceLock::new(); + let mut fs = FS + .get_or_init(|| std::sync::Mutex::new(cosmic_text::FontSystem::new())) + .lock() + .unwrap(); + let fam = [fontdb::Family::Name(family)]; + let id = fs + .db() + .query(&fontdb::Query { families: &fam, ..Default::default() }) + .or_else(|| fs.db().query(&fontdb::Query::default()))?; + let font = fs.get_font(id, fontdb::Weight::NORMAL)?; + fontdue::Font::from_bytes( + font.data().to_vec(), + fontdue::FontSettings { scale: 64.0, ..Default::default() }, + ) + .ok() +} + +struct GlyphBlit { + x: f32, + y: f32, + width: usize, + #[allow(dead_code)] + height: usize, + pixels: Vec<u8>, +} + +fn layout_text(font: &fontdue::Font, text: &str, size: f32) -> Vec<GlyphBlit> { + use fontdue::layout::{CoordinateSystem, Layout as FontLayout, TextStyle}; + let mut l = FontLayout::new(CoordinateSystem::PositiveYDown); + l.append(&[font], &TextStyle::new(text, size, 0)); + l.glyphs() + .iter() + .map(|g| { + let (m, pixels) = font.rasterize_config(g.key); + GlyphBlit { x: g.x, y: g.y, width: m.width, height: m.height, pixels } + }) + .collect() +} + +fn draw_text(img: &mut RgbaImage, font: &fontdue::Font, x: f64, y: f64, text: &str, c: [u8; 4], size: f32) { + for glyph in layout_text(font, text, size) { + let gx0 = (x + glyph.x as f64).round() as i64; + let gy0 = (y + glyph.y as f64).round() as i64; + for (i, &alpha) in glyph.pixels.iter().enumerate() { + if alpha == 0 { + continue; + } + let gx = gx0 + (i % glyph.width) as i64; + let gy = gy0 + (i / glyph.width) as i64; + if gx < 0 || gy < 0 || gx >= img.width() as i64 || gy >= img.height() as i64 { + continue; + } + let a = alpha as u32; + let old = img.get_pixel(gx as u32, gy as u32).0; + let blend = |o: u8, n: u8| ((o as u32 * (255 - a) + n as u32 * a) / 255) as u8; + px(img, gx, gy, [blend(old[0], c[0]), blend(old[1], c[1]), blend(old[2], c[2]), 255]); + } + } +} + +fn text_width(font: &fontdue::Font, text: &str, size: f32) -> f64 { + layout_text(font, text, size) + .iter() + .map(|g| (g.x + g.width as f32) as f64) + .fold(0.0, f64::max) +} + +/// Compose the dump: photo + crosses/leaders/labels + track. +/// +/// `font_size` is the on-screen label font size in screen pixels, `scale` +/// converts screen pixels to image pixels (1 / zoom): the overlay geometry +/// is specified in screen pixels (as seen on the display) but the dump is +/// rendered at the image's native resolution, so every size must be scaled +/// accordingly. Label placement runs the exact same collision-avoiding +/// layout as the on-screen viewport (in screen space, then mapped back to +/// image space), so the dump shows the labels at the same positions. +pub fn render( + img: &RgbaImage, + markers: &[DumpMarker], + track: &[DumpTrackPt], + out: &Path, + font_size: f32, + scale: f64, + font_family: &str, +) -> Result<(), String> { + let mut canvas = img.clone(); + let (w, h) = (img.width() as f64, img.height() as f64); + let (cx, cy) = (w / 2.0, h / 2.0); + let s = scale.max(0.1); + let font_img = f64::from(font_size) * s; + let font = load_font(font_family); + + // track + for win in track.windows(2) { + let (a, b) = (&win[0], &win[1]); + let c = if a.hidden { [51, 102, 255, 255] } else { [255, 51, 51, 255] }; + line(&mut canvas, cx + a.x, cy + a.y, cx + b.x, cy + b.y, c, (a.width as i64).max(1)); + } + + // label layout: the exact same collision-avoiding code as the on-screen + // viewport (see viewport::layout_label_y). It runs in screen space + // (marker positions / scale, screen font size); the resulting offsets + // are mapped back to image space with s. Needed for the leader lines + // and the label drawing below. + let line_h = f64::from(font_size) * 1.3 + 2.0; + let in_img: Vec<bool> = markers + .iter() + .map(|m| m.x.abs() * 2.0 <= w && m.y.abs() * 2.0 <= h) + .collect(); + let layout: Option<Vec<(f32, f32)>> = font.as_ref().map(|font| { + let sx: Vec<f32> = markers.iter().map(|m| (m.x / s) as f32).collect(); + let sy: Vec<f32> = markers.iter().map(|m| (m.y / s) as f32).collect(); + let widths: Vec<f32> = markers + .iter() + .map(|m| text_width(font, &m.name, font_size) as f32) + .collect(); + crate::viewport::layout_label_y(&sx, &sy, &widths, &in_img, line_h as f32) + }); + + let yellow = [255, 230, 51, 255]; + // ~1 screen pixel stroke at image resolution + let lw = ((s - 1.0) / 2.0).round().max(0.0) as i64; + // like the screen: known-hill flags in red + let flag_color = [255, 64, 64, 255]; + for (i, m) in markers.iter().enumerate() { + let (x, y) = (cx + m.x, cy + m.y); + if x < -10.0 || y < -10.0 || x > w + 10.0 || y > h + 10.0 || !in_img[i] { + continue; + } + // yellow cross + leader line up to the label (screen section 3) + let d = 4.0 * s; + line(&mut canvas, x - d, y, x + d, y, yellow, lw); + if let Some(lay) = &layout { + let (lx, ly) = lay[i]; + let label_line = y + (f64::from(ly) - line_h) * s; + line(&mut canvas, x, y, x, label_line, yellow, lw); + line(&mut canvas, x, label_line, x + f64::from(lx) * s, label_line, yellow, lw); + } + } + // known-hill flags (pole + pennant + circle), on top of all leaders + for (i, m) in markers.iter().enumerate() { + if !m.known || !in_img[i] { + continue; + } + let (x, y) = (cx + m.x, cy + m.y); + if x < -10.0 || y < -10.0 || x > w + 10.0 || y > h + 10.0 { + continue; + } + let pole = 16.0 * s; + line(&mut canvas, x, y, x, y - pole, flag_color, (s.round() as i64).max(1)); + tri( + &mut canvas, + [(x, y - pole), (x + 10.0 * s, y - pole * 0.8), (x, y - pole * 0.625)], + flag_color, + ); + circle(&mut canvas, x, y, 3.0 * s, flag_color); + } + + if let (Some(font), Some(layout)) = (&font, &layout) { + for (i, m) in markers.iter().enumerate() { + if !in_img[i] { + continue; + } + let (_, ly) = layout[i]; + let (x, y) = (cx + m.x + 2.0 * s, cy + m.y + (f64::from(ly) - line_h) * s); + if x < 0.0 || y < 0.0 || x > w || y > h { + continue; + } + let tw = text_width(font, &m.name, font_img as f32); + for yy in (y - 2.0 * s) as i64..=(y + font_img + 2.0 * s) as i64 { + for xx in (x - 1.0 * s) as i64..=(x + tw + 1.0 * s) as i64 { + px(&mut canvas, xx, yy, [0, 0, 0, 160]); + } + } + let c = if m.hidden { [102, 153, 255, 255] } else { [255, 255, 255, 255] }; + draw_text(&mut canvas, font, x, y, &m.name, c, font_img as f32); + } + } + + if out + .extension() + .is_some_and(|e| e.eq_ignore_ascii_case("jpg") || e.eq_ignore_ascii_case("jpeg")) + { + let rgb = image::DynamicImage::ImageRgba8(canvas).to_rgb8(); + rgb.save(out.with_extension("jpg")).map_err(|e| e.to_string())?; + } else { + canvas.save(out.with_extension("png")).map_err(|e| e.to_string())?; + } + Ok(()) +} diff --git a/crates/gipfel-app/src/main.rs b/crates/gipfel-app/src/main.rs new file mode 100644 index 0000000..4cf0c63 --- /dev/null +++ b/crates/gipfel-app/src/main.rs @@ -0,0 +1,1805 @@ +//! gipfel for the COSMIC desktop: annotate mountain panoramas. +//! +//! M2/M3: full app skeleton (menus, dialogs, settings sidebar) plus the +//! interactive viewport (marker placement, refit, context menu, track). + +mod compass; +mod dump; +mod viewport; + +use std::marker::PhantomData; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +use cosmic::app::{Core, Task}; +use cosmic::iced::keyboard::{self, Key}; +use cosmic::iced::window::Event as WinEvent; +use cosmic::iced::{event, Color, Event, Length, Subscription, Vector}; +use cosmic::widget::{self, menu, menu::action::MenuAction}; +use cosmic::{Application, ApplicationExt, Element, Theme}; + +use gipfel_core::hill::{self, Hill, FLAG_DUPLIC, FLAG_HIDDEN, FLAG_TRACK_POINT, FLAG_VISIBLE}; +use gipfel_core::metadata::ImageMetaData; +use gipfel_core::panorama::Panorama; +use gipfel_core::projection::ProjectionType; +use gipfel_core::stitch; +use gipfel_core::track; + +use compass::{Compass, CompassStyle, STRIP_H}; + +use viewport::{Marker, TrackPt, ViewportWidget, VpMsg}; + +/// actions of the right-click popover menu on the viewport. +/// The click position / hit index are baked into the action at menu-build +/// time (like cosmic-files bakes its entity), so the handler never depends +/// on state that may have changed while the menu was open. +#[derive(Clone, Copy, PartialEq)] +pub enum CtxAction { + /// image-space position of the right-click + FindPeak(f64, f64), + /// hill index of the clicked marker + ToggleHidden(usize), +} + +impl Eq for CtxAction {} + +impl MenuAction for CtxAction { + type Message = Message; + + fn message(&self) -> Message { + match self { + CtxAction::FindPeak(x, y) => Message::ContextFindPeak(*x, *y), + CtxAction::ToggleHidden(i) => Message::ContextToggleHidden(*i), + } + } +} + +pub struct Flags { + pub data: Option<PathBuf>, + pub image: Option<PathBuf>, +} + +pub struct Gipfel { + core: Core, + pan: Panorama, + project: Option<Project>, + /// hill indices of the known (flagged) markers + known: Vec<usize>, + /// hill indices of the GPS track points, in file order + track: Vec<usize>, + // viewport state + zoom: f32, + fit: bool, + panv: Vector, + hover: Option<usize>, + ctrl: bool, + version: u64, + win_w: f64, + win_h: f64, + vp_w: f64, + vp_h: f64, + // sidebar state + lat_in: String, + lon_in: String, + height_in: String, + k0_in: String, + k1_in: String, + x0_in: String, + profile_in: String, + profiles: Vec<String>, + profile_overwrite: bool, + track_width: f64, + show_hidden: bool, + fit_distortion: bool, + label_font: f32, + compass_style: CompassStyle, + status: String, + // overlays + picker: Option<Picker>, + context: Option<ContextState>, + /// peak info shown in the status bar while the mouse hovers a marker + hover_info: Option<String>, + about: bool, +} + +struct Project { + path: PathBuf, + handle: cosmic::widget::image::Handle, + w: u32, + h: u32, + md: ImageMetaData, +} + +struct Picker { + title: String, + query: String, + mode: PickerMode, +} + +#[derive(Clone, Copy)] +enum PickerMode { + Viewpoint, + FindPeak { img_x: f64, img_y: f64 }, +} + +struct ContextState { + img_x: f64, + img_y: f64, + hit: Option<usize>, +} + +#[derive(Debug, Clone)] +pub enum Message { + Vp(VpMsg), + Event(Event), + WinResized(f64, f64), + FileDropped(Vec<PathBuf>), + + OpenImage, + OpenPath(Option<PathBuf>), + ImageLoaded(Box<Result<LoadedImage, String>>), + SaveImage, + SavePath(Option<PathBuf>), + ImageSaved(Option<PathBuf>), + DumpImage, + ImageDumped(Box<Result<PathBuf, String>>), + StitchTiff, + StitchDone(Box<Result<PathBuf, String>>), + LoadTrack, + TrackLoaded(Box<Result<Vec<Hill>, String>>), + + ChooseViewpoint, + PickerQuery(String), + PickerPick(usize), + PickerClose, + ContextFindPeak(f64, f64), + ContextToggleHidden(usize), + Surface(cosmic::surface::Action<Message>), + Redraw, + + Center(f64), + Pitch(f64), + Roll(f64), + LabelFont(f32), + Focal(f64), + Visibility(f64), + TrackWidth(f64), + Lat(String), + Lon(String), + Height(String), + ViewApply, + K0(String), + K1(String), + X0(String), + DistortionApply, + ProfileName(String), + ProfileSave, + ProfileLoad, + ProfileSelect(usize), + Projection(ProjectionType), + ToggleShowHidden, + FitDistortion(bool), + ToggleNav, + CompassStyle(CompassStyle), + + Fit, + ZoomIn, + ZoomOut, + About(bool), + Quit, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Action { + Open, + Save, + Dump, + Stitch, + LoadTrack, + Quit, + ChooseViewpoint, + ShowHidden, + Fit, + ZoomIn, + ZoomOut, + ProjNormal, + ProjPanoramic, + CompassAligned, + CompassRuler, + CompassOff, + About, +} + +impl MenuAction for Action { + type Message = Message; + fn message(&self) -> Message { + match self { + Action::Open => Message::OpenImage, + Action::Save => Message::SaveImage, + Action::Dump => Message::DumpImage, + Action::Stitch => Message::StitchTiff, + Action::LoadTrack => Message::LoadTrack, + Action::Quit => Message::Quit, + Action::ChooseViewpoint => Message::ChooseViewpoint, + Action::ShowHidden => Message::ToggleShowHidden, + Action::Fit => Message::Fit, + Action::ZoomIn => Message::ZoomIn, + Action::ZoomOut => Message::ZoomOut, + Action::ProjNormal => Message::Projection(ProjectionType::Rectilinear), + Action::ProjPanoramic => Message::Projection(ProjectionType::Cylindrical), + Action::CompassAligned => Message::CompassStyle(CompassStyle::Aligned), + Action::CompassRuler => Message::CompassStyle(CompassStyle::Ruler), + Action::CompassOff => Message::CompassStyle(CompassStyle::Off), + Action::About => Message::About(true), + } + } +} + +impl From<VpMsg> for Message { + fn from(m: VpMsg) -> Self { + Message::Vp(m) + } +} + +#[derive(Clone)] +pub struct LoadedImage { + path: PathBuf, + w: u32, + h: u32, + handle: cosmic::widget::image::Handle, + md: ImageMetaData, +} + +impl std::fmt::Debug for LoadedImage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LoadedImage({})", self.path.display()) + } +} + +/// Load a GPS track from a `.gpx` (GPS Exchange) or `.csv`/`.dat` (gipfel +/// hill-CSV) file, returning hills flagged as track points. +fn load_track_file(path: &Path) -> Result<Vec<Hill>, String> { + if path.extension().is_some_and(|e| e.eq_ignore_ascii_case("gpx")) { + track::load_gpx(&path.to_string_lossy(), 2000) + } else { + let mut hs = hill::load(&path.to_string_lossy()).map_err(|e| e.to_string())?; + for h in hs.iter_mut() { + h.flags |= FLAG_TRACK_POINT; + } + Ok(hs) + } +} + +fn data_candidates(flag: Option<&Path>) -> Vec<PathBuf> { + let mut v = Vec::new(); + if let Some(p) = flag { + v.push(p.to_path_buf()); + } + if let Ok(e) = std::env::var("GIPFEL_DATA") { + v.push(PathBuf::from(e)); + } + v.push(PathBuf::from("gipfel.dat")); + if let Some(home) = std::env::var_os("HOME") { + v.push(PathBuf::from(home).join(".local/share/gipfel/gipfel.dat")); + } + v.push(PathBuf::from("/usr/local/share/gipfel/gipfel.dat")); + v.push(PathBuf::from("/usr/share/gipfel/gipfel.dat")); + v +} + +/// Inverse projection for the vertical axis: the elevation whose projected +/// pixel row (image-centered coords, center column) is `y_img`. Shares the +/// damped Newton iteration with the compass strip (`compass::newton_inverse`). +fn elev_at_img_y(pan: &Panorama, y_img: f64) -> f64 { + let c = pan.parms.a_center; + let p = pan.parms.a_pitch; + let seed = p - (y_img / pan.parms.scale).atan(); + compass::newton_inverse( + y_img, + |e| pan.project_point(c, e).map(|v| v.1), + seed, + p - 1.535, + p + 1.535, + ) +} + +impl Gipfel { + fn bump(&mut self) { + // keep the visibility limit in sync with the lens' actual field of + // view: the angle at which the (distorted) projection reaches the + // image edge. Directions beyond it cannot be inside the photo, even + // if the distortion model folds their projection back into it. + let limit = self.view_angle_limit(); + if (limit - self.pan.max_view_angle).abs() > 1e-9 { + self.pan.max_view_angle = limit; + // keep the user-placed marker positions (observations) intact + self.pan.update_visible_mountains(Some(&self.known)); + } + self.version += 1; + } + + /// Reload the stored profile names from the prefs file. + fn refresh_profiles(&mut self) { + self.profiles = gipfel_core::profile::list().into_iter().map(|(n, _)| n).collect(); + } + + /// Load a stored profile and apply its distortion parameters. + fn apply_profile(&mut self, name: &str) { + match gipfel_core::profile::load(name) { + Ok(p) => { + self.pan.set_distortion_params(p.k0, p.k1, p.x0); + self.sync_inputs(); + self.bump(); + self.status = format!("loaded profile '{name}'"); + } + Err(e) => self.status = format!("profile '{name}': {e}"), + } + } + + /// half-angle (radians) the projection maps the image's right edge to, + /// i.e. the horizontal FOV of the lens including fitted distortion. + /// 0 = no image loaded / not computable. + fn view_angle_limit(&self) -> f64 { + let Some(p) = &self.project else { + return 0.0; + }; + let proj = gipfel_core::projection::projection_from_type(self.pan.projection_type); + let c = self.pan.parms.a_center; + let a_edge = compass::alph_at_img_x( + self.pan.projection_type, + &*proj, + &self.pan.parms, + p.w as f64 / 2.0, + c, + ); + (a_edge - c).abs() + } + + fn img_max(&self) -> f64 { + self.project + .as_ref() + .map(|p| p.w.max(p.h) as f64) + .unwrap_or(1000.0) + } + + fn focal_35mm(&self) -> f64 { + self.pan.get_scale() * 35.0 / self.img_max() + } + + fn set_focal_35mm(&mut self, f: f64) { + self.pan.set_scale(f * self.img_max() / 35.0); + } + + fn sync_inputs(&mut self) { + self.lat_in = format!("{:.5}", self.pan.get_view_lat()); + self.lon_in = format!("{:.5}", self.pan.get_view_long()); + self.height_in = format!("{:.0}", self.pan.get_view_height()); + self.k0_in = format!("{:.5}", self.pan.parms.k0); + self.k1_in = format!("{:.5}", self.pan.parms.k1); + self.x0_in = format!("{:.1}", self.pan.parms.x0); + } + + fn fit_zoom(&self) -> f32 { + let p = match &self.project { + Some(p) => p, + None => return 1.0, + }; + if self.vp_w < 10.0 || self.vp_h < 10.0 { + return 1.0; + } + (self.vp_w / p.w as f64).min(self.vp_h / p.h as f64) as f32 + } + + fn markers(&self) -> Rc<Vec<Marker>> { + let mut v = Vec::new(); + for &i in &self.pan.visible { + let h = &self.pan.hills[i]; + if h.is_track_point() || h.flags & FLAG_DUPLIC != 0 { + continue; + } + let hidden = h.flags & FLAG_HIDDEN != 0; + if hidden && !self.show_hidden { + continue; + } + v.push(Marker { + x: h.x, + y: h.y, + name: h.name.as_str().into(), + idx: i, + known: self.known.contains(&i), + hidden, + }); + } + Rc::new(v) + } + + fn track_pts(&self) -> Rc<Vec<TrackPt>> { + let v = self + .track + .iter() + .filter(|&&i| self.pan.hills[i].flags & FLAG_VISIBLE != 0) + .map(|&i| { + let h = &self.pan.hills[i]; + // C: max(scale * track_width / (dist_m * 10), 1) - distance in + // meters. (Was wrongly divided by km here, inflating widths + // 1000x and breaking stroke triangulation.) Clamped high so + // near-camera points can't produce absurd strokes. + let dist_m = self.pan.get_real_distance(i).max(1.0); + let width = + (self.pan.get_scale() * self.track_width / (dist_m * 10.0)).clamp(1.0, 1000.0); + TrackPt { + x: h.x, + y: h.y, + hidden: h.flags & FLAG_HIDDEN != 0, + width, + } + }) + .collect(); + Rc::new(v) + } + + /// recompute viewport metrics from window size, nav and compass state + fn remeasure(&mut self) { + let nav = if self.core().nav_bar_active() { 340.0 } else { 0.0 }; + let strip = if self.project.is_some() && self.compass_style != CompassStyle::Off { + STRIP_H as f64 + } else { + 0.0 + }; + self.vp_w = (self.win_w - nav).max(100.0); + self.vp_h = (self.win_h - 100.0 - strip).max(100.0); + if self.fit { + self.zoom = self.fit_zoom(); + } + } + + fn refit(&mut self) { + if self.known.is_empty() { + return; + } + if let Err(e) = self.pan.comp_params(&self.known, self.fit_distortion) { + self.status = format!("refit failed: {e}"); + } + self.sync_inputs(); + self.bump(); + } + + fn md_from_state(&self) -> ImageMetaData { + let mut md = ImageMetaData::new(); + if let Some(p) = &self.project { + md.manufacturer = p.md.manufacturer.clone(); + md.model = p.md.model.clone(); + } + md.longitude = self.pan.get_view_long(); + md.latitude = self.pan.get_view_lat(); + md.height = self.pan.get_view_height(); + md.direction = self.pan.get_center_angle(); + md.pitch = self.pan.get_pitch_angle(); + md.roll = self.pan.get_roll_angle(); + md.focal_length_35mm = self.focal_35mm(); + md.projection_type = self.pan.projection_type as i32; + md.k0 = self.pan.parms.k0; + md.k1 = self.pan.parms.k1; + md.x0 = self.pan.parms.x0; + md + } + + fn open_image(&mut self, path: PathBuf) -> Task<Message> { + let task = async move { + let img = image::open(&path).map_err(|e| format!("{}: {e}", path.display()))?; + let (w, h) = (img.width(), img.height()); + let rgba = img.to_rgba8(); + let handle = cosmic::widget::image::Handle::from_rgba(w, h, rgba.into_raw()); + let mut md = ImageMetaData::new(); + md.load_image(&path.to_string_lossy()); + Ok(LoadedImage { path, w, h, handle, md }) + }; + cosmic::task::future(async move { Message::ImageLoaded(Box::new(task.await)) }) + } + + fn picker_entries(&self, p: &Picker) -> Vec<(String, usize)> { + let mut idxs: Vec<usize> = match p.mode { + PickerMode::Viewpoint => (0..self.pan.hills.len()).collect(), + PickerMode::FindPeak { .. } => self.pan.close.clone(), + }; + let q = p.query.to_uppercase(); + let mut entries: Vec<(String, usize)> = idxs + .drain(..) + .filter(|&i| { + let h = &self.pan.hills[i]; + h.flags & (FLAG_DUPLIC | FLAG_TRACK_POINT) == 0 + && (q.is_empty() || h.name.to_uppercase().contains(&q)) + }) + .map(|i| (format!("{} ({} m)", self.pan.hills[i].name, self.pan.hills[i].height as i64), i)) + .collect(); + entries.sort_by(|a, b| a.0.cmp(&b.0)); + entries.truncate(300); + entries + } + + fn update_vp(&mut self, msg: VpMsg) -> Task<Message> { + match msg { + VpMsg::ZoomAt { factor, img } => { + let new_zoom = (self.zoom * factor).clamp(0.02, 16.0); + let z = self.zoom as f64; + let nz = new_zoom as f64; + self.panv = Vector::new( + self.panv.x + (img.x as f64 * (z - nz)) as f32, + self.panv.y + (img.y as f64 * (z - nz)) as f32, + ); + self.zoom = new_zoom; + self.fit = false; + } + VpMsg::PanBy { delta } => { + self.panv = Vector::new(self.panv.x + delta.x, self.panv.y + delta.y); + self.fit = false; + } + VpMsg::PanTo { pan } => { + self.panv = pan; + self.fit = false; + } + VpMsg::MoveMarker { idx, img } => { + if idx < self.pan.hills.len() { + self.pan.hills[idx].x = img.x as f64; + self.pan.hills[idx].y = img.y as f64; + self.bump(); + } + } + VpMsg::DragEnd { had_marker } => { + if had_marker { + self.refit(); + } + } + VpMsg::ToggleKnown(hit) => { + if let Some(i) = hit { + if let Some(pos) = self.known.iter().position(|&k| k == i) { + self.known.remove(pos); + } else { + self.known.push(i); + } + self.refit(); + } + } + VpMsg::ContextAt { img, hit } => { + self.context = Some(ContextState { img_x: img.x as f64, img_y: img.y as f64, hit }); + } + VpMsg::Hover(h) => { + if h != self.hover { + self.hover = h; + self.hover_info = h.map(|i| { + let hm = &self.pan.hills[i]; + format!( + "{} ({} m), distance {:.2} km", + hm.name, + hm.height as i64, + self.pan.get_real_distance(i) / 1000.0 + ) + }); + } + } + } + Task::none() + } +} + +impl Application for Gipfel { + type Executor = cosmic::executor::Default; + type Flags = Flags; + type Message = Message; + const APP_ID: &'static str = "org.gipfel.App"; + + fn core(&self) -> &Core { + &self.core + } + + fn core_mut(&mut self) -> &mut Core { + &mut self.core + } + + fn init(core: Core, flags: Flags) -> (Self, Task<Message>) { + let mut pan = Panorama::new(); + let mut status = String::from("no hill database loaded"); + for c in data_candidates(flags.data.as_deref()) { + if c.is_file() { + match pan.load_data(&c.to_string_lossy()) { + Ok(()) => { + status = format!("{} hills loaded", pan.hills.len()); + break; + } + Err(e) => status = format!("cannot read {}: {e}", c.display()), + } + } + } + + let mut app = Gipfel { + core, + pan, + project: None, + known: Vec::new(), + track: Vec::new(), + zoom: 1.0, + fit: true, + panv: Vector::ZERO, + hover: None, + ctrl: false, + version: 0, + win_w: 1280.0, + win_h: 800.0, + vp_w: 0.0, + vp_h: 0.0, + lat_in: String::new(), + lon_in: String::new(), + height_in: String::new(), + k0_in: String::new(), + k1_in: String::new(), + x0_in: String::new(), + profile_in: String::new(), + profiles: gipfel_core::profile::list().into_iter().map(|(n, _)| n).collect(), + profile_overwrite: false, + track_width: 200.0, + show_hidden: false, + label_font: viewport::DEFAULT_LABEL_FONT, + fit_distortion: false, + compass_style: CompassStyle::Aligned, + status, + picker: None, + context: None, + hover_info: None, + about: false, + }; + app.sync_inputs(); + + let task = match flags.image { + Some(p) => app.open_image(p), + None => Task::none(), + }; + (app, task) + } + + fn header_start(&self) -> Vec<Element<'_, Message>> { + let mut kb = std::collections::HashMap::new(); + use cosmic::widget::menu::key_bind::{KeyBind, Modifier}; + let kb = &mut kb; + for (ch, action) in [ + ("o", Action::Open), + ("s", Action::Save), + ("d", Action::Dump), + ("t", Action::LoadTrack), + ("v", Action::ChooseViewpoint), + ("f", Action::Fit), + ("q", Action::Quit), + ] { + kb.insert( + KeyBind { modifiers: vec![Modifier::Ctrl], key: Key::Character(ch.into()) }, + action, + ); + } + + let file = menu::Tree::with_children( + cosmic::widget::RcElementWrapper::new(menu::root("File").into()), + menu::items( + kb, + vec![ + menu::Item::Button("Open Image…", None, Action::Open), + menu::Item::Button("Save Image…", None, Action::Save), + menu::Item::Button("Screen Dump…", None, Action::Dump), + menu::Item::Button("Stitch Cylindrical TIFF…", None, Action::Stitch), + menu::Item::Divider, + menu::Item::Button("Load GPX Track…", None, Action::LoadTrack), + menu::Item::Divider, + menu::Item::Button("Quit", None, Action::Quit), + ], + ), + ); + let view = menu::Tree::with_children( + cosmic::widget::RcElementWrapper::new(menu::root("View").into()), + menu::items( + kb, + vec![ + menu::Item::Button("Choose Viewpoint…", None, Action::ChooseViewpoint), + menu::Item::CheckBox( + "Show Hidden", + None, + self.show_hidden, + Action::ShowHidden, + ), + menu::Item::Divider, + menu::Item::Button("Zoom In", None, Action::ZoomIn), + menu::Item::Button("Zoom Out", None, Action::ZoomOut), + menu::Item::Button("Fit", None, Action::Fit), + menu::Item::Divider, + menu::Item::CheckBox( + "Compass strip", + None, + self.compass_style == CompassStyle::Aligned, + Action::CompassAligned, + ), + menu::Item::CheckBox( + "Compass ruler", + None, + self.compass_style == CompassStyle::Ruler, + Action::CompassRuler, + ), + menu::Item::CheckBox( + "Compass off", + None, + self.compass_style == CompassStyle::Off, + Action::CompassOff, + ), + ], + ), + ); + let projection = menu::Tree::with_children( + cosmic::widget::RcElementWrapper::new(menu::root("Projection").into()), + menu::items( + kb, + vec![ + menu::Item::CheckBox( + "Normal", + None, + self.pan.projection_type == ProjectionType::Rectilinear, + Action::ProjNormal, + ), + menu::Item::CheckBox( + "Panoramic", + None, + self.pan.projection_type == ProjectionType::Cylindrical, + Action::ProjPanoramic, + ), + ], + ), + ); + let help = menu::Tree::with_children( + cosmic::widget::RcElementWrapper::new(menu::root("Help").into()), + menu::items(kb, vec![menu::Item::Button("About gipfel", None, Action::About)]), + ); + + vec![ + widget::nav_bar_toggle() + .active(self.core().nav_bar_active()) + .on_toggle(Message::ToggleNav) + .into(), + menu::bar(vec![file, view, projection, help]) + .item_height(menu::ItemHeight::Dynamic(40)) + .item_width(menu::ItemWidth::Uniform(260)) + .spacing(4.0) + .into(), + ] + } + + fn subscription(&self) -> Subscription<Message> { + event::listen_with(|event, _status, _id| match event { + Event::Keyboard(keyboard::Event::ModifiersChanged(m)) => { + Some(Message::Event(Event::Keyboard(keyboard::Event::ModifiersChanged(m)))) + } + Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { + if modifiers.control() { + if let Key::Character(c) = &key { + match c.as_str() { + "o" | "O" => return Some(Message::OpenImage), + "s" | "S" => return Some(Message::SaveImage), + "d" | "D" => return Some(Message::DumpImage), + "t" | "T" => return Some(Message::LoadTrack), + "v" | "V" => return Some(Message::ChooseViewpoint), + "f" | "F" => return Some(Message::Fit), + "q" | "Q" => return Some(Message::Quit), + _ => {} + } + } + } + match key { + Key::Named(cosmic::iced::keyboard::key::Named::Escape) => { + Some(Message::PickerClose) + } + _ => None, + } + } + Event::Window(WinEvent::FileDropped(paths)) => Some(Message::FileDropped(paths)), + Event::Window(WinEvent::Resized(size)) => { + Some(Message::WinResized(size.width as f64, size.height as f64)) + } + _ => None, + }) + } + + fn update(&mut self, message: Message) -> Task<Message> { + use cosmic::dialog::file_chooser::FileFilter; + + match message { + Message::Vp(m) => return self.update_vp(m), + Message::Event(Event::Keyboard(keyboard::Event::ModifiersChanged(m))) => { + self.ctrl = m.control(); + } + Message::Event(_) => {} + Message::ToggleNav => { + self.core_mut().nav_bar_toggle(); + self.remeasure(); + } + Message::WinResized(w, h) => { + self.win_w = w; + self.win_h = h; + self.remeasure(); + } + Message::FileDropped(paths) => { + for p in paths { + let ext = p.extension().and_then(|e| e.to_str()).unwrap_or(""); + if matches!(ext.to_ascii_lowercase().as_str(), "jpg" | "jpeg" | "png") { + return self.open_image(p); + } else if ext.eq_ignore_ascii_case("csv") || ext.eq_ignore_ascii_case("dat") { + // treat as track file + let task = async move { + hill::load(&p.to_string_lossy()) + .map(|mut hs| { + for h in hs.iter_mut() { + h.flags |= FLAG_TRACK_POINT; + } + hs + }) + .map_err(|e| e.to_string()) + }; + return cosmic::task::future(async move { + Message::TrackLoaded(Box::new(task.await)) + }); + } + } + } + Message::OpenImage => { + return cosmic::task::future(async move { + let filter = + FileFilter::new("Images").glob("*.jpg").glob("*.jpeg").glob("*.png"); + Message::OpenPath(pick_open("Open Image", filter).await) + }); + } + Message::OpenPath(Some(p)) => return self.open_image(p), + Message::OpenPath(None) => {} + Message::ImageLoaded(res) => match *res { + Ok(li) => { + let title = li.path.file_name().map(|s| s.to_string_lossy().into_owned()).unwrap_or_default(); + self.set_header_title(title); + let (w, h) = (li.w, li.h); + self.project = Some(Project { + path: li.path, + handle: li.handle, + w, + h, + md: li.md.clone(), + }); + let md = li.md; + let have = gipfel_core::metadata::apply_image_metadata(&mut self.pan, &md, w, h); + self.known.clear(); + self.track.clear(); + self.sync_inputs(); + self.fit = true; + self.remeasure(); + self.zoom = self.fit_zoom(); + self.panv = Vector::ZERO; + self.bump(); + // prefill the profile name the auto-match used (C + // get_distortion_profile_name) and refresh the store list + let auto_profile = gipfel_core::metadata::distortion_profile_name(&md); + self.profile_in = auto_profile.clone().unwrap_or_default(); + self.profile_overwrite = false; + self.refresh_profiles(); + self.status = if have { + format!("loaded (gipfel info found)") + } else { + "loaded (no gipfel info: set viewpoint)".into() + }; + if md.k0.is_nan() { + if let Some(name) = + auto_profile.filter(|n| self.profiles.iter().any(|p| p == n)) + { + self.status = format!("{} - distortion profile '{name}'", self.status); + } + } + // the first frame after the image appears can be drawn + // before the texture is fully uploaded; a late nudge + // guarantees the photo shows without user interaction + return Task::perform( + async { tokio::time::sleep(std::time::Duration::from_millis(150)).await }, + |_| cosmic::Action::App(Message::Redraw), + ); + } + Err(e) => self.status = e, + }, + Message::SaveImage => { + let Some(p) = &self.project else { + self.status = "no image loaded".into(); + return Task::none(); + }; + let _ = p; + return cosmic::task::future(async move { + let filter = FileFilter::new("JPEG").glob("*.jpg"); + Message::SavePath(pick_save("Save Image", filter).await) + }); + } + Message::SavePath(out) => { + let (Some(out), Some(p)) = (out, &self.project) else { return Task::none() }; + let md = self.md_from_state(); + let src = p.path.clone(); + return cosmic::task::future(async move { + match md.save_image(&src.to_string_lossy(), &out.to_string_lossy()) { + Ok(()) => Message::ImageSaved(Some(out)), + Err(e) => { + let _ = e; + Message::ImageSaved(None) + } + } + }); + } + Message::ImageSaved(Some(p)) => { + self.status = format!("saved {}", p.display()); + } + Message::ImageSaved(None) => {} + Message::DumpImage => { + let Some(p) = &self.project else { + self.status = "no image loaded".into(); + return Task::none(); + }; + let path = p.path.clone(); + // the dump is at image resolution; screen-pixel overlay sizes + // must be divided by the current zoom to look like the screen + let zoom = self.zoom.max(0.02) as f64; + let markers: Vec<dump::DumpMarker> = self + .markers() + .iter() + .map(|m| dump::DumpMarker { + x: m.x, + y: m.y, + name: m.name.to_string(), + known: m.known, + hidden: m.hidden, + }) + .collect(); + let track: Vec<dump::DumpTrackPt> = self + .track_pts() + .iter() + .map(|t| dump::DumpTrackPt { x: t.x, y: t.y, hidden: t.hidden, width: t.width }) + .collect(); + let font_size = self.label_font; + let font_family = cosmic::config::interface_font().family; + return cosmic::task::future(async move { + let res: Result<PathBuf, String> = async { + let filter = FileFilter::new("Images").glob("*.png").glob("*.jpg"); + let out = + pick_save("Screen Dump", filter).await.ok_or_else(|| "cancelled".to_string())?; + let img = image::open(&path) + .map(|i| i.to_rgba8()) + .map_err(|e| format!("{e}"))?; + dump::render(&img, &markers, &track, &out, font_size, 1.0 / zoom, &font_family)?; + Ok(out) + } + .await; + Message::ImageDumped(Box::new(res)) + }); + } + Message::ImageDumped(res) => { + self.status = match *res { + Ok(p) => format!("dumped {}", p.display()), + Err(e) => e, + }; + } + Message::StitchTiff => { + let Some(p) = &self.project else { + self.status = "no image loaded".into(); + return Task::none(); + }; + // cylindrical output spanning the lens' actual field of view + let edge = self.view_angle_limit(); // half-angle, radians + if !(edge > 0.0 && edge < 1.5) { + self.status = "cannot determine lens field of view".into(); + return Task::none(); + } + let center = self.pan.get_center_angle(); + let (from, to) = (center - edge.to_degrees(), center + edge.to_degrees()); + // output scale = the source's focal scale (pixels per radian + // at image center): the cylindrical image is 1:1 in the middle + // and slightly downsampled towards the edges (where the + // rectilinear source has increasing angular stretch). Using + // the source width instead would upsample ~5-10% and blur. + let radius = self.pan.get_scale(); + let w_out = (2.0 * edge * radius).round_ties_even().max(16.0) as u32; + // cover the source's vertical extent (may be off-center + // with pitch); output rows are symmetric about elevation 0 + let e_top = elev_at_img_y(&self.pan, -(p.h as f64 / 2.0)); + let e_bot = elev_at_img_y(&self.pan, p.h as f64 / 2.0); + let e_max = e_top.abs().max(e_bot.abs()).min(85f64.to_radians()); + let h_out = (2.0 * radius * e_max.tan()).round_ties_even().max(16.0) as u32; + let path = p.path.clone(); + let pan = self.pan.params_snapshot(); + return cosmic::task::future(async move { + let res: Result<PathBuf, String> = async { + let filter = FileFilter::new("TIFF images").glob("*.tif").glob("*.tiff"); + let out = pick_save("Stitch Cylindrical TIFF", filter) + .await + .ok_or_else(|| "cancelled".to_string())?; + let out_for_task = out.clone(); + tokio::task::spawn_blocking(move || -> Result<(), String> { + let mut src = stitch::StitchSource::load(&path.to_string_lossy())?; + // use the live (possibly adjusted) parameters + src.pan = pan; + let img = stitch::resample( + &[src], + stitch::ScanMode::Bicubic, + w_out, + h_out, + from, + to, + ); + stitch::write_tiff(&out_for_task.to_string_lossy(), &img, true) + }) + .await + .map_err(|e| e.to_string())??; + Ok(out) + } + .await; + Message::StitchDone(Box::new(res)) + }); + } + Message::StitchDone(res) => { + self.status = match *res { + Ok(p) => format!("stitched {}", p.display()), + Err(e) => e, + }; + } + Message::LoadTrack => { + return cosmic::task::future(async move { + let res: Result<Vec<Hill>, String> = async { + let filter = FileFilter::new("Track files") + .glob("*.gpx") + .glob("*.csv") + .glob("*.dat"); + let path = pick_open("Load GPX Track", filter) + .await + .ok_or_else(|| "cancelled".to_string())?; + let hs = load_track_file(&path).map_err(|e| e.to_string())?; + Ok(hs) + } + .await; + Message::TrackLoaded(Box::new(res)) + }); + } + Message::TrackLoaded(res) => match *res { + Ok(hills) => { + // remap known hills by name across the index shift caused by + // removing the old track + let names: Vec<String> = self.known.iter().map(|&i| self.pan.hills[i].name.clone()).collect(); + self.pan.remove_hills(FLAG_TRACK_POINT); + let n = hills.len(); + self.pan.add_hills(&hills); + let base = self.pan.hills.len() - n; + self.track = (base..base + n).collect(); + self.known = names + .iter() + .filter_map(|n| self.pan.get_pos(n)) + .collect(); + self.status = format!("track: {n} points"); + self.bump(); + } + Err(e) => self.status = e, + }, + Message::ChooseViewpoint => { + self.picker = Some(Picker { + title: "Choose Viewpoint".into(), + query: String::new(), + mode: PickerMode::Viewpoint, + }); + } + Message::ContextFindPeak(x, y) => { + self.context = None; + self.picker = Some(Picker { + title: "Find Peak".into(), + query: String::new(), + mode: PickerMode::FindPeak { img_x: x, img_y: y }, + }); + } + Message::PickerQuery(q) => { + if let Some(p) = &mut self.picker { + p.query = q; + } + } + Message::PickerPick(i) => { + if let Some(p) = self.picker.take() { + match p.mode { + PickerMode::Viewpoint => { + self.pan.set_viewpoint(Some(i)); + self.sync_inputs(); + self.bump(); + self.status = format!("viewpoint: {}", self.pan.hills[i].name); + } + PickerMode::FindPeak { img_x, img_y } => { + if !self.known.contains(&i) { + self.known.push(i); + } + self.pan.hills[i].flags |= FLAG_VISIBLE; + if !self.pan.visible.contains(&i) { + self.pan.visible.push(i); + } + self.pan.hills[i].x = img_x; + self.pan.hills[i].y = img_y; + self.refit(); + } + } + } + } + Message::PickerClose => { + self.picker = None; + self.about = false; + self.context = None; + } + Message::ContextToggleHidden(i) => { + self.context = None; + self.pan.hills[i].flags ^= FLAG_HIDDEN; + self.bump(); + } + Message::Surface(action) => { + return cosmic::task::message(cosmic::Action::Surface(action)); + } + Message::Redraw => {} + Message::Center(a) => { + self.pan.set_center_angle(a); + self.bump(); + } + Message::CompassStyle(s) => { + self.compass_style = s; + self.remeasure(); + self.bump(); + } + Message::Pitch(a) => { + self.pan.set_pitch_angle(a); + self.bump(); + } + Message::Roll(a) => { + self.pan.set_roll_angle(a); + self.bump(); + } + Message::LabelFont(s) => { + self.label_font = s; + self.bump(); + } + Message::Focal(f) => { + self.set_focal_35mm(f); + self.bump(); + } + Message::Visibility(v) => { + // the slider shows more visibility to the right; the core + // stores a height/distance threshold (higher = fewer hills) + self.pan.set_height_dist_ratio(0.11 - v); + self.bump(); + } + Message::TrackWidth(w) => self.track_width = w, + Message::Lat(s) => self.lat_in = s, + Message::Lon(s) => self.lon_in = s, + Message::Height(s) => self.height_in = s, + Message::ViewApply => { + if let Ok(v) = self.lat_in.parse() { + self.pan.set_view_lat(v); + } + if let Ok(v) = self.lon_in.parse() { + self.pan.set_view_long(v); + } + if let Ok(v) = self.height_in.parse() { + self.pan.set_view_height(v); + } + self.bump(); + } + Message::K0(s) => self.k0_in = s, + Message::K1(s) => self.k1_in = s, + Message::X0(s) => self.x0_in = s, + Message::DistortionApply => { + let k0 = self.k0_in.parse().unwrap_or(0.0); + let k1 = self.k1_in.parse().unwrap_or(0.0); + let x0 = self.x0_in.parse().unwrap_or(0.0); + self.pan.set_distortion_params(k0, k1, x0); + self.bump(); + } + Message::ProfileName(s) => { + self.profile_in = s; + self.profile_overwrite = false; + } + Message::ProfileSave => { + let name = self.profile_in.trim().to_string(); + if name.is_empty() { + self.status = "enter a profile name".into(); + } else { + let p = gipfel_core::profile::Profile { + k0: self.pan.parms.k0, + k1: self.pan.parms.k1, + x0: self.pan.parms.x0, + }; + let exists = self.profiles.iter().any(|n| *n == name); + if exists && !self.profile_overwrite { + self.profile_overwrite = true; + self.status = + format!("profile '{name}' exists - press Save again to overwrite"); + } else { + match gipfel_core::profile::save(&name, p, true) { + Ok(()) => { + self.status = format!("saved profile '{name}'"); + self.profile_overwrite = false; + self.refresh_profiles(); + } + Err(e) => self.status = format!("save failed: {e}"), + } + } + } + } + Message::ProfileLoad => { + let name = self.profile_in.trim().to_string(); + if !name.is_empty() { + self.apply_profile(&name); + } + } + Message::ProfileSelect(i) => { + if let Some(name) = self.profiles.get(i).cloned() { + self.profile_in = name.clone(); + self.apply_profile(&name); + } + } + Message::Projection(t) => { + self.pan.set_projection(t); + self.bump(); + } + Message::ToggleShowHidden => { + self.show_hidden = !self.show_hidden; + self.bump(); + } + Message::FitDistortion(v) => { + self.fit_distortion = v; + // fitting a newly unlocked calibration takes effect at once + if v && self.known.len() >= 4 { + self.refit(); + } + } + Message::Fit => { + self.fit = true; + self.zoom = self.fit_zoom(); + self.panv = Vector::ZERO; + } + Message::ZoomIn => { + return self.update_vp(VpMsg::ZoomAt { factor: 1.25, img: cosmic::iced::Point::ORIGIN }); + } + Message::ZoomOut => { + return self.update_vp(VpMsg::ZoomAt { factor: 0.8, img: cosmic::iced::Point::ORIGIN }); + } + Message::About(b) => self.about = b, + Message::Quit => { + if let Some(id) = self.core.main_window_id() { + return cosmic::iced::window::close(id); + } + } + } + Task::none() + } + + /// The settings panel lives in the framework-managed left nav slot + /// (standard padding, condensed-mode handling, collapse state). + fn nav_bar(&self) -> Option<Element<'_, cosmic::Action<Message>>> { + if !self.core().nav_bar_active() { + return None; + } + let close: Element<Message> = widget::button::icon( + widget::icon::from_name("pan-start-symbolic").size(16), + ) + .on_press(Message::ToggleNav) + .into(); + let panel: Element<Message> = widget::container( + widget::column::with_children([ + widget::row::with_children([ + widget::text::heading("gipfel").into(), + widget::container(close) + .width(Length::Fill) + .align_x(cosmic::iced::alignment::Horizontal::Right) + .into(), + ]) + .align_y(cosmic::iced::alignment::Vertical::Center) + .spacing(8) + .into(), + widget::scrollable(self.sidebar()).into(), + ]) + .spacing(10), + ) + .padding([10, 12]) + .width(Length::Fixed(340.0)) + .height(Length::Fill) + .class(cosmic::theme::Container::Secondary) + .into(); + Some(panel.map(cosmic::Action::App)) + } + + fn view(&self) -> Element<'_, Message> { + let content: Element<Message> = match &self.project { + Some(p) => { + let vp = ViewportWidget::<Message> { + handle: p.handle.clone(), + img_w: p.w as f32, + img_h: p.h as f32, + zoom: self.zoom, + pan: self.panv, + markers: self.markers(), + track: self.track_pts(), + hover: self.hover, + ctrl: self.ctrl, + font_size: self.label_font, + version: self.version, + _msg: PhantomData, + }; + let vp: Element<Message> = Element::new(vp); + // standard popover context menu (like cosmic-files): opens in + // its own surface, never shifts the main content + let vp: Element<Message> = + cosmic::widget::context_menu(vp, self.context_menu_tree()) + .window_id( + self.core + .main_window_id() + .unwrap_or(cosmic::iced::window::Id::RESERVED), + ) + .on_surface_action(Message::Surface) + .into(); + if self.compass_style == CompassStyle::Off { + vp + } else { + let fov = match self.pan.projection_type { + ProjectionType::Rectilinear => ( + 2.0 * ((p.w as f64 * 0.5) / self.pan.parms.scale).atan() + ) + .to_degrees(), + ProjectionType::Cylindrical => { + (p.w as f64 / self.pan.parms.scale).to_degrees() + } + }; + let strip: Element<Message> = Element::new(Compass { + style: self.compass_style, + center_deg: self.pan.get_center_angle(), + zoom: self.zoom, + pan_x: self.panv.x, + parms: self.pan.parms, + ptype: self.pan.projection_type, + fov_deg: fov, + max_view_angle: self.pan.max_view_angle, + on_change: Rc::new(Message::Center), + }); + widget::column::with_children([vp, strip]).into() + } + } + None => widget::container( + widget::column::with_children([ + widget::text::title2("gipfel").into(), + widget::text("Open a JPEG to start (File ▸ Open Image…, Ctrl+O).").into(), + widget::button::suggested("Open Image…").on_press(Message::OpenImage).into(), + ]) + .spacing(12) + .align_x(cosmic::iced::alignment::Horizontal::Center), + ) + .into(), + }; + + let main: Element<Message> = content; + + let status_text = self.hover_info.as_deref().unwrap_or(&self.status); + let status: Element<Message> = widget::container(widget::text::body(status_text.to_string())) + .padding([4, 10]) + .width(Length::Fill) + .style(|_: &Theme| widget::container::Style { + background: Some(cosmic::iced::Background::Color(Color::from_rgba(0.0, 0.0, 0.0, 0.15))), + ..Default::default() + }) + .into(); + + let body: Element<Message> = widget::column::with_children([main, status]).into(); + + if let Some(picker) = &self.picker { + return self.with_dialog(body, self.view_picker(picker)); + } + if self.about { + return self.with_dialog(body, self.view_about()); + } + body + } +} + +// helpers split out to keep view() readable + +/// settings row with the label above a full-width, easy-grip slider +fn slider_item( + label: &str, + value: String, + slider: impl Into<Element<'static, Message>>, +) -> Element<'static, Message> { + let header: Element<'static, Message> = widget::row::with_children([ + widget::text::body(label.to_string()).into(), + widget::container(widget::text::body(value)) + .width(Length::Fill) + .align_x(cosmic::iced::alignment::Horizontal::Right) + .into(), + ]) + .into(); + let body: Element<'static, Message> = slider.into(); + widget::column::with_children([header, body]) + .spacing(2) + .into() +} + +/// Async file chooser: returns the chosen path, or None if cancelled. +async fn pick_open( + title: &str, + filter: cosmic::dialog::file_chooser::FileFilter, +) -> Option<PathBuf> { + cosmic::dialog::file_chooser::open::Dialog::new() + .title(title.to_string()) + .filter(filter) + .open_file() + .await + .ok()? + .url() + .to_file_path() + .ok() +} + +/// Async save dialog: returns the target path, or None if cancelled. +async fn pick_save( + title: &str, + filter: cosmic::dialog::file_chooser::FileFilter, +) -> Option<PathBuf> { + cosmic::dialog::file_chooser::save::Dialog::new() + .title(title.to_string()) + .filter(filter) + .save_file() + .await + .ok()? + .url() + .and_then(|u| u.to_file_path().ok()) +} + +impl Gipfel { + fn with_dialog<'a>(&self, body: Element<'a, Message>, dialog: Element<'a, Message>) -> Element<'a, Message> { + widget::column::with_children([ + widget::container(dialog).width(Length::Fill).into(), + body, + ]) + .into() + } + + fn view_picker<'a>(&self, p: &'a Picker) -> Element<'a, Message> { + let entries = self.picker_entries(p); + let mut list = widget::column::with_capacity(entries.len()); + let mut empty = true; + for (label, idx) in entries { + empty = false; + list = list.push( + widget::button::text(label) + .on_press(Message::PickerPick(idx)) + .width(Length::Fill), + ); + } + if empty { + list = list.push(widget::text("no matches")); + } + + widget::dialog() + .title(p.title.clone()) + .body("Type to search, click to select.") + .control( + widget::text_input("search…", &p.query) + .on_input(Message::PickerQuery), + ) + .control({ + let el: Element<Message> = list.into(); + widget::scrollable(el).height(Length::Fixed(320.0)) + }) + .secondary_action(widget::button::text("Cancel").on_press(Message::PickerClose)) + .is_overlay(false) + .into() + } + + fn context_menu_tree(&self) -> Option<Vec<menu::Tree<Message>>> { + let ctx = self.context.as_ref()?; + let mut items: Vec<menu::Item<CtxAction, String>> = Vec::new(); + // placing a marker only makes sense on empty image space, not on an + // existing peak marker + if ctx.hit.is_none() { + items.push(menu::Item::Button( + "Find Peak…".to_string(), + None, + CtxAction::FindPeak(ctx.img_x, ctx.img_y), + )); + } + if let Some(i) = ctx.hit { + let h = &self.pan.hills[i]; + items.push(menu::Item::Button( + format!( + "{} {} ({} m)", + if h.flags & FLAG_HIDDEN != 0 { "Unhide" } else { "Hide" }, + h.name, + h.height as i64 + ), + None, + CtxAction::ToggleHidden(i), + )); + } + if items.is_empty() { + return None; + } + Some(menu::items(&std::collections::HashMap::new(), items)) + } + + fn view_about<'a>(&self) -> Element<'a, Message> { + widget::dialog() + .title("About gipfel") + .body("gipfel 0.5.0 (COSMIC preview)") + .control(widget::text("Annotate mountain panoramas.")) + .control(widget::text("Port of gipfel by Johannes Hofmann - mathematical models unchanged.")) .primary_action(widget::button::suggested("Close").on_press(Message::About(false))) + .is_overlay(false) + .into() + } + + fn sidebar(&self) -> Element<'_, Message> { + let has_img = self.project.is_some(); + let focal = self.focal_35mm().clamp(1.0, 300.0); + let pitch = self.pan.get_pitch_angle().clamp(-40.0, 20.0); + let roll = self.pan.get_roll_angle().clamp(-10.0, 10.0); + let vis = self.pan.height_dist_ratio.clamp(0.01, 0.1); + let vis_ui = 0.11 - vis; // slider: right = more hills visible + let label_font = self.label_font; + let center = self.pan.get_center_angle().rem_euclid(360.0); + + let vp_name = self + .pan + .view_name + .clone() + .unwrap_or_else(|| "manual".to_string()); + + let sec_viewpoint = widget::settings::section() + .title("Viewpoint") + .add(widget::settings::item( + "Position", + widget::row::with_children([ + widget::text(vp_name).into(), + widget::button::text("Choose…").on_press(Message::ChooseViewpoint).into(), + ]) + .spacing(8), + )) + .add(widget::settings::item( + "Latitude", + widget::text_input("°", &self.lat_in) + .on_input(Message::Lat) + .on_submit(|_| Message::ViewApply), + )) + .add(widget::settings::item( + "Longitude", + widget::text_input("°", &self.lon_in) + .on_input(Message::Lon) + .on_submit(|_| Message::ViewApply), + )) + .add(widget::settings::item( + "Height (m)", + widget::text_input("m", &self.height_in) + .on_input(Message::Height) + .on_submit(|_| Message::ViewApply), + )); + + let sec_orientation = widget::settings::section() + .title("Orientation") + .add(widget::settings::item( + "Center (deg)", + widget::text(format!("{center:.1}°")), + )) + .add(slider_item( + "Focal length (35mm)", + format!("{focal:.1} mm"), + widget::slider(1.0..=300.0, focal, Message::Focal) + .step(0.01) + .height(30.0), + )) + .add(slider_item( + "Pitch (deg)", + format!("{pitch:.1}°"), + widget::slider(-40.0..=20.0, pitch, Message::Pitch) + .step(0.01) + .height(30.0), + )) + .add(slider_item( + "Roll (deg)", + format!("{roll:.1}°"), + widget::slider(-10.0..=10.0, roll, Message::Roll) + .step(0.01) + .height(30.0), + )); + + let sec_display = widget::settings::section() + .title("Display") + .add(slider_item( + "Visibility", + format!("{:.0}%", (vis_ui - 0.01) / 0.09 * 100.0), + widget::slider(0.01..=0.1, vis_ui, Message::Visibility) + .step(0.001) + .height(30.0), + )) + .add(slider_item( + "Track width", + format!("{:.0} m at 1 km", self.track_width), + widget::slider(1.0..=500.0, self.track_width, Message::TrackWidth) + .step(1.0) + .height(30.0), + )) + .add(slider_item( + "Label size", + format!("{label_font:.0} px"), + widget::slider(8.0f32..=28.0, label_font, Message::LabelFont) + .step(1.0) + .height(30.0), + )) + .add(widget::settings::item( + "Show hidden", + widget::toggler(self.show_hidden).on_toggle(|_| Message::ToggleShowHidden), + )) + .add(widget::row::with_children([ + widget::button::text("Zoom −").on_press(Message::ZoomOut).into(), + widget::button::text("Fit").on_press(Message::Fit).into(), + widget::button::text("Zoom +").on_press(Message::ZoomIn).into(), + widget::text(format!("{:.0}%", self.zoom * 100.0)).into(), + ]) + .spacing(8)); + + let sec_projection = widget::settings::section().title("Projection").add( + widget::row::with_children([ + widget::radio( + "Normal", + ProjectionType::Rectilinear, + Some(self.pan.projection_type), + Message::Projection, + ) + .into(), + widget::radio( + "Panoramic", + ProjectionType::Cylindrical, + Some(self.pan.projection_type), + Message::Projection, + ) + .into(), + ]) + .spacing(16), + ); + + // distortion fitting is locked unless enough markers are placed + let can_fit = self.known.len() >= 4; + let fit_cb = widget::checkbox(self.fit_distortion); + let fit_cb = if can_fit { + fit_cb.on_toggle(Message::FitDistortion) + } else { + fit_cb.on_toggle_maybe(None as Option<fn(bool) -> Message>) + }; + let mut fit_col = vec![fit_cb.into()]; + if !can_fit { + fit_col.push(widget::text("position 4 or more markers to enable").size(11).into()); + } + + // fixed-width label column: every control starts at the same x and + // therefore shares the same width (settings::item sizes labels to + // their text, which made the profile field narrower than k0/k1/x0) + let mut sec_distortion = widget::settings::section() + .title("Distortion") + .add(widget::settings::item_row(vec![ + widget::text("Fit distortion").width(110).into(), + widget::column::with_children(fit_col).spacing(2).into(), + ])) + .add(widget::settings::item_row(vec![ + widget::text("k0").width(110).into(), + widget::text_input("0", &self.k0_in) + .on_input(Message::K0) + .on_submit(|_| Message::DistortionApply) + .into(), + ])) + .add(widget::settings::item_row(vec![ + widget::text("k1").width(110).into(), + widget::text_input("0", &self.k1_in) + .on_input(Message::K1) + .on_submit(|_| Message::DistortionApply) + .into(), + ])) + .add(widget::settings::item_row(vec![ + widget::text("x0").width(110).into(), + widget::text_input("0", &self.x0_in) + .on_input(Message::X0) + .on_submit(|_| Message::DistortionApply) + .into(), + ])) + .add(widget::settings::item_row(vec![ + widget::text("profile").width(110).into(), + widget::text_input("Camera_Model_50.00_mm", &self.profile_in) + .on_input(Message::ProfileName) + .on_submit(|_| Message::ProfileSave) + .into(), + ])) + .add( + widget::row::with_children([ + widget::button::suggested("Save profile") + .on_press(Message::ProfileSave) + .into(), + widget::button::text("Load profile") + .on_press(Message::ProfileLoad) + .into(), + ]) + .spacing(8), + ); + if !self.profiles.is_empty() { + // stored profiles (shared with the C version); selecting one + // loads its parameters + let win = self + .core + .main_window_id() + .unwrap_or(cosmic::iced::window::Id::RESERVED); + sec_distortion = sec_distortion.add(widget::settings::item_row(vec![ + widget::text("stored").width(110).into(), + widget::dropdown::popup_dropdown( + self.profiles.clone(), + None, + Message::ProfileSelect, + win, + Message::Surface, + |m| m, + ) + .into(), + ])); + } + + let mut col = widget::column::with_children([ + sec_viewpoint.into(), + sec_orientation.into(), + sec_projection.into(), + sec_distortion.into(), + sec_display.into(), + ]) + .spacing(12) + .padding(12); + + if !has_img { + col = col.push(widget::text("Load an image to enable the controls.")); + } + col.into() + } +} + +fn main() -> cosmic::iced::Result { + let mut flags = Flags { data: None, image: None }; + let mut args = std::env::args().skip(1); + while let Some(a) = args.next() { + match a.as_str() { + "--data" => flags.data = args.next().map(PathBuf::from), + s if !s.starts_with('-') && flags.image.is_none() => flags.image = Some(PathBuf::from(s)), + _ => {} + } + } + cosmic::app::run::<Gipfel>( + cosmic::app::Settings::default().size(cosmic::iced::Size::new(1280.0, 800.0)), + flags, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn elev_inverse_matches_projection() { + let mut pan = Panorama::new(); + pan.set_projection(ProjectionType::Rectilinear); + pan.set_center_angle(0.0); + pan.set_pitch_angle(0.0); + pan.set_scale(200.0); + // undistorted rectilinear: y = scale * tan(pitch - elev) + for &y in &[-100.0, -30.0, 0.0, 45.0, 120.0] { + let e = elev_at_img_y(&pan, y); + let (_, y_back) = pan.project_point(0.0, e).unwrap(); + assert!((y_back - y).abs() < 0.01, "y={y} -> elev={e} -> {y_back}"); + } + // with pitch the band is asymmetric but still invertible + pan.set_pitch_angle(10.0); + let p = pan.parms.a_pitch; + let top = elev_at_img_y(&pan, -100.0); + let bot = elev_at_img_y(&pan, 100.0); + assert!(top > p && bot < p, "top={top} p={p} bot={bot}"); + let (_, y_back) = pan.project_point(0.0, top).unwrap(); + assert!((y_back + 100.0).abs() < 0.01); + } +} diff --git a/crates/gipfel-app/src/viewport.rs b/crates/gipfel-app/src/viewport.rs new file mode 100644 index 0000000..3001fc4 --- /dev/null +++ b/crates/gipfel-app/src/viewport.rs @@ -0,0 +1,659 @@ +//! The gipfel viewport: a custom widget that draws the photo, hill markers, +//! decluttered labels, GPS track, and handles all canvas interaction. +//! +//! Marker/label placement mirrors the original GipfelWidget::draw/set_labels +//! (image-space coordinates; the screen transform is applied at draw time). + +use std::rc::Rc; +use std::time::Instant; + +use cosmic::iced::advanced::image::{FilterMethod, Handle, Image as CoreImage}; +use cosmic::iced::advanced::renderer::Style as RendererStyle; +use cosmic::iced::advanced::text::{ + self as adv_text, paragraph::Plain, LineHeight, Renderer as _, +}; +use cosmic::iced::advanced::widget::{tree, Tree, Widget as WidgetTrait}; +use cosmic::iced::advanced::{Clipboard, Layout, Shell}; +use cosmic::iced::mouse::{self, Button, Cursor, Interaction, ScrollDelta}; +use cosmic::iced::widget::canvas::{Frame, LineCap, Path, Stroke}; +use cosmic::iced::advanced::graphics::geometry::Style as GeoStyle; +use cosmic::iced::{ + alignment, Color, Event, Length, Pixels, Point, Rectangle, Radians, Size, Vector, +}; + +use cosmic::Renderer as R; +use cosmic::Theme; + +pub const DEFAULT_LABEL_FONT: f32 = 12.0; +const CROSS_SIZE: f64 = 4.0; + +#[derive(Clone, Debug)] +pub struct Marker { + /// image-space coordinates, origin at image center (like C's m->x/m->y) + pub x: f64, + pub y: f64, + pub name: Rc<str>, + pub idx: usize, + pub known: bool, + pub hidden: bool, +} + +#[derive(Clone, Debug)] +pub struct TrackPt { + pub x: f64, + pub y: f64, + pub hidden: bool, + pub width: f64, +} + +#[derive(Debug, Clone)] +pub enum VpMsg { + /// zoom by `factor`, keeping image point `img` under the cursor + ZoomAt { factor: f32, img: Point }, + PanBy { delta: Vector }, + PanTo { pan: Vector }, + MoveMarker { idx: usize, img: Point }, + DragEnd { had_marker: bool }, + /// hill index of the toggled marker (None = clicked empty space) + ToggleKnown(Option<usize>), + ContextAt { img: Point, hit: Option<usize> }, + /// hill index of the hovered marker + Hover(Option<usize>), +} + +#[derive(Clone)] +struct LabelLayout { + version: u64, + font_size: f32, + zoom: f32, + entries: Vec<(f32, f32)>, +} + +#[derive(Default)] +struct DragState { + drag: Drag, + last_click: Option<(Instant, Point)>, + label_cache: std::cell::RefCell<Option<LabelLayout>>, + /// Paragraph primitives must outlive the frame: the renderer stores only a + /// Weak reference, so keep strong refs here until the next draw. + paragraphs: std::cell::RefCell<Vec<Plain<<R as adv_text::Renderer>::Paragraph>>>, +} + +#[derive(Default, Clone)] +enum Drag { + #[default] + None, + Marker(usize), + Pan { start: Point, pan0: Vector }, +} + +pub struct ViewportWidget<M: From<VpMsg> + Send + 'static> { + pub handle: Handle, + pub img_w: f32, + pub img_h: f32, + pub zoom: f32, + pub pan: Vector, + /// hill markers in visible order; label layout depends on this order + pub markers: Rc<Vec<Marker>>, + pub track: Rc<Vec<TrackPt>>, + pub hover: Option<usize>, + pub ctrl: bool, + /// label font size in pixels (UI-tunable) + pub font_size: f32, + /// bump whenever marker positions change to invalidate the label cache + pub version: u64, + pub _msg: std::marker::PhantomData<fn() -> M>, +} + +impl<M: From<VpMsg> + Send + 'static> Clone for ViewportWidget<M> { + fn clone(&self) -> Self { + ViewportWidget { + handle: self.handle.clone(), + img_w: self.img_w, + img_h: self.img_h, + zoom: self.zoom, + pan: self.pan, + markers: self.markers.clone(), + track: self.track.clone(), + hover: self.hover, + ctrl: self.ctrl, + font_size: self.font_size, + version: self.version, + _msg: std::marker::PhantomData, + } + } +} + +impl<M: From<VpMsg> + Send + 'static> ViewportWidget<M> { + /// line height: paragraph metrics (cosmic LineHeight default = 1.3x) plus + /// a 2px gap, so stacked labels never touch + fn line_h(&self) -> f32 { + self.font_size * 1.3 + 2.0 + } + + fn center(bounds: Rectangle, pan: Vector) -> Point { + Point::new( + bounds.x + bounds.width / 2.0 + pan.x, + bounds.y + bounds.height / 2.0 + pan.y, + ) + } + + fn to_img(&self, center: Point, p: Point) -> Point { + Point::new((p.x - center.x) / self.zoom, (p.y - center.y) / self.zoom) + } + + /// hit-test in image space; tolerance scales with zoom (~6 screen px). + /// Returns the *hill index* of the hit marker. + fn hit(&self, center: Point, p: Point) -> Option<usize> { + let img = self.to_img(center, p); + let tol = (6.0 / self.zoom).clamp(2.0, 20.0); + self.markers + .iter() + .find(|m| (m.x as f32 - img.x).abs() < tol && (m.y as f32 - img.y).abs() < tol) + .map(|m| m.idx) + } +} + +impl<M: From<VpMsg> + Send + 'static> WidgetTrait<M, Theme, R> for ViewportWidget<M> { + fn size(&self) -> Size<Length> { + Size::new(Length::Fill, Length::Fill) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::<DragState>() + } + + fn state(&self) -> tree::State { + tree::State::new(DragState::default()) + } + + fn layout( + &mut self, + _tree: &mut Tree, + _renderer: &R, + limits: &cosmic::iced::advanced::layout::Limits, + ) -> cosmic::iced::advanced::layout::Node { + cosmic::iced::advanced::layout::Node::new(limits.max()) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut R, + _theme: &Theme, + _style: &RendererStyle, + layout: Layout<'_>, + _cursor: Cursor, + _viewport: &Rectangle, + ) { + use cosmic::iced::advanced::image::Renderer as _; + use cosmic::iced::advanced::Renderer as _; + + let bounds = layout.bounds(); + let center = Self::center(bounds, self.pan); + let z = self.zoom; + + // 1. photo + let dest = Rectangle::new( + Point::new(center.x - self.img_w * z / 2.0, center.y - self.img_h * z / 2.0), + Size::new(self.img_w * z, self.img_h * z), + ); + renderer.draw_image( + CoreImage { + handle: self.handle.clone(), + filter_method: FilterMethod::Linear, + rotation: Radians(0.0), + border_radius: 0.0.into(), + opacity: 1.0, + snap: false, + }, + dest, + bounds, + ); + + let layout_entries = self.label_layout(tree, renderer); + + // The wgpu renderer draws geometry before images within one layer; + // record the overlay in a fresh layer so it lands on top of the photo. + renderer.start_layer(bounds); + self.draw_overlay(renderer, tree, bounds, center, &layout_entries); + renderer.end_layer(); + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: Cursor, + _renderer: &R, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, M>, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let center = Self::center(bounds, self.pan); + let state = tree.state.downcast_mut::<DragState>(); + + match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + let Some(p) = cursor.position_over(bounds) else { return }; + let (dx, dy) = match delta { + ScrollDelta::Pixels { x, y } => (*x, *y), + ScrollDelta::Lines { x, y } => (*x * 30.0, *y * 30.0), + }; + if self.ctrl { + shell.publish(M::from(VpMsg::ZoomAt { + factor: (1.0 + dy * 0.0015).clamp(0.5, 2.0), + img: self.to_img(center, p), + })); + } else { + shell.publish(M::from(VpMsg::PanBy { delta: Vector::new(-dx, -dy) })); + } + shell.request_redraw(); + } + Event::Mouse(mouse::Event::ButtonPressed(Button::Left)) => { + let Some(p) = cursor.position_over(bounds) else { return }; + let now = Instant::now(); + let dbl = state + .last_click + .is_some_and(|(t, q)| { + t.elapsed().as_millis() < 400 + && (q.x - p.x).abs() < 6.0 + && (q.y - p.y).abs() < 6.0 + }); + state.last_click = Some((now, p)); + + if dbl { + state.drag = Drag::None; + state.last_click = None; + shell.publish(M::from(VpMsg::ToggleKnown(self.hit(center, p)))); + return; + } + + if let Some(idx) = self.hit(center, p) { + if self.markers.iter().any(|m| m.idx == idx && m.known) { + state.drag = Drag::Marker(idx); + return; + } + } + state.drag = Drag::Pan { start: p, pan0: self.pan }; + } + Event::Mouse(mouse::Event::ButtonReleased(Button::Left)) => { + let had_marker = matches!(state.drag, Drag::Marker(_)); + state.drag = Drag::None; + shell.publish(M::from(VpMsg::DragEnd { had_marker })); + } + Event::Mouse(mouse::Event::ButtonPressed(Button::Middle)) => { + if let Some(p) = cursor.position_over(bounds) { + shell.publish(M::from(VpMsg::ToggleKnown(self.hit(center, p)))); + } + } + Event::Mouse(mouse::Event::ButtonPressed(Button::Right)) => { + if let Some(p) = cursor.position_over(bounds) { + shell.publish(M::from(VpMsg::ContextAt { + img: self.to_img(center, p), + hit: self.hit(center, p), + })); + } + } + Event::Mouse(mouse::Event::CursorMoved { .. }) => { + match state.drag.clone() { + Drag::Marker(idx) => { + if let Some(p) = cursor.position() { + shell.publish(M::from(VpMsg::MoveMarker { + idx, + img: self.to_img(center, p), + })); + shell.request_redraw(); + } + } + Drag::Pan { start, pan0 } => { + if let Some(p) = cursor.position() { + shell.publish(M::from(VpMsg::PanTo { + pan: pan0 + Vector::new(p.x - start.x, p.y - start.y), + })); + shell.request_redraw(); + } + } + Drag::None => { + match cursor.position_over(bounds) { + Some(p) => shell.publish(M::from(VpMsg::Hover(self.hit(center, p)))), + // cursor moved off the viewport (e.g. into the + // sidebar): clear any stale hover info + None => shell.publish(M::from(VpMsg::Hover(None))), + } + } + } + } + _ => {} + } + } + + fn mouse_interaction( + &self, + _tree: &Tree, + layout: Layout<'_>, + cursor: Cursor, + _viewport: &Rectangle, + _renderer: &R, + ) -> Interaction { + let center = Self::center(layout.bounds(), self.pan); + match cursor.position_over(layout.bounds()) { + Some(p) if self.hit(center, p).is_some() => Interaction::Pointer, + Some(_) => Interaction::Grab, + None => Interaction::None, + } + } +} + +impl<M: From<VpMsg> + Send + 'static> ViewportWidget<M> { + fn draw_overlay( + &self, + renderer: &mut R, + tree: &Tree, + bounds: Rectangle, + center: Point, + layout_entries: &[(f32, f32)], + ) { + use cosmic::iced::advanced::graphics::geometry::Renderer as _; + use cosmic::iced::advanced::text::Renderer as _; + + let z = self.zoom; + let to_screen = |p: Point| Point::new(center.x + p.x * z, center.y + p.y * z); + let layout_entries = layout_entries; + let mut frame = Frame::with_bounds(renderer, bounds); + + // 2. GPS track + for w in self.track.windows(2) { + let (a, b) = (&w[0], &w[1]); + let pa = to_screen(Point::new(a.x as f32, a.y as f32)); + let pb = to_screen(Point::new(b.x as f32, b.y as f32)); + let color = if a.hidden { + Color::from_rgb(0.2, 0.4, 1.0) + } else { + Color::from_rgb(1.0, 0.2, 0.2) + }; + let stroke = Stroke { + width: (a.width as f32 * z).max(1.0), + style: GeoStyle::Solid(color), + line_cap: LineCap::Round, + ..Default::default() + }; + let path = Path::new(|bld| { + bld.move_to(pa); + bld.line_to(pb); + }); + frame.stroke(&path, stroke); + } + + // 3. hill markers: crosses + leaders + // C's draw() skips markers outside the image (fabs(x) > w/2 || ...); + // must match label_layout's filter, otherwise off-image markers get + // drawn (in the letterbox area) without ever being laid out. + let in_img = |m: &Marker| { + m.x.abs() * 2.0 <= self.img_w as f64 && m.y.abs() * 2.0 <= self.img_h as f64 + }; + let yellow = Color::from_rgb(1.0, 0.9, 0.2); + for (i, m) in self.markers.iter().enumerate() { + if !in_img(m) { + continue; + } + let p = to_screen(Point::new(m.x as f32, m.y as f32)); + if !bounds.contains(p) { + continue; + } + let (lx, ly) = layout_entries[i]; + let d = CROSS_SIZE as f32; + let label_line = p.y + ly - self.line_h(); + let stroke = Stroke { + width: 1.0, + style: GeoStyle::Solid(yellow), + ..Default::default() + }; + let path = Path::new(|bld| { + bld.move_to(Point::new(p.x - d, p.y)); + bld.line_to(Point::new(p.x + d, p.y)); + bld.move_to(Point::new(p.x, p.y)); + bld.line_to(Point::new(p.x, label_line)); + bld.move_to(Point::new(p.x, label_line)); + bld.line_to(Point::new(p.x + lx, label_line)); + }); + frame.stroke(&path, stroke); + } + + // known-hill flags (pole + pennant + circle) + let flag_color = Color::from_rgb(1.0, 0.25, 0.25); + for m in self.markers.iter().filter(|m| m.known) { + if !in_img(m) { + continue; + } + let p = to_screen(Point::new(m.x as f32, m.y as f32)); + if !bounds.contains(p) { + continue; + } + let stroke = Stroke { + width: 1.5, + style: GeoStyle::Solid(flag_color), + ..Default::default() + }; + let pole = Path::new(|bld| { + bld.move_to(p); + bld.line_to(Point::new(p.x, p.y - 16.0)); + bld.circle(p, 3.0); + }); + let pennant = Path::new(|bld| { + bld.move_to(Point::new(p.x, p.y - 16.0)); + bld.line_to(Point::new(p.x + 10.0, p.y - 13.0)); + bld.line_to(Point::new(p.x, p.y - 10.0)); + bld.close(); + }); + frame.stroke(&pole, stroke.clone()); + frame.fill(&pennant, flag_color); + } + + renderer.draw_geometry(frame.into_geometry()); + + // 4. labels (screen space, constant font size) + let font = renderer.default_font(); + let size = Pixels(self.font_size); + // the renderer keeps only a Weak ref to each paragraph, so hold strong + // refs in the widget state until the next frame replaces them + let state = tree.state.downcast_ref::<DragState>(); + { + let mut paras = state.paragraphs.borrow_mut(); + paras.clear(); + paras.reserve(self.markers.len()); + for m in self.markers.iter() { + paras.push(self.make_paragraph(font, size, &m.name)); + } + } + let paras = state.paragraphs.borrow(); + + for (i, m) in self.markers.iter().enumerate() { + if !in_img(m) { + continue; + } + let p = to_screen(Point::new(m.x as f32, m.y as f32)); + if !bounds.contains(p) { + continue; + } + let (_, ly) = layout_entries[i]; + let pos = Point::new(p.x + 2.0, p.y + ly - self.line_h()); + let is_hover = self.hover == Some(m.idx); + + let para = ¶s[i]; + let mb = para.min_bounds(); + + let mut bg = Frame::with_bounds(renderer, bounds); + let rect = Rectangle::new( + Point::new(p.x + 1.0, pos.y - 1.0), + Size::new(mb.width + 2.0, mb.height + 1.0), + ); + let path = Path::new(|bld| bld.rectangle(rect.position(), rect.size())); + bg.fill( + &path, + if is_hover { + Color::from_rgba(1.0, 0.9, 0.2, 0.95) + } else { + Color::from_rgba(0.0, 0.0, 0.0, 0.55) + }, + ); + renderer.draw_geometry(bg.into_geometry()); + + let color = if is_hover { + Color::BLACK + } else if m.hidden { + Color::from_rgb(0.4, 0.6, 1.0) + } else { + Color::WHITE + }; + renderer.fill_paragraph( + para.raw(), + Point::new(p.x + 2.0, pos.y), + color, + bounds, + ); + } + } + + fn make_paragraph(&self, font: <R as adv_text::Renderer>::Font, size: Pixels, text: &str) -> Plain<<R as adv_text::Renderer>::Paragraph> { + Plain::<<R as adv_text::Renderer>::Paragraph>::new(adv_text::Text { + content: text.to_string(), + bounds: Size::new(600.0, 40.0), + size, + line_height: LineHeight::default(), + font, + align_x: alignment::Horizontal::Left.into(), + align_y: alignment::Vertical::Top.into(), + shaping: adv_text::Shaping::Basic, + wrapping: adv_text::Wrapping::None, + ellipsize: adv_text::Ellipsize::None, + }) + } + + /// C set_labels port: place labels above crosses, pushing colliding ones up. + /// Markers outside the image are skipped entirely (C: fabs(x) > w/2 filter), + /// so off-image peaks never push visible labels up. + fn label_layout(&self, tree: &Tree, renderer: &mut R) -> Vec<(f32, f32)> { + let state = tree.state.downcast_ref::<DragState>(); + if let Some(l) = state.label_cache.borrow().as_ref() { + if l.version == self.version + && l.font_size == self.font_size + && l.zoom == self.zoom + && l.entries.len() == self.markers.len() + { + return l.entries.clone(); + } + } + + let font = renderer.default_font(); + let size = Pixels(self.font_size); + let line_h = self.line_h(); + + let widths: Vec<f32> = self + .markers + .iter() + .map(|m| self.make_paragraph(font, size, &m.name).min_bounds().width) + .collect(); + + // C: fabs(m->x) > img->w()/2 || fabs(m->y) > img->h()/2 -> skip + // (marker coords are center-relative, like C's m->x/m->y) + let in_img: Vec<bool> = self + .markers + .iter() + .map(|m| m.x.abs() * 2.0 <= self.img_w as f64 && m.y.abs() * 2.0 <= self.img_h as f64) + .collect(); + + // Labels are drawn at constant size in *screen* space, so collisions + // depend on the on-screen distance between markers: run the layout on + // zoom-scaled coordinates (C had no zoom, image space == screen space). + let z = self.zoom; + let sx: Vec<f32> = self.markers.iter().map(|m| m.x as f32 * z).collect(); + let sy: Vec<f32> = self.markers.iter().map(|m| m.y as f32 * z).collect(); + + let entries = layout_label_y(&sx, &sy, &widths, &in_img, line_h); + *state.label_cache.borrow_mut() = Some(LabelLayout { + version: self.version, + font_size: self.font_size, + zoom: self.zoom, + entries: entries.clone(), + }); + entries + } +} + +/// Shared label collision-avoidance layout, used for both the on-screen +/// viewport and the screen dump so the dump shows exactly the same label +/// placement (plan.md M3: same code path). +/// +/// `xs`/`ys` are marker positions (center-relative) in one uniform pixel +/// space, `widths` the label widths and `line_h` the line height, all in +/// that same space. Markers must be ordered by x (the sweep breaks on the +/// first non-overlapping predecessor). Returns `(label_width + 1, y_offset)` +/// per marker; the label is drawn at `(marker.x + 2, marker.y + y_offset - +/// line_h)`. +pub fn layout_label_y( + xs: &[f32], + ys: &[f32], + widths: &[f32], + in_img: &[bool], + line_h: f32, +) -> Vec<(f32, f32)> { + let n = xs.len(); + let mut label_x = vec![0.0f32; n]; + let mut label_y = vec![0.0f32; n]; + let cross = CROSS_SIZE as f32; + let overlap = |a1: f32, l1: f32, a2: f32, l2: f32| a1 <= a2 + l2 && a1 + l1 >= a2; + + for i in 0..n { + if !in_img[i] { + continue; + } + label_x[i] = widths[i] + 1.0; + label_y[i] = 0.0; + + let mut colliding: Vec<usize> = Vec::new(); + for j in (0..i).rev() { + if !in_img[j] { + continue; + } + if overlap(xs[i], label_x[i], xs[j] - cross, label_x[j]) { + colliding.push(j); + } else { + break; + } + } + colliding.sort_by(|&a, &b| { + let ya = ys[a] + label_y[a]; + let yb = ys[b] + label_y[b]; + yb.total_cmp(&ya) + }); + // C does a single overwrite pass here, which can leave overlaps + // when a push moves the label into a band that was already + // checked. Iterate to a fixed point instead (converges quickly: + // each round only moves the label up, bounded by the stack). + for _round in 0..64 { + let mut pushed = false; + for &j in &colliding { + if overlap( + ys[i] + label_y[i] - line_h, + line_h, + ys[j] + label_y[j] - line_h, + line_h, + ) || overlap(ys[i] + label_y[i] - line_h, line_h, ys[j] - cross, 2.0 * cross) { + let ny = (ys[j] + label_y[j] - ys[i] - line_h - 2.0).round(); + if ny < label_y[i] { + label_y[i] = ny; + pushed = true; + } + } + } + if !pushed { + break; + } + } + } + + (0..n).map(|i| (label_x[i], label_y[i])).collect() +} diff --git a/crates/gipfel-cli/Cargo.toml b/crates/gipfel-cli/Cargo.toml new file mode 100644 index 0000000..9f0f96d --- /dev/null +++ b/crates/gipfel-cli/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "gipfel-cli" +version = "0.1.0" +edition = "2021" +description = "gipfel command line tools (export-position, export-hills)" +license = "GPL-2.0-or-later" + +[[bin]] +name = "gipfel-cli" +path = "src/main.rs" + +[dependencies] +gipfel-core = { path = "../gipfel-core" } diff --git a/crates/gipfel-cli/src/main.rs b/crates/gipfel-cli/src/main.rs new file mode 100644 index 0000000..f0690a8 --- /dev/null +++ b/crates/gipfel-cli/src/main.rs @@ -0,0 +1,371 @@ +//! gipfel-cli: headless command line tools, mirroring the C binary's +//! `-p/--export-position`, `-e/--export-hills` and stitch modes. + +use gipfel_core::hill::FLAG_EXPORT; +use gipfel_core::metadata::{apply_image_metadata, image_dimensions, ImageMetaData}; +use gipfel_core::panorama::Panorama; +use gipfel_core::stitch::{self, ScanMode}; + +fn usage() { + eprintln!( + "usage: gipfel-cli <command> [options]\n\ + \n\ + commands:\n\ + \x20 export-position <img> print position CSV line\n\ + \x20 export-hills <img> [-d <data>] [-e <hills>] export hill positions\n\ + \x20 info <img> show parsed metadata\n\ + \x20 stitch [-t out.tif [-4]] [-j out.jpg] [-w N]\n\ + \x20 [-h N] [-r from,to] [-b] img... cylindrical panorama\n\ + \x20 profiles list distortion profiles\n\ + \x20 profile-save <name> <k0> <k1> <x0> save a profile\n" + ); +} + +fn export_position(img: &str) -> i32 { + // C reads metadata directly (no image decode); missing values print as nan + let mut md = ImageMetaData::new(); + md.load_image(img); + + // C: printf(",%s,,%f,%f,%d\n", img_file, latitude, longitude, (int)rint(height)) + println!( + ",{},,{},{},{}", + img, + fmt_f(md.latitude), + fmt_f(md.longitude), + md.height.round_ties_even() as i64 + ); + 0 +} + +fn fmt_f(v: f64) -> String { + if v.is_nan() { + "nan".to_string() + } else { + format!("{:.6}", v) + } +} + +fn export_hills(img: &str, data: &str, export_file: Option<&str>) -> i32 { + let Some((w, h)) = image_dimensions(img) else { + eprintln!("could not read image {}", img); + return 1; + }; + + let mut md = ImageMetaData::new(); + md.load_image(img); + + let mut pan = Panorama::new(); + let have_info = apply_image_metadata(&mut pan, &md, w, h); + + if let Err(e) = pan.load_data(data) { + eprintln!("Could not load datafile {}: {}", data, e); + return 1; + } + pan.set_height_dist_ratio(0.07); // C default "visibility" + + if !have_info { + eprintln!("No gipfel info available for {}.", img); + return 0; + } + + // -e: restrict export to the hills from the given file + if let Some(file) = export_file { + match gipfel_core::hill::load(file) { + Ok(mut extra) => { + for m in extra.iter_mut() { + m.flags |= FLAG_EXPORT; + } + pan.add_hills(&extra); + } + Err(_) => return 1, + } + } + + println!("#\n# name\theight\tx\ty\tdistance\tflags\n#"); + + for row in pan.export_hill_rows(w as i64, h as i64, export_file.is_some()) { + println!("{}", row); + } + + if export_file.is_some() { + for m in pan.hills.iter_mut() { + m.flags &= !FLAG_EXPORT; + } + } + + 0 +} + +/// Port of the C stitch mode: resample the given images into one +/// cylindrical panorama. Flags follow the C binary: -w/-h output size, +/// -r from,to azimuth range in degrees, -b bicubic, -4 16-bit TIFF, +/// -j/-t output file (tiff wins if both given, as in C). +fn stitch(imgs: &[String], opts: &StitchOpts) -> i32 { + if imgs.is_empty() || (opts.tiff.is_none() && opts.jpeg.is_none()) { + usage(); + return 2; + } + let mut sources = Vec::new(); + for img in imgs { + match stitch::StitchSource::load(img) { + Ok(s) => sources.push(s), + Err(e) => { + eprintln!("{e}"); + return 1; + } + } + } + let mode = if opts.bicubic { ScanMode::Bicubic } else { ScanMode::Nearest }; + let out = stitch::resample(&sources, mode, opts.w, opts.h, opts.from, opts.to); + let hit = out.hit.iter().filter(|h| **h).count(); + eprintln!( + "stitched {}x{}: {}/{} pixels covered", + out.w, out.h, hit, out.hit.len() + ); + let res = if let Some(path) = &opts.tiff { + stitch::write_tiff(path, &out, opts.bits16) + } else { + stitch::write_jpeg(opts.jpeg.as_ref().unwrap(), &out, 90) + }; + match res { + Ok(()) => 0, + Err(e) => { + eprintln!("{e}"); + 1 + } + } +} + +struct StitchOpts { + w: u32, + h: u32, + from: f64, + to: f64, + bicubic: bool, + bits16: bool, + tiff: Option<String>, + jpeg: Option<String>, +} + +/// C defaults: stitch_w=2000, stitch_h=500, stitch_from=0.0, stitch_to=380.0 +fn stitch_defaults() -> StitchOpts { + StitchOpts { + w: 2000, + h: 500, + from: 0.0, + to: 380.0, + bicubic: false, + bits16: false, + tiff: None, + jpeg: None, + } +} + +fn stitch_cmd(args: &[String]) -> i32 { + let mut o = stitch_defaults(); + let mut imgs = Vec::new(); + let mut i = 0; + while i < args.len() { + let a = args[i].as_str(); + let need = |i: &mut usize| -> Option<String> { + if *i + 1 < args.len() { + *i += 1; + Some(args[*i].clone()) + } else { + None + } + }; + let ok = match a { + "-w" => match need(&mut i) { + Some(v) => { + o.w = v.parse().unwrap_or(2000); + true + } + None => false, + }, + "-h" => match need(&mut i) { + Some(v) => { + o.h = v.parse().unwrap_or(500); + true + } + None => false, + }, + "-r" => match need(&mut i) { + Some(v) => { + // C: atof(optarg) [, atof(after comma)] — atof reads the + // numeric prefix before the comma + let (first, second) = v.split_once(',').unwrap_or((v.as_str(), "")); + o.from = first.trim().parse().unwrap_or(0.0); + if !second.trim().is_empty() { + o.to = second.trim().parse().unwrap_or(380.0); + } + true + } + None => false, + }, + "-j" => match need(&mut i) { + Some(v) => { + o.jpeg = Some(v); + true + } + None => false, + }, + "-t" => match need(&mut i) { + Some(v) => { + o.tiff = Some(v); + true + } + None => false, + }, + "-b" => { + o.bicubic = true; + true + } + "-4" => { + o.bits16 = true; + true + } + _ => { + if a.starts_with('-') { + false + } else { + imgs.push(a.to_string()); + true + } + } + }; + if !ok { + usage(); + return 2; + } + i += 1; + } + stitch(&imgs, &o) +} + +fn info(img: &str) -> i32 { + let mut md = ImageMetaData::new(); + md.load_image(img); + println!("manufacturer: {:?}", md.manufacturer); + println!("model: {:?}", md.model); + println!("longitude: {}", md.longitude); + println!("latitude: {}", md.latitude); + println!("height: {}", md.height); + println!("direction: {}", md.direction); + println!("pitch: {}", md.pitch); + println!("roll: {}", md.roll); + println!("focal_length: {}", md.focal_length); + println!("focal_length_35mm: {}", md.focal_length_35mm); + println!("projection type: {}", md.projection_type); + println!("k0: {}", md.k0); + println!("k1: {}", md.k1); + println!("x0: {}", md.x0); + 0 +} + +fn profiles() -> i32 { + let all = gipfel_core::profile::list(); + if all.is_empty() { + eprintln!("no profiles in {}", gipfel_core::profile::prefs_path().display()); + } + for (name, p) in all { + println!("{name}\tk0={}\tk1={}\tx0={}", p.k0, p.k1, p.x0); + } + 0 +} + +fn profile_save(name: &str, k0: f64, k1: f64, x0: f64) -> i32 { + let p = gipfel_core::profile::Profile { k0, k1, x0 }; + match gipfel_core::profile::save(name, p, true) { + Ok(()) => { + eprintln!("saved profile {name}"); + 0 + } + Err(e) => { + eprintln!("{e}"); + 1 + } + } +} + +fn profile_save_cmd(args: &[String]) -> i32 { + if args.len() != 4 { + usage(); + return 2; + } + let (Ok(k0), Ok(k1), Ok(x0)) = ( + args[1].parse::<f64>(), + args[2].parse::<f64>(), + args[3].parse::<f64>(), + ) else { + usage(); + return 2; + }; + profile_save(&args[0], k0, k1, x0) +} + +fn main() { + let args: Vec<String> = std::env::args().collect(); + let Some(cmd) = args.get(1) else { + usage(); + std::process::exit(2); + }; + + let code = match cmd.as_str() { + "export-position" => { + if args.len() != 3 { + usage(); + 2 + } else { + export_position(&args[2]) + } + } + "export-hills" => { + if args.len() < 3 { + usage(); + 2 + } else { + let img = &args[2]; + let mut data = "gipfel.dat".to_string(); + let mut export_file: Option<String> = None; + let mut i = 3; + while i < args.len() { + match args[i].as_str() { + "-d" => { + if i + 1 < args.len() { + data = args[i + 1].clone(); + i += 1; + } + } + "-e" => { + if i + 1 < args.len() { + export_file = Some(args[i + 1].clone()); + i += 1; + } + } + _ => {} + } + i += 1; + } + export_hills(img, &data, export_file.as_deref()) + } + } + "info" => { + if args.len() != 3 { + usage(); + 2 + } else { + info(&args[2]) + } + } + "profiles" => profiles(), + "profile-save" => profile_save_cmd(&args[2..]), + "stitch" => stitch_cmd(&args[2..]), + _ => { + usage(); + 2 + } + }; + + std::process::exit(code); +} diff --git a/crates/gipfel-core/Cargo.toml b/crates/gipfel-core/Cargo.toml new file mode 100644 index 0000000..d8ddfd9 --- /dev/null +++ b/crates/gipfel-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "gipfel-core" +version = "0.1.0" +edition = "2021" +description = "Port of gipfel's computational core (hills, panorama, projections, least-squares fit, metadata)" +license = "GPL-2.0-or-later" + +[dependencies] +kamadak-exif.workspace = true +quick-xml.workspace = true +image.workspace = true +tiff.workspace = true +dirs.workspace = true diff --git a/crates/gipfel-core/examples/genjpg.rs b/crates/gipfel-core/examples/genjpg.rs new file mode 100644 index 0000000..ae12b94 --- /dev/null +++ b/crates/gipfel-core/examples/genjpg.rs @@ -0,0 +1,24 @@ +// creates a test JPEG with a gipfel comment: genjpg <out.jpg> +use gipfel_core::metadata::ImageMetaData; + +fn main() { + let out = std::env::args().nth(1).unwrap(); + let (w, h) = (1600u32, 900u32); + let img = image::DynamicImage::new_rgb8(w, h).grayscale(); + img.save(&out).unwrap(); + + let mut md = ImageMetaData::new(); + md.longitude = 11.15; + md.latitude = 47.36; + md.height = 2100.0; + md.direction = 20.0; + md.pitch = 0.0; + md.roll = 1.0; + md.focal_length_35mm = 35.0; + md.projection_type = 0; + md.k0 = 0.0; + md.k1 = 0.0; + md.x0 = 0.0; + md.save_image(&out, &out).unwrap(); + println!("wrote {}", out); +} diff --git a/crates/gipfel-core/src/hill.rs b/crates/gipfel-core/src/hill.rs new file mode 100644 index 0000000..a904948 --- /dev/null +++ b/crates/gipfel-core/src/hill.rs @@ -0,0 +1,224 @@ +//! Hill database: data structures, CSV loading, duplicate detection. +//! +//! Faithful port of gipfel's `Hill.cxx`. + +use std::fs::File; +use std::io::{BufRead, BufReader}; + +pub const FLAG_DUPLIC: u32 = 1 << 0; +pub const FLAG_KNOWN: u32 = 1 << 1; +pub const FLAG_HIDDEN: u32 = 1 << 2; +pub const FLAG_VISIBLE: u32 = 1 << 3; +pub const FLAG_TRACK_POINT: u32 = 1 << 4; +pub const FLAG_EXPORT: u32 = 1 << 5; + +pub const PI: f64 = std::f64::consts::PI; +pub const DEG2RAD: f64 = PI / 180.0; + +#[derive(Clone, Debug)] +pub struct Hill { + pub name: String, + /// latitude, radians + pub phi: f64, + /// longitude, radians + pub lam: f64, + /// height, meters + pub height: f64, + /// azimuth from viewpoint, radians + pub alph: f64, + /// peak angle over horizon, radians + pub a_elev: f64, + /// distance from viewpoint, fraction of earth radius + pub dist: f64, + pub x: f64, + pub y: f64, + pub label_x: f64, + pub label_y: f64, + pub flags: u32, +} + +impl Hill { + pub fn new(name: &str, phi: f64, lam: f64, height: f64) -> Self { + Hill { + name: name.to_string(), + phi, + lam, + height, + alph: 0.0, + a_elev: 0.0, + dist: 0.0, + x: 0.0, + y: 0.0, + label_x: 0.0, + label_y: 0.0, + flags: 0, + } + } + + pub fn is_track_point(&self) -> bool { + self.flags & FLAG_TRACK_POINT != 0 + } +} + +/// `atof()` semantics: parse the longest numeric prefix, 0.0 on failure. +fn atof(s: &str) -> f64 { + let s = s.trim(); + if s.is_empty() { + return 0.0; + } + if let Ok(v) = s.parse::<f64>() { + return v; + } + for i in (1..s.len()).rev() { + if let Ok(v) = s[..i].parse::<f64>() { + return v; + } + } + 0.0 +} + +/// Load a hill database. Two line formats are accepted: +/// - `SHORT,NAME,DESC,LAT,LON,HEIGHT` (6 fields) +/// - `LAT,LON,HEIGHT` (3 fields, track point) +pub fn load(path: &str) -> std::io::Result<Vec<Hill>> { + let file = File::open(path)?; + let mut hills = Vec::new(); + + // the database is Latin-1; decode to Unicode code points + for line in BufReader::new(file).split(b'\n') { + let raw = line?; + let line: String = raw.iter().map(|b| *b as char).collect(); + // strsep() semantics: split into at most 10 fields, keeping empties + let vals: Vec<&str> = line.split(',').take(10).collect(); + let n = vals.len(); + + if n == 6 { + // standard format including name and description + let phi = atof(vals[3]) * DEG2RAD; + let lam = atof(vals[4]) * DEG2RAD; + let height = atof(vals[5]); + hills.push(Hill::new(vals[1], phi, lam, height)); + } else if n == 3 { + // track point format + let phi = atof(vals[0]) * DEG2RAD; + let lam = atof(vals[1]) * DEG2RAD; + let height = atof(vals[2]); + let mut h = Hill::new("", phi, lam, height); + // NOTE: original does not set TRACK_POINT here + h.flags |= 0; + hills.push(h); + } + } + + Ok(hills) +} + +/// Mark hills that are (near-)exact duplicates of an earlier hill. +/// Faithful port of `Hills::mark_duplicates`. +/// +/// Unlike the C original (which sorts the pointer array in place), this +/// must NOT reorder `hills`: in this port, hill indices are stable handles +/// (track lists, known-hill sets, visible lists). Sorting an index +/// permutation instead keeps the semantics identical while preserving +/// insertion order. +pub fn mark_duplicates(hills: &mut [Hill], dist: f64) { + // descending phi (mirrors the C qsort comparator order) + let mut order: Vec<usize> = (0..hills.len()).collect(); + order.sort_by(|&a, &b| { + hills[b] + .phi + .partial_cmp(&hills[a].phi) + .unwrap_or(std::cmp::Ordering::Equal) + }); + + let len = hills.len(); + for pos in 0..len { + let i = order[pos]; + if hills[i].is_track_point() { + continue; + } + + let mut j = pos + 1; + while j < len && (hills[order[j]].phi - hills[i].phi).abs() <= dist { + let jj = order[j]; + if hills[jj].flags & FLAG_DUPLIC == 0 + && (hills[jj].lam - hills[i].lam).abs() <= dist + && (hills[jj].height - hills[i].height).abs() <= 50.0 + { + hills[jj].flags |= FLAG_DUPLIC; + } + j += 1; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn atof_semantics() { + assert_eq!(atof("12.5"), 12.5); + assert_eq!(atof(""), 0.0); + assert_eq!(atof("abc"), 0.0); + assert_eq!(atof("12abc"), 12.0); + assert_eq!(atof(" -3.5x"), -3.5); + } + + #[test] + fn load_formats() { + let dir = std::env::temp_dir().join("gipfel-core-test"); + std::fs::create_dir_all(&dir).unwrap(); + let path = dir.join("hills.csv"); + std::fs::write( + &path, + "# comment line\n\ + ZSP,Zugspitze,Germany,47.421,10.9855,2962\n\ + FOO,Foo Peak,,47.5,11.0,1000\n\ + 47.6,11.1,1234\n\ + bad,line\n", + ) + .unwrap(); + + let hills = load(path.to_str().unwrap()).unwrap(); + assert_eq!(hills.len(), 3); + assert_eq!(hills[0].name, "Zugspitze"); + assert!((hills[0].phi - 47.421 * DEG2RAD).abs() < 1e-12); + assert_eq!(hills[0].height, 2962.0); + assert_eq!(hills[2].name, ""); + let _ = std::fs::remove_file(&path); + } + + #[test] + fn duplicates() { + let mut hills = vec![ + Hill::new("A", 47.0, 11.0, 1000.0), + Hill::new("A2", 47.000001, 11.000001, 1010.0), // dup of A + Hill::new("B", 47.5, 11.5, 1000.0), + Hill::new("C", 47.0, 11.0, 1200.0), // >50m diff -> not dup + ]; + mark_duplicates(&mut hills, 0.00001); + assert_eq!(hills.iter().filter(|h| h.flags & FLAG_DUPLIC != 0).count(), 1); + } + + #[test] + fn mark_duplicates_preserves_order() { + // indices are stable handles in the port: mark_duplicates must not + // reorder the vec (regression: an in-place phi sort broke the app's + // track index range when add_hills ran it on the full hill list) + let mut hills = vec![ + Hill::new("south", 46.0, 11.0, 1000.0), + Hill::new("north", 48.0, 11.0, 1000.0), + Hill::new("mid", 47.0, 11.0, 1000.0), + Hill::new("mid-dup", 47.000001, 11.0, 1010.0), + ]; + mark_duplicates(&mut hills, 0.00001); + assert_eq!(hills[0].name, "south"); + assert_eq!(hills[1].name, "north"); + assert_eq!(hills[2].name, "mid"); + assert_eq!(hills[3].name, "mid-dup"); + // descending phi visits mid-dup first, so 'mid' is the one marked + assert_eq!(hills[2].flags & FLAG_DUPLIC, FLAG_DUPLIC); + assert_eq!(hills[3].flags & FLAG_DUPLIC, 0); + } +} diff --git a/crates/gipfel-core/src/lib.rs b/crates/gipfel-core/src/lib.rs new file mode 100644 index 0000000..2e2fac9 --- /dev/null +++ b/crates/gipfel-core/src/lib.rs @@ -0,0 +1,23 @@ +//! gipfel-core: headless port of the gipfel computational core. +//! +//! Modules: +//! - [`hill`]: hill database (CSV load, duplicate marking) +//! - [`panorama`]: viewpoint, geodesy, refraction, occlusion, visibility +//! - [`projection`]: rectilinear/cylindrical projections with symbolic +//! derivatives generated by maxima (see `codegen/`) +//! - [`lsq`]: Levenberg-Marquardt fit of the view parameters (system GSL) +//! - [`metadata`]: `gipfel:` JPEG comment + EXIF read/write + +pub mod hill; +pub mod lsq; +pub mod metadata; +pub mod panorama; +pub mod profile; +pub mod projection; +pub mod stitch; +pub mod track; + +pub use hill::Hill; +pub use metadata::ImageMetaData; +pub use panorama::Panorama; +pub use projection::{Projection, ProjectionType, ViewParams}; diff --git a/crates/gipfel-core/src/lsq.rs b/crates/gipfel-core/src/lsq.rs new file mode 100644 index 0000000..6a9d6c7 --- /dev/null +++ b/crates/gipfel-core/src/lsq.rs @@ -0,0 +1,392 @@ +//! Non-linear least squares fit of the view parameters. +//! +//! Faithful port of `ProjectionLSQ.cxx`. The C++ original uses GSL's +//! `gsl_multifit_fdfsolver_lmsder`; this is a self-contained Levenberg- +//! Marquardt with the same scaled damping (`JᵀJ + λ·diag(JᵀJ)`) and the +//! analytic Jacobian produced by the maxima-generated derivatives. For the +//! well-conditioned gipfel fit (residuals reach zero at the optimum) it +//! converges to the same minimum; the golden tests verify agreement with the +//! C/GSL output. + +use crate::hill::Hill; +use crate::projection::{Projection, ViewParams}; + +const MAX_ITER: usize = 200; + +/// Number of fitted parameters at a given level. +fn np_for_level(level: usize) -> usize { + match level { + 0 => 2, + 1 => 4, + _ => 7, + } +} + +struct FitData<'a> { + proj: &'a dyn Projection, + hills: &'a [Hill], + known: &'a [usize], + level: usize, + /// parameters at solver start; entries above `level` keep these values + parms0: ViewParams, +} + +impl FitData<'_> { + fn params_from_x(&self, x: &[f64]) -> ViewParams { + // C starts from the initial params and only overwrites the entries + // that are actually fitted at this level. + let mut p = self.parms0; + p.a_center = x[0]; + p.a_pitch = x[1]; + if self.level > 0 { + p.a_roll = x[2]; + p.scale = x[3]; + } + if self.level > 1 { + p.k0 = x[4]; + p.k1 = x[5]; + p.x0 = x[6]; + } + p + } + + fn args(&self, p: &ViewParams, i: usize) -> crate::projection::ProjArgs { + let m = &self.hills[self.known[i]]; + crate::projection::ProjArgs::from_params(p, m.alph, m.a_elev) + } + + /// Residual vector: 2 entries (x,y) per known hill, length `2*known.len()`. + fn residuals(&self, x: &[f64], f: &mut [f64]) { + let p = self.params_from_x(x); + for i in 0..self.known.len() { + let m = &self.hills[self.known[i]]; + let a = self.args(&p, i); + f[2 * i] = self.proj.mac_x(a) - m.x; + f[2 * i + 1] = self.proj.mac_y(a) - m.y; + } + } + + /// Jacobian, row-major `m`×`np` (`m = 2*known.len()`). + fn jacobian(&self, x: &[f64], j: &mut [f64]) { + let p = self.params_from_x(x); + let np = np_for_level(self.level); + let row = |r: usize, c: usize, v: f64, j: &mut [f64]| j[r * np + c] = v; + for i in 0..self.known.len() { + let a = self.args(&p, i); + + row(2 * i, 0, self.proj.mac_x_dc_view(a), j); + row(2 * i + 1, 0, self.proj.mac_y_dc_view(a), j); + row(2 * i, 1, self.proj.mac_x_dc_pitch(a), j); + row(2 * i + 1, 1, self.proj.mac_y_dc_pitch(a), j); + + if np > 2 { + row(2 * i, 2, self.proj.mac_x_dc_roll(a), j); + row(2 * i + 1, 2, self.proj.mac_y_dc_roll(a), j); + row(2 * i, 3, self.proj.mac_x_dscale(a), j); + row(2 * i + 1, 3, self.proj.mac_y_dscale(a), j); + } + + if np > 4 { + row(2 * i, 4, self.proj.mac_x_dk0(a), j); + row(2 * i + 1, 4, self.proj.mac_y_dk0(a), j); + row(2 * i, 5, self.proj.mac_x_dk1(a), j); + row(2 * i + 1, 5, self.proj.mac_y_dk1(a), j); + row(2 * i, 6, self.proj.mac_x_dx0(a), j); + row(2 * i + 1, 6, self.proj.mac_y_dx0(a), j); + } + } + } +} + +fn dot(a: &[f64], b: &[f64]) -> f64 { + a.iter().zip(b).map(|(x, y)| x * y).sum() +} + +/// Solve `A·δ = b` in place (A row-major `n`×`n`, overwritten) by Gaussian +/// elimination with partial pivoting. Returns `None` if the matrix is +/// (numerically) singular. +fn gauss_solve(a: &mut [f64], b: &[f64], n: usize) -> Option<Vec<f64>> { + let mut x = b.to_vec(); + for col in 0..n { + // partial pivot + let mut piv = col; + let mut maxv = a[col * n + col].abs(); + for r in (col + 1)..n { + let v = a[r * n + col].abs(); + if v > maxv { + maxv = v; + piv = r; + } + } + if maxv < 1e-300 { + return None; + } + if piv != col { + for c in 0..n { + a.swap(col * n + c, piv * n + c); + } + x.swap(col, piv); + } + let diag = a[col * n + col]; + for r in (col + 1)..n { + let factor = a[r * n + col] / diag; + a[r * n + col] = 0.0; + for c in (col + 1)..n { + a[r * n + c] -= factor * a[col * n + c]; + } + x[r] -= factor * x[col]; + } + } + for r in (0..n).rev() { + let mut s = x[r]; + for c in (r + 1)..n { + s -= a[r * n + c] * x[c]; + } + x[r] = s / a[r * n + r]; + } + Some(x) +} + +/// One Levenberg-Marquardt fit at a single parameter level. On return, `x` +/// holds the fitted parameters (length `np`). +fn lm(data: &FitData, x: &mut [f64], np: usize) { + let m = 2 * data.known.len(); + let mut f = vec![0.0f64; m]; + let mut fnew = vec![0.0f64; m]; + let mut jac = vec![0.0f64; m * np]; + let mut a = vec![0.0f64; np * np]; + let mut ad = vec![0.0f64; np * np]; + let mut g = vec![0.0f64; np]; + let mut neg_g = vec![0.0f64; np]; + let mut xnew = vec![0.0f64; np]; + + data.residuals(x, &mut f); + let mut cost = 0.5 * dot(&f, &f); + let mut lambda: f64 = 1e-3; + + for _ in 0..MAX_ITER { + if !cost.is_finite() { + break; + } + data.jacobian(x, &mut jac); + + // A = JᵀJ (symmetric), g = Jᵀf + for i in 0..np { + for k in i..np { + let mut s = 0.0; + for r in 0..m { + s += jac[r * np + i] * jac[r * np + k]; + } + a[i * np + k] = s; + a[k * np + i] = s; + } + let mut s = 0.0; + for r in 0..m { + s += jac[r * np + i] * f[r]; + } + g[i] = s; + } + + // Solve the damped normal equations (JᵀJ + λ·diag(JᵀJ))·δ = -Jᵀf, + // inflating λ until the system is non-singular. + let mut delta: Option<Vec<f64>> = None; + for _ in 0..50 { + ad.copy_from_slice(&a); + for i in 0..np { + let d = a[i * np + i]; + ad[i * np + i] = if d > 1e-300 { d * (1.0 + lambda) } else { lambda.max(1e-300) }; + } + for i in 0..np { + neg_g[i] = -g[i]; + } + let mut ad_try = ad.clone(); + if let Some(d) = gauss_solve(&mut ad_try, &neg_g, np) { + delta = Some(d); + break; + } + lambda *= 10.0; + } + let delta = match delta { + Some(d) => d, + None => break, + }; + + for i in 0..np { + xnew[i] = x[i] + delta[i]; + } + data.residuals(&xnew, &mut fnew); + let cost_new = 0.5 * dot(&fnew, &fnew); + + if cost_new < cost { + let improvement = cost - cost_new; + x.copy_from_slice(&xnew); + std::mem::swap(&mut f, &mut fnew); + cost = cost_new; + lambda = (lambda * 0.1).max(1e-12); + // Converged when the cost can no longer improve meaningfully. + if improvement <= 1e-18 * cost { + break; + } + } else { + lambda *= 10.0; + if lambda > 1e16 { + break; + } + } + } +} + +/// Run the Levenberg-Marquardt fit for one parameter level and write the +/// result back into `parms`. Mirrors `ProjectionLSQ::lsq`. +fn lsq(proj: &dyn Projection, hills: &[Hill], known: &[usize], parms: &mut ViewParams, level: usize) { + let np = np_for_level(level); + let data = FitData { + proj, + hills, + known, + level, + parms0: *parms, + }; + + let mut x = vec![ + parms.a_center, + parms.a_pitch, + parms.a_roll, + parms.scale, + parms.k0, + parms.k1, + parms.x0, + ]; + x.truncate(np); + + lm(&data, &mut x, np); + + parms.a_center = x[0]; + parms.a_pitch = x[1]; + if level > 0 { + parms.a_roll = x[2]; + parms.scale = x[3]; + } + if level > 1 { + parms.k0 = x[4]; + parms.k1 = x[5]; + parms.x0 = x[6]; + } +} + +/// Fit the view parameters to the known hills. +/// Mirrors `ProjectionLSQ::comp_params`. `known` indexes into `hills`. +/// With `fit_distortion == false` the lens distortion parameters +/// (k0/k1/x0) are held at their current values and only the viewpoint +/// parameters are fitted, even for 4+ known hills. +pub fn comp_params( + proj: &dyn Projection, + hills: &[Hill], + known: &[usize], + parms: &mut ViewParams, + fit_distortion: bool, +) -> Result<(), String> { + let n = known.len(); + if n < 1 { + return Err("Please position at least 1 hill".to_string()); + } + + let m1 = &hills[known[0]]; + let mut new_parms = *parms; + new_parms.a_center = m1.alph; + new_parms.a_pitch = 0.0; + + if n > 1 { + let m2 = &hills[known[1]]; + let scale_tmp = (m2.x - m1.x).abs() / (m2.alph - m1.alph).abs(); + if scale_tmp.is_nan() || scale_tmp < 50.0 { + return Err(format!("Could not determine initial scale value ({scale_tmp})")); + } + new_parms.scale = scale_tmp; + new_parms.a_roll = 0.0; + } + + if n > 3 && fit_distortion { + new_parms.k0 = 0.0; + new_parms.k1 = 0.0; + new_parms.x0 = 0.0; + eprintln!("Performing calibration"); + } + + let level = if n == 1 { 0 } else { 1 }; + lsq(proj, hills, known, &mut new_parms, level); + if n >= 4 && fit_distortion { + lsq(proj, hills, known, &mut new_parms, 2); + } + + if new_parms.scale.is_nan() || new_parms.scale < 50.0 { + return Err("Could not determine reasonable view parameters".to_string()); + } + + *parms = new_parms; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hill::Hill; + use crate::projection::{Projection, ProjectionType, ViewParams, projection_from_type}; + + /// Five observed hills projected with known parameters (incl. distortion). + fn fixture() -> (Box<dyn Projection>, Vec<Hill>, Vec<usize>, ViewParams) { + let proj = projection_from_type(ProjectionType::Rectilinear); + let truth = ViewParams { + a_center: 0.3, + scale: 2000.0, + a_pitch: 0.05, + a_roll: 0.0, + k0: -0.0012, + k1: 2e-5, + x0: 0.0, + }; + let mut hills = Vec::new(); + let mut known = Vec::new(); + for i in 0..5 { + let alph = 0.15 + i as f64 * 0.07; + let elev = 0.02 + i as f64 * 0.015; + let (x, y) = proj.get_coordinates(alph, elev, &truth); + let mut h = Hill::new(&format!("h{i}"), 0.0, 0.0, 1000.0); + h.alph = alph; + h.a_elev = elev; + h.x = x; + h.y = y; + hills.push(h); + known.push(i); + } + (proj, hills, known, truth) + } + + #[test] + fn locked_distortion_stays_untouched() { + let (proj, hills, known, truth) = fixture(); + let mut p = truth; + p.a_center -= 0.01; + p.scale *= 1.02; + comp_params(&*proj, &hills, &known, &mut p, false).unwrap(); + // 5 known hills, but the distortion params must not move + assert_eq!(p.k0, truth.k0); + assert_eq!(p.k1, truth.k1); + assert_eq!(p.x0, truth.x0); + // the viewpoint params did fit + assert!((p.a_center - truth.a_center).abs() < 1e-6); + } + + #[test] + fn unlocked_distortion_fit_recovers() { + let (proj, hills, known, truth) = fixture(); + let mut p = truth; + p.k0 = 0.0; + p.k1 = 0.0; + p.a_center -= 0.01; + p.scale *= 1.02; + comp_params(&*proj, &hills, &known, &mut p, true).unwrap(); + assert!((p.k0 - truth.k0).abs() < 1e-5, "k0={}", p.k0); + assert!((p.k1 - truth.k1).abs() < 1e-7, "k1={}", p.k1); + } +} diff --git a/crates/gipfel-core/src/metadata.rs b/crates/gipfel-core/src/metadata.rs new file mode 100644 index 0000000..e2a888f --- /dev/null +++ b/crates/gipfel-core/src/metadata.rs @@ -0,0 +1,697 @@ +//! Image metadata: the `gipfel:` JPEG comment (read/write, byte-compatible +//! with the C version) plus EXIF fallback (GPS position, focal length). +//! +//! Faithful port of gipfel's `ImageMetaData.cxx`. + +use std::fs::File; +use std::io::{BufReader, Write}; + +use exif::{In, Tag, Value}; + +pub const NAN: f64 = f64::NAN; + +#[derive(Clone, Debug)] +pub struct ImageMetaData { + pub manufacturer: Option<String>, + pub model: Option<String>, + pub longitude: f64, + pub latitude: f64, + pub height: f64, + pub direction: f64, + /// camera pitch, degrees (written as the "nick" key in the gipfel: + /// comment for backwards compatibility with the C original) + pub pitch: f64, + /// camera roll, degrees (written as the "tilt" key for compatibility) + pub roll: f64, + pub k0: f64, + pub k1: f64, + pub x0: f64, + pub focal_length: f64, + pub focal_length_35mm: f64, + pub projection_type: i32, +} + +impl Default for ImageMetaData { + fn default() -> Self { + ImageMetaData { + manufacturer: None, + model: None, + longitude: NAN, + latitude: NAN, + height: NAN, + direction: NAN, + pitch: NAN, + roll: NAN, + k0: NAN, + k1: NAN, + x0: NAN, + focal_length: NAN, + focal_length_35mm: NAN, + projection_type: 0, + } + } +} + +impl ImageMetaData { + pub fn new() -> Self { + Self::default() + } + + /// Read JPEG comment first, then fill remaining gaps from EXIF. + pub fn load_image(&mut self, path: &str) { + *self = ImageMetaData::new(); + if let Some(com) = read_jpeg_comment(path) { + self.parse_gipfel_comment(&com); + } + self.load_image_exif(path); + } + + /// Write the gipfel comment into a copy of `in_img`, saved as `out_img`. + /// Writes atomically via a temp file, like the C original. + pub fn save_image(&self, in_img: &str, out_img: &str) -> std::io::Result<()> { + let comment = self.format_comment(); + let tmp = format!("{}.tmp", out_img); + write_jpeg_comment(in_img, &tmp, comment.as_bytes())?; + std::fs::rename(&tmp, out_img)?; + Ok(()) + } + + pub fn format_comment(&self) -> String { + format!( + "gipfel: longitude {}, latitude {}, height {}, direction {}, nick {}, tilt {}, focal_length_35mm {}, projection type {}, k0 {}, k1 {}, x0 {}", + fmt_f64(self.longitude), + fmt_f64(self.latitude), + fmt_f64(self.height), + fmt_f64(self.direction), + fmt_f64(self.pitch), + fmt_f64(self.roll), + fmt_f64(self.focal_length_35mm), + self.projection_type, + fmt_f64(self.k0), + fmt_f64(self.k1), + fmt_f64(self.x0), + ) + } + + /// sscanf(gipfel_format_scan, ...) equivalent; returns true when at + /// least 8 fields were matched (the C requirement). + pub fn parse_gipfel_comment(&mut self, com: &[u8]) -> bool { + let s = String::from_utf8_lossy(com); + let mut sc = Scanner { s: s.as_ref() }; + + if !sc.lit("gipfel: longitude ") { + return false; + } + let Some(lo) = sc.f64() else { return false }; + if !sc.lit(", latitude ") { return false; } + let la = sc.f64(); + if la.is_none() { return false; } + if !sc.lit(", height ") { return false; } + let he = sc.f64(); + if he.is_none() { return false; } + if !sc.lit(", direction ") { return false; } + let dir = sc.f64(); + if dir.is_none() { return false; } + if !sc.lit(", nick ") { return false; } + let pit = sc.f64(); + if pit.is_none() { return false; } + if !sc.lit(", tilt ") { return false; } + let rol = sc.f64(); + if rol.is_none() { return false; } + if !sc.lit(", focal_length_35mm ") { return false; } + let fr = sc.f64(); + if fr.is_none() { return false; } + if !sc.lit(", projection type ") { return false; } + let pt = sc.i32(); + + // 8 matches required: through projection type + let Some(pt) = pt else { return false }; + + self.longitude = lo; + self.latitude = la.unwrap(); + self.height = he.unwrap(); + self.direction = dir.unwrap(); + self.pitch = pit.unwrap(); + self.roll = rol.unwrap(); + self.focal_length_35mm = fr.unwrap(); + self.projection_type = pt; + + // n >= 10: k0 and k1 were matched; x0 defaults to 0.0 when only 10 + if let Some(k0) = sc.f64_after(", k0 ") { + if let Some(k1) = sc.f64_after(", k1 ") { + self.k0 = k0; + self.k1 = k1; + self.x0 = sc.f64_after(", x0 ").unwrap_or(0.0); + } + } + + true + } + + fn load_image_exif(&mut self, path: &str) { + let Ok(file) = File::open(path) else { return }; + let mut reader = BufReader::new(file); + let Ok(exif) = exif::Reader::new().read_from_container(&mut reader) else { + return; + }; + + if self.manufacturer.is_none() { + if let Some(f) = exif.get_field(Tag::Make, In::PRIMARY) { + let s = field_string(f); + if !s.is_empty() { + self.manufacturer = Some(s); + } + } + } + if self.model.is_none() { + if let Some(f) = exif.get_field(Tag::Model, In::PRIMARY) { + let s = field_string(f); + if !s.is_empty() { + self.model = Some(s); + } + } + } + + if self.focal_length.is_nan() { + exif_set_value(self, &exif, Tag::FocalLength, In::PRIMARY, |md, v| md.focal_length = v); + } + if self.focal_length_35mm.is_nan() { + exif_set_value( + self, + &exif, + Tag::FocalLengthIn35mmFilm, + In::PRIMARY, + |md, v| md.focal_length_35mm = v, + ); + } + if self.longitude.is_nan() { + exif_set_coordinate(self, &exif, Tag::GPSLongitude, In::PRIMARY, |md, v| md.longitude = v); + if !self.longitude.is_nan() && exif_ref_is(&exif, Tag::GPSLongitudeRef, "W") { + self.longitude = -self.longitude; + } + } + if self.latitude.is_nan() { + exif_set_coordinate(self, &exif, Tag::GPSLatitude, In::PRIMARY, |md, v| md.latitude = v); + if !self.latitude.is_nan() && exif_ref_is(&exif, Tag::GPSLatitudeRef, "S") { + self.latitude = -self.latitude; + } + } + if self.height.is_nan() { + exif_set_value(self, &exif, Tag::GPSAltitude, In::PRIMARY, |md, v| md.height = v); + // GPSAltitudeRef: 0 = above sea level, 1 = below + if !self.height.is_nan() { + if let Some(f) = exif.get_field(Tag::GPSAltitudeRef, In::PRIMARY) { + if let Value::Byte(b) = &f.value { + if b.first() == Some(&1) { + self.height = -self.height; + } + } + } + } + } + } +} + +/// True if an EXIF ASCII reference tag (GPSLatitudeRef etc.) equals `val`. +fn exif_ref_is(exif: &exif::Exif, tag: Tag, val: &str) -> bool { + exif.get_field(tag, In::PRIMARY) + .and_then(|f| match &f.value { + Value::Ascii(a) => a.first().map(|v| String::from_utf8_lossy(v).trim() == val), + _ => None, + }) + .unwrap_or(false) +} + +fn fmt_f64(v: f64) -> String { + if v.is_nan() { + // C printf("%f", nan) prints "nan"; keep it parseable by both + "nan".to_string() + } else { + format!("{:.6}", v) + } +} + +fn field_string(f: &exif::Field) -> String { + let join = |v: &[String]| v.join(" "); + match &f.value { + Value::Ascii(a) => a + .first() + .map(|v| String::from_utf8_lossy(v).trim().to_string()) + .unwrap_or_default(), + Value::Rational(r) => join(r.iter().map(|v| v.to_string()).collect::<Vec<_>>().as_slice()), + Value::Short(v) => join(v.iter().map(|v| v.to_string()).collect::<Vec<_>>().as_slice()), + Value::Long(v) => join(v.iter().map(|v| v.to_string()).collect::<Vec<_>>().as_slice()), + Value::SShort(v) => join(v.iter().map(|v| v.to_string()).collect::<Vec<_>>().as_slice()), + Value::SLong(v) => join(v.iter().map(|v| v.to_string()).collect::<Vec<_>>().as_slice()), + _ => String::new(), + } +} + +/// exifSetValue: assign when the tag exists and its value is >= 0. +fn exif_set_value<F>(md: &mut ImageMetaData, exif: &exif::Exif, tag: Tag, inp: In, set: F) +where + F: FnOnce(&mut ImageMetaData, f64), +{ + if let Some(f) = exif.get_field(tag, inp) { + if let Some(v) = field_tofloat(f, 0) { + if v >= 0.0 { + set(md, v); + } + } + } +} + +/// exifSetCoordinate: degrees + minutes/60 + seconds/3600, each component +/// only used when >= 0 (faithful to the C original, incl. ignoring N/S refs). +fn exif_set_coordinate<F>(md: &mut ImageMetaData, exif: &exif::Exif, tag: Tag, inp: In, set: F) +where + F: FnOnce(&mut ImageMetaData, f64), +{ + let Some(f) = exif.get_field(tag, inp) else { + return; + }; + + let mut dest: Option<f64> = None; + for i in 0..3 { + if let Some(v) = field_tofloat(f, i) { + if v >= 0.0 { + dest = Some(match i { + 0 => v, + 1 => dest.unwrap_or(0.0) + v / 60.0, + _ => dest.unwrap_or(0.0) + v / 3600.0, + }); + } + } + } + if let Some(v) = dest { + set(md, v); + } +} + +fn field_tofloat(f: &exif::Field, idx: usize) -> Option<f64> { + match &f.value { + Value::Rational(r) => r.get(idx).map(|v| v.num as f64 / v.denom as f64), + Value::Short(v) => v.get(idx).map(|v| *v as f64), + Value::Long(v) => v.get(idx).map(|v| *v as f64), + Value::SShort(v) => v.get(idx).map(|v| *v as f64), + Value::SLong(v) => v.get(idx).map(|v| *v as f64), + _ => None, + } +} + +// --------------------------------------------------------------------------- +// strtod-like scanner for the sscanf format +// --------------------------------------------------------------------------- + +struct Scanner<'a> { + s: &'a str, +} + +impl Scanner<'_> { + fn skip_ws(&mut self) { + self.s = self.s.trim_start(); + } + + fn lit(&mut self, lit: &str) -> bool { + if self.s.starts_with(lit) { + self.s = &self.s[lit.len()..]; + true + } else { + false + } + } + + /// %lf: skip whitespace, parse the longest numeric prefix. + fn f64(&mut self) -> Option<f64> { + self.skip_ws(); + for i in (1..=self.s.len()).rev() { + if let Ok(v) = self.s[..i].parse::<f64>() { + self.s = &self.s[i..]; + return Some(v); + } + } + None + } + + fn f64_after(&mut self, lit: &str) -> Option<f64> { + if self.lit(lit) { + self.f64() + } else { + None + } + } + + /// %d + fn i32(&mut self) -> Option<i32> { + self.skip_ws(); + for i in (1..=self.s.len()).rev() { + if let Ok(v) = self.s[..i].parse::<i32>() { + self.s = &self.s[i..]; + return Some(v); + } + } + None + } +} + +// --------------------------------------------------------------------------- +// JPEG COM segment reading / writing +// --------------------------------------------------------------------------- + +fn marker_len_ok(marker: u8) -> bool { + // standalone markers without length field + marker == 0x01 || (0xD0..=0xD9).contains(&marker) +} + +/// Return the payload of the first COM segment before SOS, if any. +pub fn read_jpeg_comment(path: &str) -> Option<Vec<u8>> { + let bytes = std::fs::read(path).ok()?; + if bytes.len() < 2 || bytes[0] != 0xFF || bytes[1] != 0xD8 { + return None; + } + + let mut i = 2; + while i + 1 < bytes.len() { + if bytes[i] != 0xFF { + return None; + } + let marker = bytes[i + 1]; + if marker == 0xFF { + i += 1; + continue; + } + if marker == 0xDA || marker == 0xD9 { + return None; + } + if marker_len_ok(marker) { + i += 2; + continue; + } + if i + 4 > bytes.len() { + return None; + } + let len = (u16::from_be_bytes([bytes[i + 2], bytes[i + 3]]) as usize).checked_add(2)?; + if i + len > bytes.len() { + return None; + } + if marker == 0xFE { + return Some(bytes[i + 4..i + 2 + len - 2].to_vec()); + } + i += len; + } + None +} + +/// Copy `in_img` to `out_img`, dropping all COM segments and inserting a +/// fresh COM segment right after SOI. +pub fn write_jpeg_comment(in_img: &str, out_img: &str, comment: &[u8]) -> std::io::Result<()> { + let bytes = std::fs::read(in_img)?; + if bytes.len() < 2 || bytes[0] != 0xFF || bytes[1] != 0xD8 { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "not a JPEG file")); + } + if comment.len() + 2 > 0xFFFF { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "comment too long")); + } + + let mut out: Vec<u8> = Vec::with_capacity(bytes.len() + comment.len() + 4); + out.extend_from_slice(&bytes[..2]); // SOI + + // Copy the SOI-adjacent header region (APPn/COM/DPI/etc.), dropping COM + // segments, and insert our fresh COM after the last APPn segment: + // that is where exiv2 (and thus the C gipfel) expects it. + let mut i = 2; + let mut insert_at = out.len(); + let mut copy_rest_from = bytes.len(); + while i + 1 < bytes.len() { + if bytes[i] != 0xFF { + break; + } + let marker = bytes[i + 1]; + if marker == 0xFF { + i += 1; + continue; + } + if marker == 0xDA || marker == 0xD9 { + copy_rest_from = i; + break; + } + if marker_len_ok(marker) { + out.push(0xFF); + out.push(marker); + i += 2; + continue; + } + if i + 4 > bytes.len() { + break; + } + let len = match u16::from_be_bytes([bytes[i + 2], bytes[i + 3]]) { + l => (l as usize).checked_add(2).unwrap_or(usize::MAX), + }; + if len < 2 || i + len > bytes.len() { + break; + } + if marker != 0xFE { + out.extend_from_slice(&bytes[i..i + len]); + if (0xE0..=0xEF).contains(&marker) { + insert_at = out.len(); + } + } + i += len; + } + copy_rest_from = copy_rest_from.min(i.max(2)); + + let mut com: Vec<u8> = Vec::with_capacity(comment.len() + 4); + com.push(0xFF); + com.push(0xFE); + com.extend_from_slice(&((comment.len() + 2) as u16).to_be_bytes()); + com.extend_from_slice(comment); + out.splice(insert_at..insert_at, com); + + out.extend_from_slice(&bytes[copy_rest_from..]); + + let mut f = File::create(out_img)?; + f.write_all(&out)?; + f.flush() +} + +/// Read raw image dimensions (width, height) of a JPEG. +pub fn image_dimensions(path: &str) -> Option<(u32, u32)> { + image::image_dimensions(path).ok() +} + +/// Apply image metadata to a panorama like `GipfelWidget::load_image` does. +/// Returns false when the gipfel info is incomplete (C: `have_gipfel_info`). +pub fn apply_image_metadata( + pan: &mut crate::panorama::Panorama, + md: &ImageMetaData, + img_w: u32, + img_h: u32, +) -> bool { + use crate::projection::ProjectionType; + + let mut have_info = true; + + pan.set_view_long(md.longitude); + pan.set_view_lat(md.latitude); + pan.set_view_height(md.height); + + pan.set_projection(match md.projection_type { + 1 => ProjectionType::Cylindrical, + _ => ProjectionType::Rectilinear, + }); + + if md.direction.is_nan() { + pan.set_center_angle(0.0); + have_info = false; + } else { + pan.set_center_angle(md.direction); + } + + if md.pitch.is_nan() { + pan.set_pitch_angle(0.0); + have_info = false; + } else { + pan.set_pitch_angle(md.pitch); + } + + if md.roll.is_nan() { + pan.set_roll_angle(0.0); + have_info = false; + } else { + pan.set_roll_angle(md.roll); + } + + // set_focal_length_35mm: scale = fl * max(w, h) / 35 + let w = img_w.max(img_h); + if md.focal_length_35mm.is_nan() || md.focal_length_35mm == 0.0 { + pan.set_scale(35.0 * w as f64 / 35.0); + have_info = false; + } else { + pan.set_scale(md.focal_length_35mm * w as f64 / 35.0); + } + + // distortion parameters, C ordering: + // 1. gipfel data in JPEG comment + // 2. matching distortion profile (camera + focal length) + // 3. 0.0, 0.0, 0.0 + let (mut k0, mut k1, mut x0) = (md.k0, md.k1, md.x0); + if k0.is_nan() { + if let Some(p) = md + .manufacturer + .as_deref() + .zip(md.model.as_deref()) + .and_then(|(mfr, model)| crate::profile::profile_name(mfr, model, md.focal_length)) + .and_then(|name| crate::profile::load(&name).ok()) + { + k0 = p.k0; + k1 = p.k1; + x0 = p.x0; + } + } + if k0.is_nan() { + k0 = 0.0; + } + if k1.is_nan() { + k1 = 0.0; + } + if x0.is_nan() { + x0 = 0.0; + } + pan.set_distortion_params(k0, k1, x0); + + have_info +} + +/// The profile name auto-matched from this image's camera and focal length +/// (C `get_distortion_profile_name`). Suggested whenever the camera is +/// known — [`apply_image_metadata`] only *applies* it when the JPEG +/// comment carries no distortion params. +pub fn distortion_profile_name(md: &ImageMetaData) -> Option<String> { + md.manufacturer + .as_deref() + .zip(md.model.as_deref()) + .and_then(|(mfr, model)| crate::profile::profile_name(mfr, model, md.focal_length)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn jpeg_comment_write_read() { + let dir = std::env::temp_dir().join("gipfel-core-mdtest"); + std::fs::create_dir_all(&dir).unwrap(); + let img = dir.join("t.jpg"); + let out = dir.join("o.jpg"); + + image::DynamicImage::new_rgb8(64, 48) + .grayscale() + .save(&img) + .unwrap(); + + let comment = b"gipfel: longitude 1.000000, latitude 2.000000, height 3.000000, direction 4.000000, nick 5.000000, tilt 6.000000, focal_length_35mm 7.000000, projection type 0, k0 0.000000, k1 0.000000, x0 0.000000"; + write_jpeg_comment(img.to_str().unwrap(), out.to_str().unwrap(), comment).unwrap(); + + // no COM before, one after + assert!(read_jpeg_comment(img.to_str().unwrap()).is_none()); + assert_eq!(read_jpeg_comment(out.to_str().unwrap()).unwrap(), comment); + + // rewriting replaces the existing comment + let out2 = dir.join("o2.jpg"); + write_jpeg_comment(out.to_str().unwrap(), out2.to_str().unwrap(), b"short").unwrap(); + assert_eq!(read_jpeg_comment(out2.to_str().unwrap()).unwrap(), b"short"); + + // image still decodes + assert!(image::image_dimensions(out.to_str().unwrap()).is_ok()); + + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn distortion_precedence_and_legacy_import() { + use crate::panorama::Panorama; + use crate::profile; + + let dir = std::env::temp_dir().join(format!("gipfel-prec-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let data = dir.join("data"); + let home = dir.join("home"); + std::fs::create_dir_all(&data).unwrap(); + + let old = (std::env::var("HOME").ok(), std::env::var("XDG_DATA_HOME").ok()); + std::env::set_var("HOME", &home); + std::env::set_var("XDG_DATA_HOME", &data); + + // legacy FLTK prefs file (C version's location and format) + let legacy = home + .join(".fltk") + .join("Johannes.HofmannATgmx.de") + .join("gipfel") + .join("DistortionProfiles.prefs"); + std::fs::create_dir_all(legacy.parent().unwrap()).unwrap(); + std::fs::write( + &legacy, + "; FLTK preferences file format 1.0\n; vendor: Johannes.HofmannATgmx.de\n\ + ; application: gipfel/DistortionProfiles\n\n[.]\n\n\n\ + [./Canon_EOS_5D_50.00_mm]\n\nk0:-0.01\nk1:0.001\nx0:3\n", + ) + .unwrap(); + + // first access imports into the XDG data dir + assert!(!profile::prefs_path().exists()); + let all = profile::list(); + assert!(all.iter().any(|(n, p)| n == "Canon_EOS_5D_50.00_mm" && p.k0 == -0.01)); + assert!(profile::prefs_path().starts_with(&data)); + assert!(profile::prefs_path().exists()); + + let mut md = ImageMetaData::new(); + md.manufacturer = Some("Canon".into()); + md.model = Some("EOS_5D".into()); + md.focal_length = 50.0; + let mut pan = Panorama::new(); + + // 1. comment wins when present + md.k0 = -0.9; + md.k1 = 0.0; + md.x0 = 0.0; + apply_image_metadata(&mut pan, &md, 1000, 800); + assert!((pan.parms.k0 + 0.9).abs() < 1e-12, "comment should win"); + + // 2. profile used when comment absent + md.k0 = f64::NAN; + md.k1 = f64::NAN; + md.x0 = f64::NAN; + apply_image_metadata(&mut pan, &md, 1000, 800); + assert!( + (pan.parms.k0 + 0.01).abs() < 1e-12, + "profile should apply: k0={}", + pan.parms.k0 + ); + // name is suggested regardless of the comment (C save dialog) + assert_eq!( + distortion_profile_name(&md).as_deref(), + Some("Canon_EOS_5D_50.00_mm") + ); + md.k0 = 0.0; + assert_eq!( + distortion_profile_name(&md).as_deref(), + Some("Canon_EOS_5D_50.00_mm") + ); + + // 3. zero when neither + md.k0 = f64::NAN; + md.manufacturer = None; + apply_image_metadata(&mut pan, &md, 1000, 800); + assert_eq!(pan.parms.k0, 0.0); + + match old.0 { + Some(h) => std::env::set_var("HOME", h), + None => std::env::remove_var("HOME"), + } + match old.1 { + Some(h) => std::env::set_var("XDG_DATA_HOME", h), + None => std::env::remove_var("XDG_DATA_HOME"), + } + let _ = std::fs::remove_dir_all(&dir); + } +} diff --git a/crates/gipfel-core/src/panorama.rs b/crates/gipfel-core/src/panorama.rs new file mode 100644 index 0000000..9e27251 --- /dev/null +++ b/crates/gipfel-core/src/panorama.rs @@ -0,0 +1,502 @@ +//! Panorama: viewpoint, hill visibility (occlusion, refraction) and the +//! link between geographic coordinates and image coordinates. +//! +//! Faithful port of gipfel's `Panorama.cxx`. + +use crate::hill::{Hill, DEG2RAD, PI}; +use crate::projection::{projection_from_type, Projection, ProjectionType, ViewParams}; + +const EARTH_RADIUS: f64 = 6371000.785; + +pub struct Panorama { + pub hills: Vec<Hill>, + /// indices into `hills`, sorted by alpha descending (rebuilt by update_angles) + pub alpha_order: Vec<usize>, + /// indices into `hills`, sorted by alpha descending + pub close: Vec<usize>, + /// indices into `hills` + pub visible: Vec<usize>, + + pub view_name: Option<String>, + /// latitude, radians + pub view_phi: f64, + /// longitude, radians + pub view_lam: f64, + /// meters + pub view_height: f64, + + pub height_dist_ratio: f64, + pub hide_value: f64, + /// Additional visibility limit: maximum angular distance (radians) from + /// the image center. The app sets this to the angle the projection maps + /// to the image edge (the lens' actual field of view, including fitted + /// distortion), so that directions outside the photo — which a distortion + /// model can fold back into the image — neither get markers nor compass + /// ticks. 0 = no additional limit (only the projection's own limit). + pub max_view_angle: f64, + + pub parms: ViewParams, + pub projection_type: ProjectionType, + proj: Box<dyn Projection>, +} + +impl Default for Panorama { + fn default() -> Self { + Self::new() + } +} + +impl Panorama { + pub fn new() -> Self { + Panorama { + hills: Vec::new(), + alpha_order: Vec::new(), + close: Vec::new(), + visible: Vec::new(), + view_name: None, + view_phi: 0.0, + view_lam: 0.0, + view_height: 0.0, + height_dist_ratio: 0.07, + hide_value: 1.2, + max_view_angle: 0.0, + parms: ViewParams::new(), + projection_type: ProjectionType::Rectilinear, + proj: projection_from_type(ProjectionType::Rectilinear), + } + } + + pub fn load_data(&mut self, path: &str) -> std::io::Result<()> { + let mut hills = crate::hill::load(path)?; + crate::hill::mark_duplicates(&mut hills, 0.00001); + self.hills = hills; + self.update_angles(); + Ok(()) + } + + pub fn add_hills(&mut self, extra: &[Hill]) { + self.hills.extend_from_slice(extra); + crate::hill::mark_duplicates(&mut self.hills, 0.00001); + self.update_angles(); + } + + pub fn remove_hills(&mut self, flags: u32) { + self.hills.retain(|m| m.flags & flags == 0); + // indices shifted; rebuild all derived lists + self.alpha_order.clear(); + self.close.clear(); + self.visible.clear(); + } + + pub fn set_viewpoint(&mut self, idx: Option<usize>) { + let Some(idx) = idx else { return }; + let m = &self.hills[idx]; + self.view_phi = m.phi; + self.view_lam = m.lam; + self.view_height = m.height; + self.view_name = Some(m.name.clone()); + self.update_angles(); + } + + /// Fit view parameters to the known hills (indices into `self.hills`). + /// `fit_distortion` enables the 7-parameter fit (k0/k1/x0) for 4+ hills; + /// otherwise the distortion params stay locked at their current values. + pub fn comp_params(&mut self, known: &[usize], fit_distortion: bool) -> Result<(), String> { + let ret = crate::lsq::comp_params(&*self.proj, &self.hills, known, &mut self.parms, fit_distortion); + if ret.is_ok() { + self.update_visible_mountains(Some(known)); + } + ret + } + + /// Like [`Panorama::comp_params`] but the observed hills are an external + /// slice (used by the golden fit tests, mirroring the C harness). + pub fn comp_params_with(&mut self, hills: &[Hill], known: &[usize], fit_distortion: bool) -> Result<(), String> { + let ret = crate::lsq::comp_params(&*self.proj, hills, known, &mut self.parms, fit_distortion); + if ret.is_ok() { + self.update_visible_mountains(None); + } + ret + } + + pub fn set_center_angle(&mut self, deg: f64) { + self.parms.a_center = deg * DEG2RAD; + self.update_visible_mountains(None); + } + + pub fn set_pitch_angle(&mut self, deg: f64) { + self.parms.a_pitch = deg * DEG2RAD; + self.update_coordinates(None); + } + + pub fn set_roll_angle(&mut self, deg: f64) { + self.parms.a_roll = deg * DEG2RAD; + self.update_coordinates(None); + } + + pub fn set_scale(&mut self, s: f64) { + self.parms.scale = s; + self.update_coordinates(None); + } + + pub fn set_distortion_params(&mut self, k0: f64, k1: f64, x0: f64) { + self.parms.k0 = k0; + self.parms.k1 = k1; + self.parms.x0 = x0; + self.update_coordinates(None); + } + + pub fn set_height_dist_ratio(&mut self, r: f64) { + self.height_dist_ratio = r; + self.update_close_mountains(); + } + + pub fn set_view_long(&mut self, deg: f64) { + self.view_lam = deg * DEG2RAD; + self.update_angles(); + } + + pub fn set_view_lat(&mut self, deg: f64) { + self.view_phi = deg * DEG2RAD; + self.update_angles(); + } + + pub fn set_view_height(&mut self, v: f64) { + self.view_height = v; + self.update_angles(); + } + + pub fn set_projection(&mut self, t: ProjectionType) { + self.projection_type = t; + self.proj = projection_from_type(t); + self.update_angles(); + } + + pub fn get_center_angle(&self) -> f64 { + self.parms.a_center / DEG2RAD + } + pub fn get_pitch_angle(&self) -> f64 { + self.parms.a_pitch / DEG2RAD + } + pub fn get_roll_angle(&self) -> f64 { + self.parms.a_roll / DEG2RAD + } + pub fn get_scale(&self) -> f64 { + self.parms.scale + } + pub fn get_view_long(&self) -> f64 { + self.view_lam / DEG2RAD + } + pub fn get_view_lat(&self) -> f64 { + self.view_phi / DEG2RAD + } + pub fn get_view_height(&self) -> f64 { + self.view_height + } + + /// Last hill whose name matches exactly (C keeps the last match). + pub fn get_pos(&self, name: &str) -> Option<usize> { + let mut ret = None; + for (i, m) in self.hills.iter().enumerate() { + if m.name == name { + ret = Some(i); + } + } + ret + } + + pub fn update_angles(&mut self) { + for i in 0..self.hills.len() { + let (phi, lam) = (self.hills[i].phi, self.hills[i].lam); + let d = self.distance(phi, lam); + self.hills[i].dist = d; + if phi != self.view_phi || lam != self.view_lam { + let a = self.alpha(i); + self.hills[i].alph = a; + } + } + + // keep `hills` in insertion order (indices stay stable) and maintain a + // separate alpha-descending order, mirroring the C pointer-array sort + let mut idx: Vec<usize> = (0..self.hills.len()).collect(); + idx.sort_by(|&a, &b| { + self.hills[b] + .alph + .partial_cmp(&self.hills[a].alph) + .unwrap_or(std::cmp::Ordering::Equal) + }); + self.alpha_order = idx; + + self.update_close_mountains(); + } + + /// Mark hills that are hidden behind a nearer, taller neighbour. + /// Faithful port including the original's `fabs(m->alph - n->alph > pi/2)` + /// oddity (comparison inside fabs, i.e. skip when m.alph - n.alph > pi/2). + pub fn mark_hidden(&mut self, hills: &[usize]) { + for i in 0..hills.len() { + let m = hills[i]; + self.hills[m].flags &= !crate::hill::FLAG_HIDDEN; + + if self.hills[m].flags & crate::hill::FLAG_DUPLIC != 0 { + continue; + } + + for j in 0..hills.len() { + let n = hills[j]; + if self.hills[n].flags & crate::hill::FLAG_DUPLIC != 0 + || self.hills[n].flags & crate::hill::FLAG_TRACK_POINT != 0 + { + continue; + } + if n == m || self.hills[m].alph - self.hills[n].alph > PI / 2.0 { + continue; + } + if self.hills[m].dist < self.hills[n].dist + || self.hills[m].a_elev > self.hills[n].a_elev + { + continue; + } + + let h = (self.hills[n].a_elev - self.hills[m].a_elev) + / (self.hills[m].alph - self.hills[n].alph).abs(); + if h.is_infinite() || h > self.hide_value { + self.hills[m].flags |= crate::hill::FLAG_HIDDEN; + } + } + } + } + + pub fn update_close_mountains(&mut self) { + self.close.clear(); + + let order = self.alpha_order.clone(); + for &i in &order { + let m = &self.hills[i]; + if m.flags & crate::hill::FLAG_TRACK_POINT != 0 + || ((m.phi != self.view_phi || m.lam != self.view_lam) + && (m.height / (m.dist * EARTH_RADIUS) > self.height_dist_ratio)) + { + let elev = self.elevation(i); + self.hills[i].a_elev = elev; + self.close.push(i); + } + } + + let close = self.close.clone(); + self.mark_hidden(&close); + self.update_visible_mountains(None); + } + + pub fn update_visible_mountains(&mut self, excluded: Option<&[usize]>) { + self.visible.clear(); + + for k in 0..self.close.len() { + let i = self.close[k]; + if self.is_visible(self.hills[i].alph) { + self.visible.push(i); + self.hills[i].flags |= crate::hill::FLAG_VISIBLE; + } else { + self.hills[i].flags &= !crate::hill::FLAG_VISIBLE; + } + } + + let owned; + let excl: Option<&[usize]> = match excluded { + Some(e) => { + owned = e.to_vec(); + Some(&owned) + } + None => None, + }; + self.update_coordinates(excl); + } + + pub fn update_coordinates(&mut self, excluded: Option<&[usize]>) { + for k in 0..self.visible.len() { + let i = self.visible[k]; + if let Some(e) = excluded { + if e.contains(&i) { + continue; + } + } + let (alph, elev) = (self.hills[i].alph, self.hills[i].a_elev); + let (x, y) = self.proj.get_coordinates(alph, elev, &self.parms); + self.hills[i].x = x; + self.hills[i].y = y; + } + } + + /// angular distance (fraction of earth radius) viewpoint -> (phi, lam) + pub fn distance(&self, phi: f64, lam: f64) -> f64 { + let d_lam = self.view_lam - lam; + + let t1 = (phi.cos() * d_lam.sin()).powi(2); + let t2 = (self.view_phi.cos() * phi.sin() + - self.view_phi.sin() * phi.cos() * d_lam.cos()) + .powi(2); + + (t1 + t2).sqrt().atan2( + self.view_phi.sin() * phi.sin() + self.view_phi.cos() * phi.cos() * d_lam.cos(), + ) + } + + pub fn alpha(&self, idx: usize) -> f64 { + let m = &self.hills[idx]; + let sin_alph = (m.lam - self.view_lam).sin() * m.phi.cos() / m.dist.sin(); + let cos_alph = + (m.phi.sin() - self.view_phi.sin() * m.dist.cos()) / (self.view_phi.cos() * m.dist.sin()); + + (sin_alph.atan2(cos_alph) + 2.0 * PI) % (2.0 * PI) + } + + /// approximation of refraction effect as described by Tom Chester at + /// <http://tchester.org/sgm/analysis/peaks/refraction.html> + pub fn refraction(&self, idx: usize) -> f64 { + let (alpha, t0) = (6.5, 10.0); + + let a = 2.9e-4 * (-self.view_height / 10000.0).exp() / (1.0 + 2.9 * t0 / 760.0); + let b = 2.9 * alpha / (760.0 * (1.0 + 2.9 * t0 / 760.0)); + let c = a * (b - 1.0 / 10.0); + + c * self.get_real_distance(idx) / (2000.0 * (1.0 + a)) + } + + pub fn elevation(&self, idx: usize) -> f64 { + let m = &self.hills[idx]; + let theta = self.refraction(idx); + + let b = m.height + self.get_earth_radius(m.phi); + let c = self.view_height + self.get_earth_radius(self.view_phi); + + ((m.dist.cos() * b - c) / (m.dist.sin() * b)).atan() - theta + } + + /// local distance to center of WGS84 ellipsoid + pub fn get_earth_radius(&self, phi: f64) -> f64 { + let a = 6378137.000f64; + let b = 6356752.315f64; + let ata = phi.tan(); + + a * (ata.powi(2) + 1.0).powf(1.0 / 2.0) + * b.abs() + * (b.powi(2) + a.powi(2) * ata.powi(2)).powf(-1.0 / 2.0) + } + + /// real (line-of-sight) distance in meters + pub fn get_real_distance(&self, idx: usize) -> f64 { + let m = &self.hills[idx]; + let a = self.view_height + self.get_earth_radius(self.view_phi); + let b = m.height + self.get_earth_radius(m.phi); + let gam = m.dist; + + (a.powi(2) + b.powi(2) - 2.0 * a * b * gam.cos()).sqrt() + } + + pub fn is_visible(&self, alph: f64) -> bool { + let mut center_dist = (alph - self.parms.a_center).abs(); + if center_dist > PI { + center_dist = 2.0 * PI - center_dist; + } + if center_dist >= self.proj.get_view_angle() { + return false; + } + self.max_view_angle <= 0.0 || center_dist < self.max_view_angle + } + + /// Forward-project (azimuth, elevation) to image-centered pixel coords. + /// `None` when the direction is not visible or the projection is + /// undefined there (mirrors C `Panorama::get_coordinates` returning 1, + /// plus the NaN guard of C `ScanImage::get_pixel_nearest`). + pub fn project_point(&self, alph: f64, elev: f64) -> Option<(f64, f64)> { + if !self.is_visible(alph) { + return None; + } + let (x, y) = self.proj.get_coordinates(alph, elev, &self.parms); + if x.is_nan() || y.is_nan() { + return None; + } + Some((x, y)) + } + + /// Parameter-only snapshot (no hill data) for resampling: same viewpoint + /// and projection parameters, but without the UI's lens-FOV visibility + /// limit (the stitch output range defines the FOV itself). + pub fn params_snapshot(&self) -> Panorama { + let mut p = Panorama::new(); + p.projection_type = self.projection_type; + p.proj = projection_from_type(self.projection_type); + p.parms = self.parms; + p.view_phi = self.view_phi; + p.view_lam = self.view_lam; + p.view_height = self.view_height; + p + } + + pub fn get_coordinates(&self, alph: f64, elev: f64) -> Option<(f64, f64)> { + if self.is_visible(alph) { + Some(self.proj.get_coordinates(alph, elev, &self.parms)) + } else { + None + } + } + + /// Export rows for the visible hills, mirroring the C `export_hills` + /// output loop: `name\theight\tx\ty\tdistance` with image-center origin. + /// When `require_export` is set, only hills with `FLAG_EXPORT` are listed. + pub fn export_hill_rows(&self, img_w: i64, img_h: i64, require_export: bool) -> Vec<String> { + let mut rows = Vec::new(); + for &i in &self.visible { + let m = &self.hills[i]; + if m.flags & crate::hill::FLAG_DUPLIC != 0 + || m.flags & crate::hill::FLAG_HIDDEN != 0 + || (require_export && m.flags & crate::hill::FLAG_EXPORT == 0) + { + continue; + } + + let x = m.x.round_ties_even() as i64 + img_w / 2; + let y = m.y.round_ties_even() as i64 + img_h / 2; + if x < 0 || x > img_w || y < 0 || y > img_h { + continue; + } + + rows.push(format!( + "{}\t{}\t{}\t{}\t{}", + m.name, + m.height.round_ties_even() as i64, + x, + y, + self.get_real_distance(i).round_ties_even() as i64 + )); + } + rows + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn max_view_angle_limits_visibility() { + let mut pan = Panorama::new(); + pan.set_center_angle(0.0); + let a30 = 30f64.to_radians(); + let a70 = 70f64.to_radians(); + + // rectilinear default limit is +-60 deg + assert!(pan.is_visible(a30)); + assert!(!pan.is_visible(a70)); + + // a tighter lens FOV (say +-35 deg) hides the 40 deg direction that + // the projection's own +-60 deg limit would allow + pan.max_view_angle = 35f64.to_radians(); + assert!(pan.is_visible(a30)); + assert!(!pan.is_visible(40f64.to_radians())); + + // a wider limit never overrides the projection's own bound + pan.max_view_angle = 80f64.to_radians(); + assert!(!pan.is_visible(a70)); + } +} diff --git a/crates/gipfel-core/src/profile.rs b/crates/gipfel-core/src/profile.rs new file mode 100644 index 0000000..e8bda68 --- /dev/null +++ b/crates/gipfel-core/src/profile.rs @@ -0,0 +1,362 @@ +//! Lens distortion profiles. +//! +//! Profiles live in the standard per-user data directory +//! (`$XDG_DATA_HOME/gipfel/distortion-profiles.prefs`, i.e. +//! `~/.local/share/gipfel/…`). The file format is the FLTK preferences +//! format used by the C version (`[./name]` groups, `key:value` entries, +//! `%g` values), so the legacy file +//! `~/.fltk/Johannes.HofmannATgmx.de/gipfel/DistortionProfiles.prefs` +//! imports automatically into the new location on first use. +//! +//! File layout (as written by FLTK): +//! ```text +//! ; FLTK preferences file format 1.0 +//! ; vendor: Johannes.HofmannATgmx.de +//! ; application: gipfel/DistortionProfiles +//! +//! [.] +//! +//! [./Canon_EOS_5D_50.00_mm] +//! +//! k0:-0.00123 +//! k1:2.5e-05 +//! x0:0.5 +//! ``` + +use std::path::{Path, PathBuf}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Profile { + pub k0: f64, + pub k1: f64, + pub x0: f64, +} + +#[derive(Debug)] +pub enum Error { + NotFound, + Exists, + BadName, + Io(std::io::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::NotFound => write!(f, "profile not found"), + Error::Exists => write!(f, "profile already exists"), + Error::BadName => write!(f, "invalid profile name"), + Error::Io(e) => write!(f, "{e}"), + } + } +} + +/// The profile store in the standard data directory +/// (`$XDG_DATA_HOME/gipfel/distortion-profiles.prefs`). +pub fn prefs_path() -> PathBuf { + dirs::data_dir() + .unwrap_or_else(|| PathBuf::from(std::env::var("HOME").unwrap_or_default())) + .join("gipfel") + .join("distortion-profiles.prefs") +} + +/// Legacy FLTK preferences file used by the C version. +fn fltk_prefs_path() -> PathBuf { + let home = std::env::var("HOME").unwrap_or_default(); + Path::new(&home) + .join(".fltk") + .join("Johannes.HofmannATgmx.de") + .join("gipfel") + .join("DistortionProfiles.prefs") +} + +/// Path to the profile store; imports legacy FLTK profiles on first use. +fn ensure_store() -> PathBuf { + let path = prefs_path(); + if !path.exists() { + let legacy = fltk_prefs_path(); + if legacy.exists() { + if let Some(dir) = path.parent() { + let _ = std::fs::create_dir_all(dir); + } + // same format, so a copy suffices + let _ = std::fs::copy(&legacy, &path); + } + } + path +} + +/// C `get_distortion_profile_name`: `"%s_%s_%.2f_mm"`. `None` when the +/// camera is unknown or the focal length is unusable (C would build a name +/// containing "nan" that can never match a stored profile). +pub fn profile_name(manufacturer: &str, model: &str, focal_mm: f64) -> Option<String> { + if manufacturer.is_empty() || model.is_empty() || !focal_mm.is_finite() || focal_mm <= 0.0 { + return None; + } + let name = format!("{manufacturer}_{model}_{focal_mm:.2}_mm"); + sanitize_name(&name).ok().map(|_| name) +} + +/// Reject names that would break the prefs file structure (FLTK group paths +/// use '/' as separator; control chars/newlines corrupt the file). +pub fn sanitize_name(name: &str) -> Result<(), Error> { + if name.trim().is_empty() || name.len() > 255 { + return Err(Error::BadName); + } + if name + .chars() + .any(|c| c == '/' || c == '\\' || c.is_control()) + { + return Err(Error::BadName); + } + Ok(()) +} + +/// Parse a prefs file into ordered (name, profile) pairs. Tolerant: +/// comments (`;`), the root group `[.]`, blank lines and unknown keys are +/// ignored; values are parsed with Rust's `f64` (superset of C `%g`). +fn parse(text: &str) -> Vec<(String, Profile)> { + let mut out: Vec<(String, Profile)> = Vec::new(); + let mut cur: Option<usize> = None; + for line in text.lines() { + let line = line.trim_end(); + if line.is_empty() || line.starts_with(';') { + continue; + } + if let Some(group) = line.strip_prefix('[').and_then(|s| s.strip_suffix(']')) { + cur = match group.strip_prefix("./") { + Some(name) => { + out.push(( + name.to_string(), + Profile { + k0: f64::NAN, + k1: f64::NAN, + x0: f64::NAN, + }, + )); + Some(out.len() - 1) + } + None => None, // "[.]" root group + }; + continue; + } + let Some(idx) = cur else { continue }; + if let Some((key, val)) = line.split_once(':') { + let Ok(v) = val.trim().parse::<f64>() else { + continue; + }; + let p = &mut out[idx].1; + match key.trim() { + "k0" => p.k0 = v, + "k1" => p.k1 = v, + "x0" => p.x0 = v, + _ => {} + } + } + } + out.retain(|(_, p)| p.k0.is_finite() && p.k1.is_finite() && p.x0.is_finite()); + out +} + +/// Serialize in FLTK's layout so the C version can read our file. +fn serialize(profiles: &[(String, Profile)]) -> String { + let mut s = String::from( + "; FLTK preferences file format 1.0\n\ + ; vendor: Johannes.HofmannATgmx.de\n\ + ; application: gipfel/DistortionProfiles\n\ + \n\ + [.]\n\ + \n", + ); + for (name, p) in profiles { + s.push_str(&format!( + "\n[./{name}]\n\nk0:{}\nk1:{}\nx0:{}\n", + fmt_g(p.k0), + fmt_g(p.k1), + fmt_g(p.x0) + )); + } + s +} + +/// Closest Rust equivalent of C `%g`: scientific notation for magnitudes +/// outside [1e-4, 1e15), exponent zero-padded to two digits ("2.5e-05"). +/// Unlike C we keep the shortest round-trip mantissa (more precise; the +/// FLTK reader uses strtod and reads either fine). +fn fmt_g(v: f64) -> String { + if v != 0.0 && (v.abs() < 1e-4 || v.abs() >= 1e15) { + if let Some((mant, exp)) = format!("{v:e}").split_once('e') { + let neg = exp.starts_with('-'); + let digits = exp.trim_start_matches(['-', '+']); + let digits = if digits.len() < 2 { format!("0{digits}") } else { digits.to_string() }; + let sign = if neg { '-' } else { '+' }; + return format!("{mant}e{sign}{digits}"); + } + } + if v == v.trunc() && v.abs() < 1e16 { + format!("{}", v as i64) + } else { + format!("{v}") + } +} + +fn read_all(path: &Path) -> Result<Vec<(String, Profile)>, Error> { + match std::fs::read_to_string(path) { + Ok(text) => Ok(parse(&text)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Vec::new()), + Err(e) => Err(Error::Io(e)), + } +} + +fn write_all(path: &Path, profiles: &[(String, Profile)]) -> Result<(), Error> { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(Error::Io)?; + } + // atomic: temp file + rename + let tmp = path.with_extension("prefs.tmp"); + std::fs::write(&tmp, serialize(profiles)).map_err(Error::Io)?; + std::fs::rename(&tmp, path).map_err(Error::Io)?; + Ok(()) +} + +/// Load a profile by exact name from the default store. +pub fn load(name: &str) -> Result<Profile, Error> { + load_from(&ensure_store(), name) +} + +pub fn load_from(path: &Path, name: &str) -> Result<Profile, Error> { + read_all(path)? + .into_iter() + .find(|(n, _)| n == name) + .map(|(_, p)| p) + .ok_or(Error::NotFound) +} + +/// Save a profile. With `force == false` an existing profile of that name is +/// not overwritten (C `save_distortion_params(name, force)`). +pub fn save(name: &str, profile: Profile, force: bool) -> Result<(), Error> { + save_to(&ensure_store(), name, profile, force) +} + +pub fn save_to(path: &Path, name: &str, profile: Profile, force: bool) -> Result<(), Error> { + sanitize_name(name)?; + let mut all = read_all(path)?; + if let Some(pos) = all.iter().position(|(n, _)| n == name) { + if !force { + return Err(Error::Exists); + } + all[pos].1 = profile; + } else { + all.push((name.to_string(), profile)); + } + write_all(path, &all) +} + +/// All profiles in the default store, in file order. +pub fn list() -> Vec<(String, Profile)> { + read_all(&ensure_store()).unwrap_or_default() +} + +pub fn list_from(path: &Path) -> Vec<(String, Profile)> { + read_all(path).unwrap_or_default() +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Exact bytes as written by the C version (verified against FLTK 1.4). + const FLTK_FIXTURE: &str = "; FLTK preferences file format 1.0\n\ + ; vendor: Johannes.HofmannATgmx.de\n\ + ; application: gipfel/DistortionProfiles\n\ + \n\ + [.]\n\ + \n\ + \n\ + [./Canon_EOS_5D_50.00_mm]\n\ + \n\ + k0:-0.00123\n\ + k1:2.5e-05\n\ + x0:0.5\n\ + \n\ + [./Nikon D850_35.00_mm]\n\ + \n\ + k0:0.01\n\ + k1:0\n\ + x0:0\n"; + + #[test] + fn parses_fltk_layout() { + let all = parse(FLTK_FIXTURE); + assert_eq!(all.len(), 2); + assert_eq!(all[0].0, "Canon_EOS_5D_50.00_mm"); + assert_eq!(all[0].1.k0, -0.00123); + assert_eq!(all[0].1.k1, 2.5e-5); + assert_eq!(all[0].1.x0, 0.5); + assert_eq!(all[1].0, "Nikon D850_35.00_mm"); + assert_eq!(all[1].1.k0, 0.01); + assert_eq!(all[1].1.k1, 0.0); + } + + #[test] + fn serialize_replicates_fltk_layout() { + let all = parse(FLTK_FIXTURE); + assert_eq!(serialize(&all), FLTK_FIXTURE); + } + + #[test] + fn fmt_g_matches_c_percent_g() { + assert_eq!(fmt_g(2.5e-5), "2.5e-05"); + assert_eq!(fmt_g(-0.00123), "-0.00123"); + assert_eq!(fmt_g(0.0001), "0.0001"); + assert_eq!(fmt_g(1e15), "1e+15"); + assert_eq!(fmt_g(0.0), "0"); + assert_eq!(fmt_g(0.01), "0.01"); + } + + #[test] + fn profile_name_matches_c() { + assert_eq!( + profile_name("Canon", "EOS_5D", 50.0).as_deref(), + Some("Canon_EOS_5D_50.00_mm") + ); + assert_eq!( + profile_name("Nikon", "D850", 34.666).as_deref(), + Some("Nikon_D850_34.67_mm") + ); + assert!(profile_name("", "D850", 35.0).is_none()); + assert!(profile_name("Canon", "D850", f64::NAN).is_none()); + assert!(profile_name("Can/on", "D850", 35.0).is_none()); + } + + #[test] + fn roundtrip_and_overwrite() { + let dir = std::env::temp_dir().join(format!("gipfel-prof-{}", std::process::id())); + let path = dir.join("DistortionProfiles.prefs"); + let _ = std::fs::remove_dir_all(&dir); + + let p = Profile { k0: -0.001, k1: 2e-5, x0: 0.0 }; + save_to(&path, "Cam_A_24.00_mm", p, false).unwrap(); + // exists -> error without force + assert!(matches!( + save_to(&path, "Cam_A_24.00_mm", p, false), + Err(Error::Exists) + )); + save_to(&path, "Cam_B_50.00_mm", Profile { k0: 0.1, k1: 0.0, x0: 1.0 }, false).unwrap(); + // overwrite first, keep second + let p2 = Profile { k0: -0.002, k1: 2e-5, x0: 0.0 }; + save_to(&path, "Cam_A_24.00_mm", p2, true).unwrap(); + + let all = list_from(&path); + assert_eq!(all.len(), 2); + assert_eq!(all[0].1, p2); + assert_eq!(load_from(&path, "Cam_B_50.00_mm").unwrap().k0, 0.1); + assert!(matches!(load_from(&path, "nope"), Err(Error::NotFound))); + + // bad names rejected + assert!(save_to(&path, "a/b", p, true).is_err()); + assert!(save_to(&path, "with\nnewline", p, true).is_err()); + + std::fs::remove_dir_all(&dir).unwrap(); + } +} diff --git a/crates/gipfel-core/src/projection/cylindrical.rs b/crates/gipfel-core/src/projection/cylindrical.rs new file mode 100644 index 0000000..9adf79c --- /dev/null +++ b/crates/gipfel-core/src/projection/cylindrical.rs @@ -0,0 +1,86 @@ +//! Cylindrical projection. + +mod funcs { + #![allow(non_snake_case, unused_parens, unused_variables, dead_code, clippy::all)] + + // math shims: the maxima-generated code uses C-style calls + #[inline(always)] + fn sin(x: f64) -> f64 { + x.sin() + } + #[inline(always)] + fn cos(x: f64) -> f64 { + x.cos() + } + #[inline(always)] + fn tan(x: f64) -> f64 { + x.tan() + } + #[inline(always)] + fn sqrt(x: f64) -> f64 { + x.sqrt() + } + #[inline(always)] + fn pow(x: f64, y: f64) -> f64 { + x.powf(y) + } + #[inline(always)] + fn exp(x: f64) -> f64 { + x.exp() + } + #[inline(always)] + fn fabs(x: f64) -> f64 { + x.abs() + } + #[inline(always)] + fn atan2(y: f64, x: f64) -> f64 { + y.atan2(x) + } + #[inline(always)] + fn sec(x: f64) -> f64 { + 1.0 / x.cos() + } + + include!("generated/cylindrical_funcs.rs"); +} + +use super::{ProjArgs, Projection}; + +pub struct ProjectionCylindrical; + +impl Projection for ProjectionCylindrical { + fn get_view_angle(&self) -> f64 { + 6.2831853 /* 360 deg */ + } + + fn mac_x(&self, a: ProjArgs) -> f64 { + funcs::mac_x(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y(&self, a: ProjArgs) -> f64 { + funcs::mac_y(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_view(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_view(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_pitch(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_pitch(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_roll(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_roll(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dscale(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dscale(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_view(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_view(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_pitch(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_pitch(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_roll(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_roll(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dscale(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dscale(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } +} diff --git a/crates/gipfel-core/src/projection/generated/cylindrical_funcs.rs b/crates/gipfel-core/src/projection/generated/cylindrical_funcs.rs new file mode 100644 index 0000000..220a7d8 --- /dev/null +++ b/crates/gipfel-core/src/projection/generated/cylindrical_funcs.rs @@ -0,0 +1,10 @@ +pub fn mac_x ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return (cos(c_roll)*(m_view-c_view)-(sin(c_roll)*(_2-_1))/(_1*_2+1.0))*scale ; } +pub fn mac_y ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return ((-sin(c_roll)*(m_view-c_view))-(cos(c_roll)*(_2-_1))/(_1*_2+1.0))*scale ; } +pub fn mac_x_dc_view ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { return -cos(c_roll)*scale ; } +pub fn mac_x_dc_pitch ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; _1 = pow(sec(c_pitch),2.0) ; _2 = sin(c_roll) ; _3 = tan(m_elev) ; _4 = tan(c_pitch) ; _5 = _4*_3+1.0 ; return ((_1*_2*_3*(_3-_4))/pow(_5,2.0)+(_1*_2)/_5)*scale ; } +pub fn mac_x_dc_roll ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return ((-sin(c_roll)*(m_view-c_view))-(cos(c_roll)*(_2-_1))/(_1*_2+1.0))*scale ; } +pub fn mac_x_dscale ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return cos(c_roll)*(m_view-c_view)-(sin(c_roll)*(_2-_1))/(_1*_2+1.0) ; } +pub fn mac_y_dc_view ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { return sin(c_roll)*scale ; } +pub fn mac_y_dc_pitch ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; _1 = pow(sec(c_pitch),2.0) ; _2 = cos(c_roll) ; _3 = tan(m_elev) ; _4 = tan(c_pitch) ; _5 = _4*_3+1.0 ; return ((_1*_2*_3*(_3-_4))/pow(_5,2.0)+(_1*_2)/_5)*scale ; } +pub fn mac_y_dc_roll ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return ((sin(c_roll)*(_2-_1))/(_1*_2+1.0)-cos(c_roll)*(m_view-c_view))*scale ; } +pub fn mac_y_dscale ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; _1 = tan(c_pitch) ; _2 = tan(m_elev) ; return (-sin(c_roll)*(m_view-c_view))-(cos(c_roll)*(_2-_1))/(_1*_2+1.0) ; } diff --git a/crates/gipfel-core/src/projection/generated/rectilinear_funcs.rs b/crates/gipfel-core/src/projection/generated/rectilinear_funcs.rs new file mode 100644 index 0000000..7bd450d --- /dev/null +++ b/crates/gipfel-core/src/projection/generated/rectilinear_funcs.rs @@ -0,0 +1,16 @@ +pub fn mac_x ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2.0)+pow(_13*_11*_12-_1*_5*_14*_11,2.0) ; return scale*_15*(k0*sqrt(_16)+k1*_16+1.0) ; } +pub fn mac_y ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = pow(x0+_1*_5*_14*_11+_13*_11*_12,2.0)+pow(_15,2.0) ; return _15*scale*(k0*sqrt(_16)+k1*_16+1.0) ; } +pub fn mac_x_dc_view ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; let mut _20 : f64 = 0.0; let mut _21 : f64 = 0.0; let mut _22 : f64 = 0.0; let mut _23 : f64 = 0.0; let mut _24 : f64 = 0.0; let mut _25 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1.0/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = cos(c_roll) ; _15 = _6*_9-_8*_7 ; _16 = x0+_14*_5*_15*_12+_1*_12*_13 ; _17 = _14*_12*_13-_1*_5*_15*_12 ; _18 = 1.0/pow(_11,2.0) ; _19 = pow(_5,2.0) ; _20 = pow(_15,2.0) ; _21 = (-_8*_9)-_6*_7 ; _22 = _14*_5*_21*_12+_2*_1*_5*_15*_12-_4*_14*_19*_20*_18-_4*_1*_5*_15*_18*_13 ; _23 = 2.0*_22*_16+2.0*_17*((-_1*_5*_21*_12)+_2*_14*_5*_15*_12+_4*_1*_19*_20*_18-_4*_14*_5*_15*_18*_13) ; _24 = pow(_17,2.0)+pow(_16,2.0) ; _25 = sqrt(_24) ; return _22*scale*(k0*_25+k1*_24+1.0)+scale*_16*((k0*_23)/(2.0*_25)+k1*_23) ; } +pub fn mac_x_dc_pitch ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; let mut _20 : f64 = 0.0; let mut _21 : f64 = 0.0; let mut _22 : f64 = 0.0; let mut _23 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1.0/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = cos(c_roll) ; _15 = _6*_9-_8*_7 ; _16 = x0+_14*_5*_15*_12+_1*_12*_13 ; _17 = 1.0/pow(_11,2.0) ; _18 = _4*_3-_2*_5*_10 ; _19 = _14*_12*_13-_1*_5*_15*_12 ; _20 = (-_1*_17*_18*_13)-_14*_5*_15*_17*_18+_1 ; _21 = 2.0*_20*_16+2.0*((-_14*_17*_18*_13)+_1*_5*_15*_17*_18+_14)*_19 ; _22 = pow(_19,2.0)+pow(_16,2.0) ; _23 = sqrt(_22) ; return _20*scale*(k0*_23+k1*_22+1.0)+scale*_16*((k0*_21)/(2.0*_23)+k1*_21) ; } +pub fn mac_x_dc_roll ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = _13*_11*_12-_1*_5*_14*_11 ; _17 = 2.0*_16*_15+2.0*((-_13*_5*_14*_11)-_1*_11*_12)*_16 ; _18 = pow(_16,2.0)+pow(_15,2.0) ; _19 = sqrt(_18) ; return _16*scale*(k0*_19+k1*_18+1.0)+scale*_15*((k0*_17)/(2.0*_19)+k1*_17) ; } +pub fn mac_x_dscale ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2.0)+pow(_13*_11*_12-_1*_5*_14*_11,2.0) ; return _15*(k0*sqrt(_16)+k1*_16+1.0) ; } +pub fn mac_x_dk0 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; return _15*sqrt(pow(_15,2.0)+pow(_13*_11*_12-_1*_5*_14*_11,2.0))*scale ; } +pub fn mac_x_dk1 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; return _15*(pow(_15,2.0)+pow(_13*_11*_12-_1*_5*_14*_11,2.0))*scale ; } +pub fn mac_x_dx0 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; _1 = sin(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2.0)+pow(_13*_11*_12-_1*_5*_14*_11,2.0) ; _17 = sqrt(_16) ; return scale*(k0*_17+k1*_16+1.0)+scale*_15*((k0*_15)/_17+2.0*k1*_15) ; } +pub fn mac_y_dc_view ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; let mut _20 : f64 = 0.0; let mut _21 : f64 = 0.0; let mut _22 : f64 = 0.0; let mut _23 : f64 = 0.0; let mut _24 : f64 = 0.0; let mut _25 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1.0/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = sin(c_roll) ; _15 = _6*_9-_8*_7 ; _16 = _1*_12*_13-_14*_5*_15*_12 ; _17 = 1.0/pow(_11,2.0) ; _18 = pow(_5,2.0) ; _19 = pow(_15,2.0) ; _20 = (-_8*_9)-_6*_7 ; _21 = (-_14*_5*_20*_12)+_2*_1*_5*_15*_12+_4*_14*_18*_19*_17-_4*_1*_5*_15*_17*_13 ; _22 = x0+_1*_5*_15*_12+_14*_12*_13 ; _23 = 2.0*(_1*_5*_20*_12+_2*_14*_5*_15*_12-_4*_1*_18*_19*_17-_4*_14*_5*_15*_17*_13)*_22+2.0*_16*_21 ; _24 = pow(_22,2.0)+pow(_16,2.0) ; _25 = sqrt(_24) ; return _21*scale*(k0*_25+k1*_24+1.0)+_16*scale*((k0*_23)/(2.0*_25)+k1*_23) ; } +pub fn mac_y_dc_pitch ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; let mut _20 : f64 = 0.0; let mut _21 : f64 = 0.0; let mut _22 : f64 = 0.0; let mut _23 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1.0/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = sin(c_roll) ; _15 = _6*_9-_8*_7 ; _16 = _1*_12*_13-_14*_5*_15*_12 ; _17 = 1.0/pow(_11,2.0) ; _18 = _4*_3-_2*_5*_10 ; _19 = (-_1*_17*_18*_13)+_14*_5*_15*_17*_18+_1 ; _20 = x0+_1*_5*_15*_12+_14*_12*_13 ; _21 = 2.0*((-_14*_17*_18*_13)-_1*_5*_15*_17*_18+_14)*_20+2.0*_19*_16 ; _22 = pow(_20,2.0)+pow(_16,2.0) ; _23 = sqrt(_22) ; return _19*scale*(k0*_23+k1*_22+1.0)+_16*scale*((k0*_21)/(2.0*_23)+k1*_21) ; } +pub fn mac_y_dc_roll ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; let mut _17 : f64 = 0.0; let mut _18 : f64 = 0.0; let mut _19 : f64 = 0.0; let mut _20 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = (-_1*_5*_14*_11)-_13*_11*_12 ; _17 = x0+_1*_5*_14*_11+_13*_11*_12 ; _18 = 2.0*_15*_17+2.0*_16*_15 ; _19 = pow(_17,2.0)+pow(_15,2.0) ; _20 = sqrt(_19) ; return _16*scale*(k0*_20+k1*_19+1.0)+_15*scale*((k0*_18)/(2.0*_20)+k1*_18) ; } +pub fn mac_y_dscale ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = pow(x0+_1*_5*_14*_11+_13*_11*_12,2.0)+pow(_15,2.0) ; return _15*(k0*sqrt(_16)+k1*_16+1.0) ; } +pub fn mac_y_dk0 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; return _15*scale*sqrt(pow(x0+_1*_5*_14*_11+_13*_11*_12,2.0)+pow(_15,2.0)) ; } +pub fn mac_y_dk1 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; return _15*scale*(pow(x0+_1*_5*_14*_11+_13*_11*_12,2.0)+pow(_15,2.0)) ; } +pub fn mac_y_dx0 ( c_view: f64, c_pitch: f64, c_roll: f64, scale: f64, k0: f64, k1: f64, x0: f64, m_view: f64, m_elev: f64 ) -> f64 { let mut _1 : f64 = 0.0; let mut _2 : f64 = 0.0; let mut _3 : f64 = 0.0; let mut _4 : f64 = 0.0; let mut _5 : f64 = 0.0; let mut _6 : f64 = 0.0; let mut _7 : f64 = 0.0; let mut _8 : f64 = 0.0; let mut _9 : f64 = 0.0; let mut _10 : f64 = 0.0; let mut _11 : f64 = 0.0; let mut _12 : f64 = 0.0; let mut _13 : f64 = 0.0; let mut _14 : f64 = 0.0; let mut _15 : f64 = 0.0; let mut _16 : f64 = 0.0; _1 = cos(c_roll) ; _2 = sin(c_pitch) ; _3 = sin(m_elev) ; _4 = cos(c_pitch) ; _5 = cos(m_elev) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1.0/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_roll) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = x0+_1*_5*_14*_11+_13*_11*_12 ; return _15*((_16*k0)/sqrt(pow(_16,2.0)+pow(_15,2.0))+2.0*k1*_16)*scale ; } diff --git a/crates/gipfel-core/src/projection/mod.rs b/crates/gipfel-core/src/projection/mod.rs new file mode 100644 index 0000000..65b74e5 --- /dev/null +++ b/crates/gipfel-core/src/projection/mod.rs @@ -0,0 +1,174 @@ +//! Projections: image coordinates as a function of azimuth/elevation angle, +//! with symbolically pre-generated derivatives (see `codegen/`). +//! +//! Faithful port of gipfel's `ProjectionLSQ.{H,cxx}`, `ProjectionRectilinear.*` +//! and `ProjectionCylindrical.*`. + +pub mod cylindrical; +pub mod rectilinear; + +pub const NAN: f64 = f64::NAN; + +/// The 7 fitted view parameters (radians where angular). +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct ViewParams { + /// azimuth of image center, radians + pub a_center: f64, + /// focal scale (pixels) + pub scale: f64, + /// pitch angle of image center (camera nose up), radians + pub a_pitch: f64, + /// camera roll, radians + pub a_roll: f64, + /// radial distortion coefficient 0 + pub k0: f64, + /// radial distortion coefficient 1 + pub k1: f64, + /// distortion center x, pixels + pub x0: f64, +} + +impl ViewParams { + pub fn new() -> Self { + ViewParams { + a_center: 0.0, + scale: 3500.0, + a_pitch: 0.0, + a_roll: 0.0, + k0: 0.0, + k1: 0.0, + x0: 0.0, + } + } +} + +/// The 9 arguments every generated macro function takes. +#[derive(Clone, Copy, Debug)] +pub struct ProjArgs { + pub c_view: f64, + pub c_pitch: f64, + pub c_roll: f64, + pub scale: f64, + pub k0: f64, + pub k1: f64, + pub x0: f64, + pub m_view: f64, + pub m_elev: f64, +} + +impl ProjArgs { + pub fn from_params(p: &ViewParams, m_view: f64, m_elev: f64) -> Self { + ProjArgs { + c_view: p.a_center, + c_pitch: p.a_pitch, + c_roll: p.a_roll, + scale: p.scale, + k0: p.k0, + k1: p.k1, + x0: p.x0, + m_view, + m_elev, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ProjectionType { + Rectilinear = 0, + Cylindrical = 1, +} + +/// A projection plus its symbolic partial derivatives w.r.t. the view +/// parameters. Methods default to `NAN`, matching the C++ base class. +pub trait Projection: Send + Sync { + /// half-width of the visible azimuth range, radians + fn get_view_angle(&self) -> f64; + + fn mac_x(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dc_view(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dc_pitch(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dc_roll(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dscale(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dk0(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dk1(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_x_dx0(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dc_view(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dc_pitch(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dc_roll(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dscale(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dk0(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dk1(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + fn mac_y_dx0(&self, a: ProjArgs) -> f64 { + let _ = a; + NAN + } + + /// Project azimuth/elevation to image coordinates (relative to image center), + /// normalizing azimuth around the view center like the C++ original. + fn get_coordinates(&self, alph: f64, elev: f64, parms: &ViewParams) -> (f64, f64) { + let mut alph = alph; + let pi = crate::hill::PI; + + if alph - parms.a_center > pi { + alph -= 2.0 * pi; + } else if alph - parms.a_center < -pi { + alph += 2.0 * pi; + } + + let args = ProjArgs::from_params(parms, alph, elev); + (self.mac_x(args), self.mac_y(args)) + } +} + +pub fn projection_from_type(t: ProjectionType) -> Box<dyn Projection> { + match t { + ProjectionType::Rectilinear => Box::new(rectilinear::ProjectionRectilinear), + ProjectionType::Cylindrical => Box::new(cylindrical::ProjectionCylindrical), + } +} diff --git a/crates/gipfel-core/src/projection/rectilinear.rs b/crates/gipfel-core/src/projection/rectilinear.rs new file mode 100644 index 0000000..a7037d6 --- /dev/null +++ b/crates/gipfel-core/src/projection/rectilinear.rs @@ -0,0 +1,104 @@ +//! Rectilinear (gnomonic) projection with lens distortion. + +mod funcs { + #![allow(non_snake_case, unused_parens, unused_variables, dead_code, clippy::all)] + + // math shims: the maxima-generated code uses C-style calls + #[inline(always)] + fn sin(x: f64) -> f64 { + x.sin() + } + #[inline(always)] + fn cos(x: f64) -> f64 { + x.cos() + } + #[inline(always)] + fn tan(x: f64) -> f64 { + x.tan() + } + #[inline(always)] + fn sqrt(x: f64) -> f64 { + x.sqrt() + } + #[inline(always)] + fn pow(x: f64, y: f64) -> f64 { + x.powf(y) + } + #[inline(always)] + fn exp(x: f64) -> f64 { + x.exp() + } + #[inline(always)] + fn fabs(x: f64) -> f64 { + x.abs() + } + #[inline(always)] + fn atan2(y: f64, x: f64) -> f64 { + y.atan2(x) + } + #[inline(always)] + fn sec(x: f64) -> f64 { + 1.0 / x.cos() + } + + include!("generated/rectilinear_funcs.rs"); +} + +use super::{ProjArgs, Projection}; + +pub struct ProjectionRectilinear; + +impl Projection for ProjectionRectilinear { + fn get_view_angle(&self) -> f64 { + 1.0471976 /* 60 deg */ + } + + fn mac_x(&self, a: ProjArgs) -> f64 { + funcs::mac_x(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y(&self, a: ProjArgs) -> f64 { + funcs::mac_y(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_view(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_view(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_pitch(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_pitch(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dc_roll(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dc_roll(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dscale(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dscale(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dk0(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dk0(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dk1(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dk1(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_x_dx0(&self, a: ProjArgs) -> f64 { + funcs::mac_x_dx0(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_view(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_view(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_pitch(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_pitch(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dc_roll(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dc_roll(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dscale(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dscale(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dk0(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dk0(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dk1(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dk1(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } + fn mac_y_dx0(&self, a: ProjArgs) -> f64 { + funcs::mac_y_dx0(a.c_view, a.c_pitch, a.c_roll, a.scale, a.k0, a.k1, a.x0, a.m_view, a.m_elev) + } +} diff --git a/crates/gipfel-core/src/stitch.rs b/crates/gipfel-core/src/stitch.rs new file mode 100644 index 0000000..a311961 --- /dev/null +++ b/crates/gipfel-core/src/stitch.rs @@ -0,0 +1,323 @@ +//! Cylindrical panorama resampling — port of the C++ `Stitch.cxx` / +//! `ScanImage.cxx` stitch mode. +//! +//! The output is a cylindrical (equirectangular-in-azimuth, tan-in-elevation) +//! image spanning `view_start..view_end` degrees of azimuth. Each output +//! pixel is mapped back through every source image's fitted projection; the +//! first source that covers the direction contributes its (resampled) color. +//! +//! All sampling arithmetic reproduces the C implementation bit-for-bit: +//! colors are handled on the 0..=65025 scale (`value * 255`), nearest uses +//! `rint` (round-half-to-even), bicubic uses the same Keys cubic and only +//! rounds after the final vertical interpolation. + +use crate::metadata::{ImageMetaData, apply_image_metadata}; +use crate::panorama::Panorama; +use image::RgbImage; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum ScanMode { + Nearest, + Bicubic, +} + +/// C MAX_VALUE: clamp bound on the 0..=65025 (value*255) scale. +pub const MAX_VALUE: i64 = 65025; + +fn pixel(img: &RgbImage, x: i64, y: i64) -> Option<[i64; 3]> { + if x < 0 || y < 0 || x >= img.width() as i64 || y >= img.height() as i64 { + return None; + } + let p = img.get_pixel(x as u32, y as u32); + Some([p[0] as i64 * 255, p[1] as i64 * 255, p[2] as i64 * 255]) +} + +/// C ScanImage::get_pixel_nearest: NaN-guard, then round to nearest pixel. +fn get_pixel_nearest(img: &RgbImage, x: f64, y: f64) -> Option<[i64; 3]> { + if x.is_nan() || y.is_nan() { + return None; + } + pixel(img, x.round_ties_even() as i64, y.round_ties_even() as i64) +} + +/// C ScanImage interp_cubic (Keys cubic convolution kernel, evaluated in +/// Bernstein-like power basis). +#[inline] +fn interp_cubic(x: f64, x2: f64, x3: f64, v: &[f64; 4]) -> f64 { + let a0 = v[3] - v[2] - v[0] + v[1]; + let a1 = v[0] - v[1] - a0; + let a2 = v[2] - v[0]; + let a3 = v[1]; + a0 * x3 + a1 * x2 + a2 * x + a3 +} + +/// C ScanImage::get_pixel_bicubic. Any of the 16 taps outside the source +/// makes the pixel unset (C returns 1), matching the "leave black" behavior. +fn get_pixel_bicubic(img: &RgbImage, x: f64, y: f64) -> Option<[i64; 3]> { + if x.is_nan() || y.is_nan() { + return None; + } + let fl_x = x.floor(); + let fl_y = y.floor(); + let dx = x - fl_x; + let dx2 = dx * dx; + let dx3 = dx2 * dx; + let dy = y - fl_y; + let dy2 = dy * dy; + let dy3 = dy2 * dy; + + let mut c1 = [[0f64; 4]; 3]; + for iy in 0..4usize { + let mut c = [[0f64; 4]; 3]; + for ix in 0..4usize { + let p = pixel(img, fl_x as i64 + ix as i64 - 1, fl_y as i64 + iy as i64 - 1)?; + for l in 0..3 { + c[l][ix] = p[l] as f64; + } + } + for l in 0..3 { + c1[l][iy] = interp_cubic(dx, dx2, dx3, &c[l]); + } + } + Some([ + interp_cubic(dy, dy2, dy3, &c1[0]).round_ties_even() as i64, + interp_cubic(dy, dy2, dy3, &c1[1]).round_ties_even() as i64, + interp_cubic(dy, dy2, dy3, &c1[2]).round_ties_even() as i64, + ]) +} + +/// C ScanImage::get_pixel(mode, x, y). Values on the 0..=65025 scale; +/// bicubic may overshoot (clamped by the caller, as in C). +pub fn get_pixel(img: &RgbImage, mode: ScanMode, x: f64, y: f64) -> Option<[i64; 3]> { + match mode { + ScanMode::Nearest => get_pixel_nearest(img, x, y), + ScanMode::Bicubic => get_pixel_bicubic(img, x, y), + } +} + +/// One input photo: its fitted projection plus its decoded pixels. +pub struct StitchSource { + pub pan: Panorama, + pub img: RgbImage, +} + +impl StitchSource { + /// Load a JPEG and apply its gipfel metadata (comment or EXIF), like + /// `GipfelWidget::load_image` does for the stitch mode. + pub fn load(path: &str) -> Result<Self, String> { + let img = image::open(path) + .map(|i| i.to_rgb8()) + .map_err(|e| format!("{}: {e}", path))?; + let mut md = ImageMetaData::new(); + md.load_image(path); + let mut pan = Panorama::new(); + apply_image_metadata(&mut pan, &md, img.width(), img.height()); + Ok(Self { pan, img }) + } + + /// C GipfelWidget::get_pixel: forward-project (azimuth, elevation) + /// through this image's projection and sample the source. + pub fn get_pixel(&self, mode: ScanMode, a_alph: f64, a_elev: f64) -> Option<[i64; 3]> { + let (px, py) = self.pan.project_point(a_alph, a_elev)?; + get_pixel( + &self.img, + mode, + px + self.img.width() as f64 / 2.0, + py + self.img.height() as f64 / 2.0, + ) + } +} + +/// The resampled cylindrical panorama. Pixel values are RGB triplets on the +/// C 0..=65025 scale (clamped); `hit` marks pixels some source contributed +/// to (unset pixels are black in the C output). +pub struct StitchedImage { + pub w: u32, + pub h: u32, + pub pixels: Vec<[u16; 3]>, + pub hit: Vec<bool>, +} + +/// Port of `Stitch::resample`: output column x maps linearly to azimuth, +/// output row y maps to elevation via `atan((y_off - y) / radius)`. +pub fn resample( + sources: &[StitchSource], + mode: ScanMode, + w: u32, + h: u32, + view_start_deg: f64, + view_end_deg: f64, +) -> StitchedImage { + let view_start = view_start_deg.to_radians(); + let view_end = view_end_deg.to_radians(); + let mut pixels = vec![[0u16; 3]; (w as usize) * (h as usize)]; + let mut hit = vec![false; (w as usize) * (h as usize)]; + if !(view_end > view_start) || w == 0 || h == 0 { + return StitchedImage { + w, + h, + pixels, + hit, + }; + } + let step_view = (view_end - view_start) / w as f64; + let y_off = (h / 2) as f64; + let radius = w as f64 / (view_end - view_start); + + for y in 0..h { + let a_elev = ((y_off - y as f64) / radius).atan(); + for x in 0..w { + let a_view = view_start + x as f64 * step_view; + for src in sources { + let Some(rgb) = src.get_pixel(mode, a_view, a_elev) else { + continue; + }; + let clamped = [ + rgb[0].clamp(0, MAX_VALUE) as u16, + rgb[1].clamp(0, MAX_VALUE) as u16, + rgb[2].clamp(0, MAX_VALUE) as u16, + ]; + let i = (y as usize) * (w as usize) + (x as usize); + pixels[i] = clamped; + hit[i] = true; + break; // C: first contributing image wins + } + } + } + + StitchedImage { + w, + h, + pixels, + hit, + } +} + +/// Write an RGBA TIFF like the C `TIFFOutputImage`: 8 bit (value/255, alpha +/// 255) or 16 bit (raw 0..=65025, alpha 65025), deflate-compressed. +pub fn write_tiff(path: &str, img: &StitchedImage, bits16: bool) -> Result<(), String> { + use tiff::encoder::colortype::{RGBA16, RGBA8}; + use tiff::encoder::{Compression, TiffEncoder}; + + let n = (img.w as usize) * (img.h as usize); + let file = std::fs::File::create(path).map_err(|e| format!("{}: {e}", path))?; + let mut enc = TiffEncoder::new(file) + .map_err(|e| format!("{}: {e}", path))? + .with_compression(Compression::Deflate(Default::default())); + if bits16 { + let mut buf: Vec<u16> = Vec::with_capacity(n * 4); + for p in &img.pixels { + buf.extend_from_slice(&[p[0], p[1], p[2], MAX_VALUE as u16]); + } + enc.write_image::<RGBA16>(img.w, img.h, &buf) + .map_err(|e| format!("{}: {e}", path))?; + } else { + let mut buf: Vec<u8> = Vec::with_capacity(n * 4); + for p in &img.pixels { + buf.extend_from_slice(&[(p[0] / 255) as u8, (p[1] / 255) as u8, (p[2] / 255) as u8, 255]); + } + enc.write_image::<RGBA8>(img.w, img.h, &buf) + .map_err(|e| format!("{}: {e}", path))?; + } + Ok(()) +} + +/// Write a JPEG (quality 90, like the C `JPEGOutputImage`). +pub fn write_jpeg(path: &str, img: &StitchedImage, quality: u8) -> Result<(), String> { + use image::codecs::jpeg::JpegEncoder; + use image::ImageEncoder; + + let rgb: Vec<u8> = img + .pixels + .iter() + .flat_map(|p| [(p[0] / 255) as u8, (p[1] / 255) as u8, (p[2] / 255) as u8]) + .collect(); + let enc = JpegEncoder::new_with_quality( + std::io::BufWriter::new(std::fs::File::create(path).map_err(|e| format!("{}: {e}", path))?), + quality, + ); + enc.write_image(&rgb, img.w, img.h, image::ExtendedColorType::Rgb8) + .map_err(|e| format!("{}: {e}", path))?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn solid(v: u8, w: u32, h: u32) -> RgbImage { + RgbImage::from_pixel(w, h, image::Rgb([v, v, v])) + } + + #[test] + fn nearest_rounds_half_to_even() { + let img = solid(100, 4, 4); + // 1.5 -> 2 (ties to even), 2.5 -> 2 + assert_eq!(get_pixel_nearest(&img, 1.5, 0.0), Some([100 * 255; 3])); + assert_eq!(get_pixel_nearest(&img, 2.5, 0.0), Some([100 * 255; 3])); + assert_eq!(get_pixel_nearest(&img, -0.4, 0.0), Some([100 * 255; 3])); + assert_eq!(get_pixel_nearest(&img, -0.6, 0.0), None); + assert_eq!(get_pixel_nearest(&img, f64::NAN, 0.0), None); + } + + #[test] + fn bicubic_constant_and_bounds() { + let img = solid(100, 4, 4); + // constant image: cubic reproduces the constant exactly + assert_eq!(get_pixel_bicubic(&img, 1.3, 1.7), Some([100 * 255; 3])); + // any tap outside the 4x4 neighborhood -> unset + assert_eq!(get_pixel_bicubic(&img, 0.0, 2.0), None); // fl_x-1 = -1 + assert_eq!(get_pixel_bicubic(&img, 2.0, 3.5), None); // fl_y+2 = 5 + assert_eq!(get_pixel_bicubic(&img, 1.0, 1.0), Some([100 * 255; 3])); + } + + #[test] + fn bicubic_matches_keys_cubic_on_ramp() { + // Keys cubic (a=-1/2): on a linear ramp the kernel yields + // a + b*(2 dx^3 - 3 dx^2 + 2 dx), not the linear value. + let mut img = RgbImage::new(8, 8); + for x in 0..8 { + for y in 0..8 { + img.put_pixel(x, y, image::Rgb([(x as u8) * 30, 0, 0])); + } + } + let got = get_pixel_bicubic(&img, 3.25, 4.0).unwrap(); + let a = 3.0 * 30.0 * 255.0; + let b = 30.0 * 255.0; + let dx = 0.25f64; + let want = (a + b * (2.0 * dx * dx * dx - 3.0 * dx * dx + 2.0 * dx)).round_ties_even(); + assert_eq!(got[0], want as i64); + } + + #[test] + fn bicubic_exact_at_integer_positions() { + // at integer sample coordinates (dx = dy = 0) the cubic kernel + // reproduces the center tap exactly -> a 1:1 resample is lossless + let mut img = RgbImage::new(8, 8); + for x in 0..8 { + for y in 0..8 { + img.put_pixel(x, y, image::Rgb([(x * 7 + y * 3) as u8, (y * 5) as u8, 9])); + } + } + for y in 1..6 { + for x in 1..6 { + let p = img.get_pixel(x, y).0; + assert_eq!( + get_pixel_bicubic(&img, x as f64, y as f64).unwrap(), + [p[0] as i64 * 255, p[1] as i64 * 255, p[2] as i64 * 255] + ); + } + } + } + + #[test] + fn resample_rejects_empty_range() { + let img = StitchSource { + pan: Panorama::new(), + img: solid(50, 8, 8), + }; + let out = resample(&[img], ScanMode::Nearest, 4, 4, 10.0, 10.0); + assert_eq!(out.hit.len(), 16); + assert!(out.hit.iter().all(|h| !h)); + } +} diff --git a/crates/gipfel-core/src/track.rs b/crates/gipfel-core/src/track.rs new file mode 100644 index 0000000..5450aa8 --- /dev/null +++ b/crates/gipfel-core/src/track.rs @@ -0,0 +1,248 @@ +//! GPS track loading (GPX). +//! +//! Minimal streaming parser built directly on quick-xml: we only need the +//! `trkpt` positions, and real-world files (Garmin etc.) are full of vendor +//! `<extensions>` blocks that stricter parsers choke on. Waypoints and +//! routes are ignored; only track points are read. +//! +//! Security posture for untrusted files: +//! - quick-xml is used with `default-features = false` (no serde, no encoding +//! detection); it never fetches external DTDs/entities and expands only the +//! five predefined XML entities, so XXE and entity-expansion ("billion +//! laughs") attacks are structurally impossible; unknown entity references +//! make the attribute fail to parse and the point is skipped +//! - input size is capped (MAX_GPX_BYTES) before anything is parsed +//! - the parser only reads lat/lon attributes and <ele> text; all other +//! elements (including vendor extensions) are ignored without being +//! interpreted + +use crate::hill::{Hill, FLAG_TRACK_POINT}; +use quick_xml::events::Event; + +/// Reject absurdly large track files (a 64 MiB GPX is already ~500k points). +pub const MAX_GPX_BYTES: u64 = 64 * 1024 * 1024; + +/// Load track points from a GPX file (all tracks/segments, in file order). +/// +/// Consecutive duplicate positions are dropped; the result is decimated with +/// a uniform stride to at most `max_points` (GPX logs easily reach 10k +/// points, which would bloat the panorama's hill list). +pub fn load_gpx(path: &str, max_points: usize) -> Result<Vec<Hill>, String> { + let size = std::fs::metadata(path).map_err(|e| format!("{path}: {e}"))?; + if size.len() > MAX_GPX_BYTES { + return Err(format!("{path}: file too large ({} bytes)", size.len())); + } + let data = std::fs::read_to_string(path).map_err(|e| format!("{path}: {e}"))?; + let mut reader = quick_xml::Reader::from_str(&data); + reader.config_mut().trim_text(true); + + let mut pts: Vec<(f64, f64, f64)> = Vec::new(); // (lat, lon, ele) degrees + let mut depth: usize = 0; + let mut trkpt_depth: Option<usize> = None; + let mut in_ele = false; + let (mut lat, mut lon, mut ele) = (f64::NAN, f64::NAN, 0.0); + + fn push(pts: &mut Vec<(f64, f64, f64)>, lat: f64, lon: f64, ele: f64) { + if lat.is_nan() || lon.is_nan() { + return; + } + if let Some(&(lata, lona, _)) = pts.last() { + if lata == lat && lona == lon { + return; // consecutive duplicate fix + } + } + pts.push((lat, lon, ele)); + } + + loop { + match reader.read_event().map_err(|e| format!("{path}: {e}"))? { + Event::Eof => break, + Event::Empty(e) => { + if e.local_name().as_ref() == b"trkpt" { + let (la, lo) = read_latlon(&e); + lat = la; + lon = lo; + ele = 0.0; + push(&mut pts, lat, lon, ele); + } + } + Event::Start(e) => { + depth += 1; + match e.local_name().as_ref() { + b"trkpt" => { + trkpt_depth = Some(depth); + ele = 0.0; + let (la, lo) = read_latlon(&e); + lat = la; + lon = lo; + } + // accept <ele> only as a direct child of <trkpt>, so + // elevation-like tags inside vendor extensions are ignored + b"ele" => { + in_ele = trkpt_depth == Some(depth - 1); + if in_ele { + ele = 0.0; + } + } + _ => {} + } + } + Event::Text(t) => { + if in_ele { + if let Ok(s) = t.decode() { + if let Ok(v) = s.trim().parse() { + ele = v; + } + } + } + } + Event::End(e) => { + match e.local_name().as_ref() { + b"ele" => in_ele = false, + b"trkpt" => { + push(&mut pts, lat, lon, ele); + trkpt_depth = None; + } + _ => {} + } + depth = depth.saturating_sub(1); + } + _ => {} + } + } + + if pts.is_empty() { + return Err(format!("{path}: no track points")); + } + + let stride = pts.len().div_ceil(max_points.max(1)); + Ok(pts + .iter() + .step_by(stride) + .map(|&(lat, lon, ele)| { + let mut h = Hill::new("track", lat.to_radians(), lon.to_radians(), ele); + h.flags |= FLAG_TRACK_POINT; + h + }) + .collect()) +} + +fn read_latlon(e: &quick_xml::events::BytesStart) -> (f64, f64) { + let (mut lat, mut lon) = (f64::NAN, f64::NAN); + for a in e.attributes().flatten() { + if let Ok(v) = a.normalized_value(quick_xml::XmlVersion::Implicit1_0) { + match a.key.local_name().as_ref() { + b"lat" => lat = v.trim().parse().unwrap_or(f64::NAN), + b"lon" => lon = v.trim().parse().unwrap_or(f64::NAN), + _ => {} + } + } + } + (lat, lon) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn tmp(name: &str, content: &str) -> std::path::PathBuf { + let dir = std::env::temp_dir().join(format!("gipfel-gpx-test-{name}")); + std::fs::create_dir_all(&dir).unwrap(); + let p = dir.join("t.gpx"); + std::fs::write(&p, content).unwrap(); + p + } + + #[test] + fn gpx_track_roundtrip() { + let gpx = r#"<?xml version="1.0" encoding="UTF-8"?> +<gpx version="1.1" creator="test" xmlns="http://www.topografix.com/GPX/1/1"> + <trk><name>tour</name><trkseg> + <trkpt lat="47.30" lon="11.10"><ele>1000</ele></trkpt> + <trkpt lat="47.31" lon="11.11"><ele>1100</ele></trkpt> + <trkpt lat="47.31" lon="11.11"><ele>1100</ele></trkpt> + <trkpt lat="47.32" lon="11.12"><ele>1200</ele></trkpt> + </trkseg></trk> +</gpx>"#; + let p = tmp("rt", gpx); + let hills = load_gpx(p.to_str().unwrap(), 100).unwrap(); + assert_eq!(hills.len(), 3); // duplicate dropped + assert!(hills.iter().all(|h| h.is_track_point())); + assert!((hills[1].phi - 47.31f64.to_radians()).abs() < 1e-12); + assert!((hills[2].height - 1200.0).abs() < 1e-9); + } + + #[test] + fn gpx_vendor_extensions_ignored() { + // Garmin-style files are full of vendor extension blocks; they must + // not confuse the parser (and must not contribute elevation). + let gpx = r#"<?xml version="1.0" encoding="utf-8"?> +<gpx creator="Garmin Desktop App" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" + xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"> + <metadata><extensions><gpxx:TrackPointExtension xmlns:gpxx="x" version="1.0"/></extensions></metadata> + <trk><name>T</name><extensions><gpxx2:x xmlns:gpxx2="y"><ele>9999</ele></gpxx2:x></extensions> + <trkseg> + <trkpt lat="47.30" lon="11.10"> + <ele>1000</ele> + <extensions><gpxtpx:TrackPointExtension><gpxtpx:hr>120</gpxtpx:hr></gpxtpx:TrackPointExtension></extensions> + </trkpt> + <trkpt lat="47.31" lon="11.11"><ele>1100</ele></trkpt> + </trkseg> + </trk> +</gpx>"#; + let p = tmp("ext", gpx); + let hills = load_gpx(p.to_str().unwrap(), 100).unwrap(); + assert_eq!(hills.len(), 2); + assert!((hills[0].height - 1000.0).abs() < 1e-9); // not 9999 + } + + #[test] + fn gpx_decimation() { + let mut pts = String::new(); + for i in 0..1000 { + pts.push_str(&format!( + "<trkpt lat=\"{}\" lon=\"{}\"><ele>1</ele></trkpt>", + 47.0 + i as f64 * 1e-5, + 11.0 + i as f64 * 1e-5 + )); + } + let gpx = format!( + r#"<?xml version="1.0"?><gpx version="1.1" creator="t" xmlns="http://www.topografix.com/GPX/1/1"><trk><trkseg>{pts}</trkseg></trk></gpx>"# + ); + let p = tmp("dec", &gpx); + let hills = load_gpx(p.to_str().unwrap(), 100).unwrap(); + assert!(hills.len() <= 100); + } + + #[test] + fn gpx_bom_is_tolerated() { + // Garmin Desktop App writes a UTF-8 BOM; it must not break parsing. + let gpx = format!( + "\u{feff}<?xml version=\"1.0\"?><gpx version=\"1.1\" creator=\"t\" xmlns=\"http://www.topografix.com/GPX/1/1\"><trk><trkseg><trkpt lat=\"47.30\" lon=\"11.10\"/></trkseg></trk></gpx>" + ); + let p = tmp("bom", &gpx); + let hills = load_gpx(p.to_str().unwrap(), 100).unwrap(); + assert_eq!(hills.len(), 1); + } + + #[test] + fn gpx_entity_injection_is_inert() { + // XXE / entity-expansion attempts: quick-xml only expands the five + // predefined entities; unknown references make the attribute fail to + // parse, so the point is simply skipped (never expanded, never fetched). + let gpx = r#"<?xml version="1.0"?> +<!DOCTYPE gpx [ <!ENTITY evil "47.30"> <!ENTITY sys SYSTEM "file:///etc/passwd"> ]> +<gpx version="1.1" creator="t" xmlns="http://www.topografix.com/GPX/1/1"> + <trk><trkseg> + <trkpt lat="&evil;" lon="11.10"><ele>1000</ele></trkpt> + <trkpt lat="47.31" lon="&sys;"><ele>1100</ele></trkpt> + <trkpt lat="47.32" lon="11.12"><ele>1200</ele></trkpt> + </trkseg></trk> +</gpx>"#; + let p = tmp("ent", gpx); + let hills = load_gpx(p.to_str().unwrap(), 100).unwrap(); + // only the clean point survives; the entity-bearing ones are skipped + assert_eq!(hills.len(), 1); + assert!((hills[0].phi - 47.32f64.to_radians()).abs() < 1e-12); + } +} diff --git a/crates/gipfel-core/tests/golden.rs b/crates/gipfel-core/tests/golden.rs new file mode 100644 index 0000000..fb12e40 --- /dev/null +++ b/crates/gipfel-core/tests/golden.rs @@ -0,0 +1,511 @@ +//! Golden-value tests against the original C++ implementation. +//! +//! Goldens are generated by `tests/gen_golden.sh` (needs the built C++ tree). +//! Run with `cargo test --test golden` (unit tests: `cargo test --lib`). + +use gipfel_core::hill; +use gipfel_core::metadata::ImageMetaData; +use gipfel_core::panorama::Panorama; +use gipfel_core::projection::{ + cylindrical::ProjectionCylindrical, rectilinear::ProjectionRectilinear, ProjArgs, Projection, ProjectionType, +}; + +fn golden(name: &str) -> Vec<String> { + let path = format!( + "{}/../../tests/golden/{}", + env!("CARGO_MANIFEST_DIR"), + name + ); + std::fs::read_to_string(&path) + .unwrap_or_else(|_| panic!("missing golden file {} (run tests/gen_golden.sh)", path)) + .lines() + .map(|l| l.to_string()) + .collect() +} + +fn data_path() -> Option<String> { + if let Ok(p) = std::env::var("GIPFEL_DATA") { + if std::path::Path::new(&p).exists() { + return Some(p); + } + } + let home = std::env::var("HOME").unwrap_or_default(); + let p = format!("{}/gipfel/gipfel.dat", home); + std::path::Path::new(&p).exists().then_some(p) +} + +/// relative-or-absolute closeness (libm differences between glibc and Rust) +fn close(a: f64, b: f64) -> bool { + if a.is_nan() && b.is_nan() { + return true; + } + if a.is_infinite() || b.is_infinite() { + return a == b; + } + (a - b).abs() <= 1e-9f64.max(a.abs() * 1e-9) +} + +#[test] +fn generated_functions_match_c() { + let golden = golden("funcs.txt"); + let rect = ProjectionRectilinear; + let cyl = ProjectionCylindrical; + + assert_eq!(golden.len(), 200); + + for (k, line) in golden.iter().enumerate() { + let kk = k as f64; + let c_view = -3.0 + 0.03 * kk; + let c_pitch = 0.10 * (0.11 * kk).sin(); + let c_roll = 0.05 * (0.07 * kk).cos(); + let scale = 2000.0 + 37.0 * (kk % 19.0); + let k0 = 0.001 * (0.31 * kk).sin(); + let k1 = 0.0001 * (0.17 * kk).cos(); + let x0 = 50.0 * (0.13 * kk).sin(); + let m_view = c_view + 0.5 * (0.23 * kk).sin(); + let m_elev = 0.05 * (0.19 * kk).cos(); + + let a = ProjArgs { c_view, c_pitch, c_roll, scale, k0, k1, x0, m_view, m_elev }; + + let mine: Vec<f64> = vec![ + rect.mac_x(a), + rect.mac_y(a), + rect.mac_x_dc_view(a), + rect.mac_x_dc_pitch(a), + rect.mac_x_dc_roll(a), + rect.mac_x_dscale(a), + rect.mac_x_dk0(a), + rect.mac_x_dk1(a), + rect.mac_x_dx0(a), + rect.mac_y_dc_view(a), + rect.mac_y_dc_pitch(a), + rect.mac_y_dc_roll(a), + rect.mac_y_dscale(a), + rect.mac_y_dk0(a), + rect.mac_y_dk1(a), + rect.mac_y_dx0(a), + cyl.mac_x(a), + cyl.mac_y(a), + cyl.mac_x_dc_view(a), + cyl.mac_x_dc_pitch(a), + cyl.mac_x_dc_roll(a), + cyl.mac_x_dscale(a), + cyl.mac_y_dc_view(a), + cyl.mac_y_dc_pitch(a), + cyl.mac_y_dc_roll(a), + cyl.mac_y_dscale(a), + ]; + + let theirs: Vec<f64> = line.split_whitespace().map(|t| t.parse().unwrap()).collect(); + assert_eq!(mine.len(), theirs.len(), "line {}", k); + + for (i, (m, t)) in mine.iter().zip(theirs.iter()).enumerate() { + assert!(close(*m, *t), "k={} fn#{}: rust {} != c {}", k, i, m, t); + } + } +} + +/// derivatives of the generated code vs. central finite differences +#[test] +fn derivatives_match_finite_differences() { + let rect = ProjectionRectilinear; + let base = ProjArgs { + c_view: 0.3, + c_pitch: 0.05, + c_roll: 0.02, + scale: 3000.0, + k0: 0.001, + k1: 0.0002, + x0: 12.0, + m_view: 0.5, + m_elev: 0.04, + }; + + let h = 1e-6f64; + + // (arg index, analytic derivative fn) + let dx: [(usize, &dyn Fn(ProjArgs) -> f64); 7] = [ + (0, &|a| rect.mac_x_dc_view(a)), + (1, &|a| rect.mac_x_dc_pitch(a)), + (2, &|a| rect.mac_x_dc_roll(a)), + (3, &|a| rect.mac_x_dscale(a)), + (4, &|a| rect.mac_x_dk0(a)), + (5, &|a| rect.mac_x_dk1(a)), + (6, &|a| rect.mac_x_dx0(a)), + ]; + let dy: [(usize, &dyn Fn(ProjArgs) -> f64); 7] = [ + (0, &|a| rect.mac_y_dc_view(a)), + (1, &|a| rect.mac_y_dc_pitch(a)), + (2, &|a| rect.mac_y_dc_roll(a)), + (3, &|a| rect.mac_y_dscale(a)), + (4, &|a| rect.mac_y_dk0(a)), + (5, &|a| rect.mac_y_dk1(a)), + (6, &|a| rect.mac_y_dx0(a)), + ]; + + let set = |a: ProjArgs, idx: usize, v: f64| -> ProjArgs { + let mut b = a; + match idx { + 0 => b.c_view = v, + 1 => b.c_pitch = v, + 2 => b.c_roll = v, + 3 => b.scale = v, + 4 => b.k0 = v, + 5 => b.k1 = v, + _ => b.x0 = v, + } + b + }; + + for (idx, f) in dx { + let plus = rect.mac_x(set(base, idx, base_val(&base, idx) + h)); + let minus = rect.mac_x(set(base, idx, base_val(&base, idx) - h)); + let fd = (plus - minus) / (2.0 * h); + let an = f(base); + assert!( + (an - fd).abs() <= 1e-4f64.max(an.abs() * 1e-5), + "dx deriv arg {}: analytic {} finite-diff {}", + idx, + an, + fd + ); + } + for (idx, f) in dy { + let plus = rect.mac_y(set(base, idx, base_val(&base, idx) + h)); + let minus = rect.mac_y(set(base, idx, base_val(&base, idx) - h)); + let fd = (plus - minus) / (2.0 * h); + let an = f(base); + assert!( + (an - fd).abs() <= 1e-4f64.max(an.abs() * 1e-5), + "dy deriv arg {}: analytic {} finite-diff {}", + idx, + an, + fd + ); + } +} + +fn base_val(a: &ProjArgs, idx: usize) -> f64 { + match idx { + 0 => a.c_view, + 1 => a.c_pitch, + 2 => a.c_roll, + 3 => a.scale, + 4 => a.k0, + 5 => a.k1, + _ => a.x0, + } +} + +/// full fit (geodesy + projection + GSL LM) against the C++ fitdump +fn fit_golden(projtype: ProjectionType, file: &str) { + let Some(data) = data_path() else { + eprintln!("gipfel.dat not found, skipping"); + return; + }; + let golden = golden(file); + + let mut pan = Panorama::new(); + pan.load_data(&data).unwrap(); + pan.set_projection(projtype); + + pan.set_view_lat(47.36); + pan.set_view_long(11.15); + pan.set_view_height(2100.0); + + // ground truth + pan.set_center_angle(20.0); + pan.set_pitch_angle(0.0); + pan.set_roll_angle(1.0); + pan.set_scale(3000.0); + + // snapshot first 6 visible hills as observations + let mut obs: Vec<hill::Hill> = pan.visible[..6].iter().map(|&i| pan.hills[i].clone()).collect(); + let n = obs.len(); + let known: Vec<usize> = (0..n).collect(); + // the C harness copies a_elev explicitly (C copy ctor omits it) + for (k, &i) in pan.visible[..6].iter().enumerate() { + obs[k].a_elev = pan.hills[i].a_elev; + } + + // perturb initial guess + pan.set_center_angle(pan.get_center_angle() + 0.5); + pan.set_pitch_angle(0.3); + pan.set_roll_angle(0.5); + pan.set_scale(2800.0); + + let ret = pan.comp_params_with(&obs, &known, true); + + let g_ret: i32 = golden[0]["ret ".len()..].parse().unwrap(); + assert_eq!(ret.is_ok(), g_ret == 0); + + let g: Vec<f64> = golden[1..8] + .iter() + .map(|l| l.split_whitespace().nth(1).unwrap().parse().unwrap()) + .collect(); + + let mine = [ + pan.parms.a_center, + pan.parms.a_pitch, + pan.parms.a_roll, + pan.parms.scale, + pan.parms.k0, + pan.parms.k1, + pan.parms.x0, + ]; + for (m, t) in mine.iter().zip(g.iter()) { + // solver results: allow small drift, exact zeros must stay ~zero + assert!( + (m - t).abs() <= 1e-6f64.max(m.abs() * 1e-6), + "fit param {} vs {}", + m, + t + ); + } + + // final coordinates + let proj = gipfel_core::projection::projection_from_type(projtype); + for line in &golden[8..] { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() != 4 { + continue; + } + let i: usize = parts[1].parse().unwrap(); + let tx: f64 = parts[2].parse().unwrap(); + let ty: f64 = parts[3].parse().unwrap(); + let (x, y) = proj.get_coordinates(obs[i].alph, obs[i].a_elev, &pan.parms); + assert!(close(x, tx), "hill {} x: {} vs {}", i, x, tx); + assert!(close(y, ty), "hill {} y: {} vs {}", i, y, ty); + } +} + +#[test] +fn fit_rectilinear_matches_c() { + fit_golden(ProjectionType::Rectilinear, "fit_rect.txt"); +} + +#[test] +fn fit_cylindrical_matches_c() { + fit_golden(ProjectionType::Cylindrical, "fit_cyl.txt"); +} + +#[test] +fn real_database_loads() { + let Some(data) = data_path() else { + eprintln!("gipfel.dat not found, skipping"); + return; + }; + let hills = hill::load(&data).unwrap(); + assert!(hills.len() > 21000, "loaded {}", hills.len()); + assert!(hills.iter().any(|h| h.name == "ZUGSPITZE")); +} + +#[test] +fn viewpoint_geometry() { + let Some(data) = data_path() else { + eprintln!("gipfel.dat not found, skipping"); + return; + }; + let mut pan = Panorama::new(); + pan.load_data(&data).unwrap(); + let idx = pan.get_pos("ZUGSPITZE").expect("ZUGSPITZE in db"); + pan.set_viewpoint(Some(idx)); + + // NOTE: set_viewpoint re-sorts hills by azimuth; look the name up again + let idx = pan.get_pos("ZUGSPITZE").unwrap(); + let m = &pan.hills[idx]; + assert!((m.height - 2963.0).abs() < 1.0); + // distance to self is zero + assert!(pan.distance(m.phi, m.lam).abs() < 1e-9); + // a far hill: Mont Blanc (~45.83 N, 6.86 E) + let mb = pan.get_pos("MONT BLANC").expect("MONT BLANC in db"); + let d = pan.distance(pan.hills[mb].phi, pan.hills[mb].lam); + let km = d * 6371000.785 / 1000.0; + assert!(km > 340.0 && km < 380.0, "Mont Blanc distance {} km", km); +} + +#[test] +fn export_hills_matches_c() { + let Some(data) = data_path() else { + eprintln!("gipfel.dat not found, skipping"); + return; + }; + let jpg = format!( + "{}/../../tests/golden/test.jpg", + env!("CARGO_MANIFEST_DIR") + ); + let golden = golden("export.txt"); + + let (w, h) = gipfel_core::metadata::image_dimensions(&jpg).expect("test.jpg readable"); + let mut md = ImageMetaData::new(); + md.load_image(&jpg); + + let mut pan = Panorama::new(); + assert!(gipfel_core::metadata::apply_image_metadata(&mut pan, &md, w, h)); + pan.load_data(&data).unwrap(); + pan.set_height_dist_ratio(0.07); + + let mut out = vec![ + "#".to_string(), + "# name\theight\tx\ty\tdistance\tflags".to_string(), + "#".to_string(), + ]; + out.extend(pan.export_hill_rows(w as i64, h as i64, false)); + + assert_eq!(out.len(), golden.len()); + for (a, b) in out.iter().zip(golden.iter()) { + assert_eq!(a, b); + } +} + +#[test] +fn metadata_comment_roundtrip_and_c_format() { + let mut md = ImageMetaData::new(); + md.longitude = 11.1533; + md.latitude = 47.3601; + md.height = 2100.5; + md.direction = 20.0; + md.pitch = 0.3; + md.roll = 1.0; + md.focal_length_35mm = 35.0; + md.projection_type = 1; + md.k0 = 0.001; + md.k1 = -0.0002; + md.x0 = 12.0; + + let s = md.format_comment(); + // exact C printf("%f") compatibility + assert_eq!( + s, + "gipfel: longitude 11.153300, latitude 47.360100, height 2100.500000, direction 20.000000, nick 0.300000, tilt 1.000000, focal_length_35mm 35.000000, projection type 1, k0 0.001000, k1 -0.000200, x0 12.000000" + ); + + let mut md2 = ImageMetaData::new(); + assert!(md2.parse_gipfel_comment(s.as_bytes())); + assert!((md2.longitude - 11.1533).abs() < 1e-9); + assert!((md2.latitude - 47.3601).abs() < 1e-9); + assert!((md2.height - 2100.5).abs() < 1e-9); + assert!((md2.direction - 20.0).abs() < 1e-9); + assert_eq!(md2.projection_type, 1); + assert!((md2.k0 - 0.001).abs() < 1e-9); + assert!((md2.k1 + 0.0002).abs() < 1e-9); + assert!((md2.x0 - 12.0).abs() < 1e-9); + + // short form (8 fields, pre-distortion files): k0/k1/x0 stay NaN + let short = b"gipfel: longitude 1.000000, latitude 2.000000, height 3.000000, direction 4.000000, nick 5.000000, tilt 6.000000, focal_length_35mm 7.000000, projection type 0"; + let mut md3 = ImageMetaData::new(); + assert!(md3.parse_gipfel_comment(short)); + assert!(md3.k0.is_nan()); + assert_eq!(md3.projection_type, 0); + + // trailing garbage after x0 must not break parsing (sscanf semantics) + let mut md4 = ImageMetaData::new(); + let mut s4 = s.clone(); + s4.push_str(" some other comment junk"); + assert!(md4.parse_gipfel_comment(s4.as_bytes())); + assert!((md4.x0 - 12.0).abs() < 1e-9); +} + +/// The least-squares fit must run on a small stack: the LM solver keeps its +/// working buffers (residuals, Jacobian, normal equations) on the heap, so a +/// 128 KiB thread stack is plenty. Guards against regressions that move those +/// buffers back onto the stack. +#[test] +fn fit_survives_small_stack() { + let Some(data) = data_path() else { + eprintln!("gipfel.dat not found, skipping"); + return; + }; + std::thread::Builder::new() + .stack_size(128 * 1024) + .spawn(move || { + let mut pan = Panorama::new(); + pan.load_data(&data).unwrap(); + pan.set_view_lat(47.36); + pan.set_view_long(11.15); + pan.set_view_height(2100.0); + pan.set_center_angle(20.0); + pan.set_pitch_angle(0.0); + pan.set_roll_angle(1.0); + pan.set_scale(3000.0); + let known: Vec<usize> = pan.visible[..4].to_vec(); + // perturb so the solver actually iterates + pan.set_center_angle(20.5); + pan.set_scale(2800.0); + pan.comp_params(&known, true).unwrap(); + }) + .unwrap() + .join() + .unwrap(); +} + +/// Stitch resample against the C Stitch::resample + ScanImage pipeline. +fn stitch_golden( + name: &str, + proj: ProjectionType, + center: f64, + pitch: f64, + roll: f64, + scale: f64, + k0: f64, + k1: f64, + bicubic: bool, +) { + use gipfel_core::stitch::{self, ScanMode}; + + let ppm = format!( + "{}/../../tests/golden/stitch.ppm", + env!("CARGO_MANIFEST_DIR") + ); + let img = image::open(&ppm) + .unwrap_or_else(|e| panic!("missing {}: {e}", ppm)) + .to_rgb8(); + + let mut pan = Panorama::new(); + pan.set_projection(proj); + pan.set_center_angle(center); + pan.set_pitch_angle(pitch); + pan.set_roll_angle(roll); + pan.set_scale(scale); + pan.set_distortion_params(k0, k1, 0.0); + + let src = stitch::StitchSource { pan, img }; + let mode = if bicubic { ScanMode::Bicubic } else { ScanMode::Nearest }; + let out = stitch::resample(&[src], mode, 64, 48, 10.0, 30.0); + + let mut checked = 0; + for line in golden(name) { + let f: Vec<&str> = line.split_whitespace().collect(); + let (x, y, hit): (usize, usize, i32) = (f[0].parse().unwrap(), f[1].parse().unwrap(), f[2].parse().unwrap()); + let want = [ + f[3].parse::<u16>().unwrap(), + f[4].parse::<u16>().unwrap(), + f[5].parse::<u16>().unwrap(), + ]; + let i = y * 64 + x; + assert_eq!(out.hit[i], hit == 1, "{name} pixel ({x},{y}) hit flag"); + assert_eq!(out.pixels[i], want, "{name} pixel ({x},{y})"); + checked += 1; + } + assert!(checked > 1000); +} + +#[test] +fn stitch_rect_nearest_matches_c() { + stitch_golden("stitch_rect_near.txt", ProjectionType::Rectilinear, 20.0, 2.0, 0.0, 200.0, 0.0, 0.0, false); +} + +#[test] +fn stitch_rect_bicubic_matches_c() { + stitch_golden("stitch_rect_bic.txt", ProjectionType::Rectilinear, 20.0, 2.0, 0.0, 200.0, 0.0, 0.0, true); +} + +#[test] +fn stitch_cyl_bicubic_matches_c() { + stitch_golden("stitch_cyl_bic.txt", ProjectionType::Cylindrical, 20.0, 2.0, 0.0, 200.0, 0.0, 0.0, true); +} + +#[test] +fn stitch_distorted_bicubic_matches_c() { + stitch_golden("stitch_dist_bic.txt", ProjectionType::Rectilinear, 20.0, 2.0, 3.0, 200.0, -0.3, 0.1, true); +} diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 062c88c..0000000 --- a/examples/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -images := $(wildcard *.jpg) -slides := $(addprefix gipfelweb/slides/, $(images)) -thumbs := $(addprefix gipfelweb/thumbs/, $(images)) -positions := $(addprefix gipfelweb/gipfel/, $(addsuffix .pos, $(basename $(images)))) -imgpos := $(addprefix gipfelweb/gipfel/, $(addsuffix .imgs, $(basename $(images)))) -hillpos := $(addprefix gipfelweb/gipfel/, $(addsuffix .hills, $(basename $(images)))) -html := $(addprefix gipfelweb/, $(addsuffix .html, $(basename $(images)))) -htmlhills := $(addprefix gipfelweb/hills/, $(addsuffix .html, $(basename $(images)))) - -all: - echo "make gipfelweb - create a HTML based we of gipfel oriented" - echo "images in the current directory." - -gipfelweb: dirs $(slides) $(thumbs) $(imgpos) $(hillpos) $(html) $(htmlhills) - -clean: - rm -rf gipfelweb/gipfel - -clobber: - rm -rf gipfelweb - -dirs: - mkdir -p gipfelweb/slides - mkdir -p gipfelweb/thumbs - mkdir -p gipfelweb/hills - mkdir -p gipfelweb/gipfel - -gipfelweb/slides/%.jpg: %.jpg - anytopnm $< | pamscale -f lanczos -xyfit 20000 500 | pnmtojpeg -quality 94 > $@ - jhead -te $< $@ - -gipfelweb/thumbs/%.jpg: %.jpg - anytopnm $< | pamscale -f point -xyfit 400 50 | pnmtojpeg -quality 80 > $@ - -gipfelweb/gipfel/%.pos: %.jpg - gipfel -p $< > $@ - -gipfelweb/gipfel/%.imgs: gipfelweb/slides/%.jpg %.opts gipfelweb/gipfel/index.gipfel - gipfel `cat $*.opts` -e gipfelweb/gipfel/index.gipfel $< > $@ - -gipfelweb/gipfel/%.imgs: gipfelweb/slides/%.jpg gipfelweb/gipfel/index.gipfel - gipfel -V 0.05 -e gipfelweb/gipfel/index.gipfel $< > $@ - -gipfelweb/gipfel/%.hills: gipfelweb/slides/%.jpg %.opts - gipfel `cat $*.opts` -E $< > $@ - -gipfelweb/gipfel/%.hills: gipfelweb/slides/%.jpg - gipfel -V 0.05 -E $< > $@ - -gipfelweb/gipfel/index.gipfel: $(positions) - cat $(positions) > gipfelweb/gipfel/index.gipfel - -gipfelweb/%.html: gipfelweb/slides/%.jpg gipfelweb/gipfel/%.imgs %.title $(thumbs) - gipfel2html images "`cat $*.title`" $* > $@ - -gipfelweb/%.html: gipfelweb/slides/%.jpg gipfelweb/gipfel/%.imgs $(thumbs) - gipfel2html images $* $* > $@ - -gipfelweb/hills/%.html: gipfelweb/slides/%.jpg gipfelweb/gipfel/%.hills %.title - gipfel2html hills "`cat $*.title`" $* > $@ - -gipfelweb/hills/%.html: gipfelweb/slides/%.jpg gipfelweb/gipfel/%.hills - gipfel2html hills $* $* > $@ diff --git a/examples/gipfel2html b/examples/gipfel2html deleted file mode 100755 index c8df477..0000000 --- a/examples/gipfel2html +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh - -MODE="$1" -TITLE="$2" -FILE="$3" - -if [ "$MODE" = "images" ]; then - DATAFILE="gipfelweb/gipfel/${FILE}.imgs" -else - DATAFILE="gipfelweb/gipfel/${FILE}.hills" -fi - -cat << EOF -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"> -<html> -<head> -EOF - -echo "<title>$TITLE</title>" - -cat << EOF -<style type="text/css"> -#gipfel { display: block; position: relative; margin: 0; padding: 0; } -#gipfel li { visibility: hidden; list-style-type: none; position: absolute; background: url(25w.png); border: 1px solid black; font-family: sans-serif; font-size: x-small; color: #000; padding: 0; line-height: 1.3em; overflow: hidden; } -#gipfel span { visibility: hidden; position: absolute; left: 0; right: 0; display: block; padding: 5px; } -#gipfel:hover li { visibility: visible; } -#gipfel li:hover span {visibility: visible;} -EOF - -IFS=" " -i=0 -grep -v "^#" ${DATAFILE} | \ -while read name height x y dist flags dummy; do - if [ "$MODE" = "images" ]; then - thumb_w=`identify -format %w gipfelweb/thumbs/${name}` - thumb_h=`identify -format %h gipfelweb/thumbs/${name}` - - echo "#id_${i} {left:$((${x} - 10 / 2))px; top: $((${y} - 6 / 2))px; width: 10px; height: 6px;}" - echo "#id_${i}:hover {width: ${thumb_w}px; height: ${thumb_h}px; background: url(thumbs/${name});}" - else - echo "#id_${i} {left:$((${x} - 4 / 2))px; top: $((${y} - 4 / 2))px; width: 4px; height: 4px;}" - echo "#id_${i}:hover {width: 200px; height: 20px;}" - fi - - i=$((${i} + 1)) -done - -cat << EOF -</style></head> -<body bgcolor="#919faa"> -<br/> -<br/> -EOF - -echo "<h3>$TITLE</h3>" - -if [ "$MODE" = "images" ]; then - echo "<a href=\"hills/${FILE}.html\">show hill names</a>" -else - echo "<a href=\"../${FILE}.html\">back to gipfelweb</a>" -fi - -cat << EOF -<table border="0" cellpadding="0" cellspacing="1" bgcolor="black"> -<tr><td> -<table border="0" cellpadding="10" cellspacing="0" bgcolor="white"> -<tr><td> -<div id="gipfel"> -EOF - -if [ "$MODE" = "images" ]; then - echo "<img src=\"slides/${FILE}.jpg\"/>" -else - echo "<img src=\"../slides/${FILE}.jpg\"/>" -fi - - -echo "<ul>" -i=0 -grep -v "^#" ${DATAFILE} | \ -while read name height x y dist flags dummy; do - if [ "$MODE" = "images" ]; then - THUMB_TITLE="${name%.jpg}" - if [ -e "${name%.jpg}.title" ]; then - THUMB_TITLE=`cat ${name%.jpg}.title` - fi - echo "<li id=\"id_${i}\"><a href=\"${name%.jpg}.html\"><span>${THUMB_TITLE} (${height}m)</span></a></li>" - else - echo "<li id=\"id_${i}\"><span>${name} (${height}m)</span></li>" - fi - - i=$((${i} + 1)) -done -echo "</ul>" - -cat << EOF -</div> -</td></tr> -</table> -</td></tr> -</table> -<br/> -<b>Created with <a href="http://www.ecademix.com/JohannesHofmann/gipfel.html">gipfel</a></b> -<br/> -<br/> -EOF - -if [ "$MODE" = "images" ]; then - echo Move the mouse over the image to see other images. -else - echo Move the mouse over the image to see known mountains. -fi - -cat << EOF -<br/> -Move the mouse over the rectangle to see details. -<br/> -<br/> -This image is visible on these images:<br/> -EOF - -grep -l ${FILE} gipfelweb/gipfel/*.imgs | \ -while read f; do - img="`basename ${f%.imgs}`" - if [ "$MODE" = "images" ]; then - echo "<a href=\"${img}.html\"><img src=\"thumbs/${img}.jpg\"></a><br/>" - else - echo "<a href=\"../${img}.html\"><img src=\"../thumbs/${img}.jpg\"></a><br/>" - fi -done - - -cat << EOF -</body> -</html> -EOF diff --git a/gipfel.spec b/gipfel.spec deleted file mode 100644 index eed33bb..0000000 --- a/gipfel.spec +++ /dev/null @@ -1,56 +0,0 @@ -Name: gipfel -Summary: gipfel - Photogrammetry For Mountain Images -Version: 0.4.0 -Release: 4.0 -URL: http://www.ecademix.com/JohannesHofmann/gipfel.html -BuildRoot: %{_tmppath}/%{name}-%{version}-build -Source0: %{name}-%{version}.tar.gz -License: GPL -Group: Productivity/Scientific/Other - -BuildRequires: binutils gcc gcc-c++ libstdc++-devel gsl-devel fltk-devel libjpeg-devel libtiff-devel libpng-devel - -%if 0%{?fedora_version} -BuildRequires: exiv2-devel -%else -BuildRequires: libexiv2-devel -%endif - -%if 0%{?suse_version} -BuildRequires: xorg-x11-libXext-devel -%endif - -%description -gipfel helps to find the names of mountains or points of interest -on a picture. -It uses a database containing names and GPS data. With the given viewpoint -(the point from which the picture was taken) and two known mountains -on the picture, gipfel can compute all parameters needed to compute the -positions of other mountains on the picture. -gipfel can also be used to play around with the parameters manually. -%prep -%setup -q -%build -./configure --prefix=%{_prefix} --docdir=%{_defaultdocdir}/%{name} || cat config.log -make -%install -make DESTDIR=$RPM_BUILD_ROOT install-strip -rm -rf $RPM_BUILD_ROOT/%{_defaultdocdir}/%{name} -%files -%defattr(-, root, root) -%doc NEWS README -%{_bindir}/%{name} -%dir %{_datadir}/%{name} -%{_datadir}/%{name}/gipfel.dat - -%changelog -* Fri Jan 17 2014 Johannes Hofmann <Johannes.Hofmann@gmx.de> -- update to gipfel-0.4.0 -* Thu Jul 2 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -- update to gipfel-0.3.2 -* Tue Apr 7 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -- update to gipfel-0.3.1 -* Thu Mar 26 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -- update to gipfel-0.2.9 -* Sun Mar 22 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -- initial revision diff --git a/src/Fl_Search_Chooser.H b/src/Fl_Search_Chooser.H deleted file mode 100644 index 37ed431..0000000 --- a/src/Fl_Search_Chooser.H +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef FL_SEARCH_CHOOSER_H -#define FL_SEARCH_CHOOSER_H - -#include <FL/Fl_Window.H> -#include <FL/Fl_Select_Browser.H> -#include <FL/Fl_Input.H> - -class Fl_Search_Browser : public Fl_Select_Browser { - private: - int find_prefix(const char *p, int s, int e); - - public: - Fl_Search_Browser(int X, int Y, int W, int H, const char *c):Fl_Select_Browser(X,Y,W,H,c){}; - - int find_prefix(const char *p); -}; - -class Fl_Search_Chooser : public Fl_Window { - private: - Fl_Search_Browser *sb; - int visible_focus; - - static void input_cb(Fl_Input* in, void*c); - static void ok_cb(Fl_Input* in, void*c); - static void cancel_cb(Fl_Input* in, void*c); - - public: - Fl_Search_Chooser(const char *title); - - void close(); - void add(const char *t, void *d); - void *data(); - int handle(int event); -}; - -#endif diff --git a/src/Fl_Search_Chooser.cxx b/src/Fl_Search_Chooser.cxx deleted file mode 100644 index 286516c..0000000 --- a/src/Fl_Search_Chooser.cxx +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <string.h> -#include <FL/Fl.H> -#include <FL/Fl_Window.H> -#include <FL/Fl_Button.H> -#include "Fl_Search_Chooser.H" - -int -Fl_Search_Browser::find_prefix(const char *p) { - int i = find_prefix(p, 1, size()); - if (i == -1) { - return 1; - } else { - deselect(); - middleline(i); - select(i); - return 0; - } -} - -int -Fl_Search_Browser::find_prefix(const char *p, int s, int e) { - if (s < 0 || e > size() || s > e) { - fprintf(stderr, "Invalid search range %d %d\n", s, e); - return 1; - } else if (e - s <= 1) { - if (strncasecmp(p, text(s), strlen(p)) == 0) { - return s; - } else if (strncasecmp(p, text(e), strlen(p)) == 0){ - return e; - } else { - return -1; - } - } else { - int med = s + (e - s) / 2; - if (strncasecmp(p, text(med), strlen(p)) > 0) { - return find_prefix(p, med, e); - } else { - return find_prefix(p, s, med); - } - } -} - -void -Fl_Search_Chooser::input_cb(Fl_Input* in, void*c) { - Fl_Search_Browser *sb = ((Fl_Search_Chooser *) c)->sb; - sb->find_prefix(in->value()); -} - -void -Fl_Search_Chooser::ok_cb(Fl_Input* in, void*c) { - Fl_Search_Chooser *sc = (Fl_Search_Chooser *) c; - sc->close(); -} - -void -Fl_Search_Chooser::cancel_cb(Fl_Input* in, void*c) { - Fl_Search_Chooser *sc = (Fl_Search_Chooser *) c; - sc->sb->deselect(); - sc->close(); -} - -class Fl_Search_Input : public Fl_Input { - public: - Fl_Search_Input(int x, int y, int w, int h, const char *title) : - Fl_Input(x, y, w, h, title) {} - int handle(int event) { - if (event == FL_KEYBOARD && - (Fl::event_key() == FL_Up || Fl::event_key() == FL_Down)) { - return 0; - } else { - return Fl_Input::handle(event); - } - } -}; - -Fl_Search_Chooser::Fl_Search_Chooser(const char *title) : Fl_Window(320, 320, title?title:"Choose") { - callback((Fl_Callback*) cancel_cb, this); - visible_focus = Fl::visible_focus(); - Fl::visible_focus(0); - Fl_Group *g = new Fl_Group(10, 10, w() - 10, h() - 10); - sb = new Fl_Search_Browser(g->x(), g->y(), g->w() , g->h() - 100, NULL); - sb->type(FL_HOLD_BROWSER); - Fl_Input *in = new Fl_Search_Input(g->x()+50, g->h()-80, g->w()-80, 20, "Search"); - in->callback((Fl_Callback*) input_cb, this); - in->when(FL_WHEN_CHANGED); - Fl_Button *cancel_b = new Fl_Button(g->w()-200, g->h()-30, 80, 25, "Cancel"); - cancel_b->callback((Fl_Callback*) cancel_cb, this); - Fl_Button *ok_b = new Fl_Button(g->w()-100, g->h()-30, 80, 25, "Ok"); - ok_b->callback((Fl_Callback*) ok_cb, this); - Fl::focus(in); - g->end(); - end(); -} - -void -Fl_Search_Chooser::close() { - hide(); - Fl::visible_focus(visible_focus); -} - -void -Fl_Search_Chooser::add(const char *t, void *d) { - sb->add(t, d); -} - -void * -Fl_Search_Chooser::data() { - int v = sb->value(); - if (v) { - return sb->data(v); - } else { - return NULL; - } -} - -int -Fl_Search_Chooser::handle(int event) { - switch(event) { - case FL_KEYBOARD: - int key = Fl::event_key(); - - if (key == FL_Up || key == FL_Down) { - return sb->handle(event); - } else if (key == FL_Enter) { - close(); - } - } - - return Fl_Window::handle(event); -} diff --git a/src/Fl_Value_Dial.H b/src/Fl_Value_Dial.H deleted file mode 100644 index aa09407..0000000 --- a/src/Fl_Value_Dial.H +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef Fl_Value_Dial_H -#define Fl_Value_Dial_H - -#include <FL/Fl_Dial.H> - -class Fl_Value_Dial : public Fl_Dial { - private: - uchar textfont_, textsize_; - unsigned textcolor_; - public: - void draw(); - int handle(int event); - - Fl_Value_Dial(int x,int y,int w,int h, const char *l = 0); - Fl_Font textfont() const {return (Fl_Font)textfont_;} - void textfont(uchar s) {textfont_ = s;} - uchar textsize() const {return textsize_;} - void textsize(uchar s) {textsize_ = s;} - Fl_Color textcolor() const {return (Fl_Color)textcolor_;} - void textcolor(unsigned s) {textcolor_ = s;} -}; - -#endif diff --git a/src/Fl_Value_Dial.cxx b/src/Fl_Value_Dial.cxx deleted file mode 100644 index 9b54458..0000000 --- a/src/Fl_Value_Dial.cxx +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <FL/Fl.H> -#include <FL/fl_draw.H> -#include <math.h> -#include "Fl_Value_Dial.H" - -Fl_Value_Dial::Fl_Value_Dial(int X, int Y, int W, int H, const char*l) - : Fl_Dial(X,Y,W,H,l) -{ - step(1,100); - textfont_ = FL_HELVETICA; - textsize_ = 10; - textcolor_ = FL_BLACK; -} - -void Fl_Value_Dial::draw() { - int sxx = x(), syy = y(), sww = w(), shh = h(); - int bxx = x(), byy = y(), bww = w(), bhh = h(); - char buf[128]; - - if (damage()&FL_DAMAGE_ALL) draw_box(box(),sxx,syy,sww,shh,color()); - Fl_Dial::draw(sxx+Fl::box_dx(box()), - syy+Fl::box_dy(box()), - sww-Fl::box_dw(box()), - shh-Fl::box_dh(box())); - - format(buf); // prints at most 20 chars - fl_font(textfont(), textsize()); - - fl_color(active_r() ? textcolor() : fl_inactive(textcolor())); - fl_draw(buf, bxx, byy + fl_height() - 2, bww, bhh, FL_ALIGN_TOP); -} - -int Fl_Value_Dial::handle(int event) { - switch (event) { - case FL_KEYBOARD : - switch (Fl::event_key()) { - case FL_Left: - handle_drag(clamp(increment(value(),-1))); - handle_release(); - return 1; - case FL_Right: - handle_drag(clamp(increment(value(),1))); - handle_release(); - return 1; - default: - return 0; - } - break; - case FL_FOCUS : - case FL_UNFOCUS : - if (Fl::visible_focus()) { - redraw(); - return 1; - } else return 0; - case FL_ENTER : - case FL_LEAVE : - return 1; - default: - return Fl_Dial::handle(event); - } -} diff --git a/src/GipfelWidget.H b/src/GipfelWidget.H deleted file mode 100644 index db67d5e..0000000 --- a/src/GipfelWidget.H +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright 2006 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef GipfelWidget_H -#define GipfelWidget_H - -#include <stdio.h> - -#include <FL/Fl_Group.H> -#include <FL/Fl_Menu_Button.H> - -#include "Panorama.H" -#include "ImageMetaData.H" -#include "ScanImage.H" - -class GipfelWidget : public Fl_Group { - private: - Fl_Image *img; - Hill *cur_mountain, *focused_mountain; - Hills *track_points; - Hills *known_hills; - Panorama *pan; - char *img_file; - double track_width; - bool have_gipfel_info; - bool show_hidden; - ImageMetaData *md; - int mouse_x, mouse_y; - char focused_mountain_label[128]; - void (*params_changed_cb)(); - - int handle(int event); - Hill * find_mountain(Hills *mnts, int m_x, int m_y); - int toggle_known_mountain(int m_x, int m_y); - int set_mountain(int m_x, int m_y); - void set_labels(Hills *v); - int get_rel_track_width(Hill *m); - - static void find_peak_cb(Fl_Widget *o, void *f); - static void toggle_hidden_cb(Fl_Widget *o, void *f); - - public: - GipfelWidget(int X,int Y,int W, int H, void (*changed_cb)()); - - int load_image(char *file); - int save_image(char *file); - int export_hills(const char *file, FILE *fp); - const char * get_image_filename(); - int load_data(const char *file); - int load_track(const char *file); - int set_viewpoint(const char *pos); - void set_viewpoint(const Hill *m); - void set_center_angle(double a); - void set_nick_angle(double a); - void set_tilt_angle(double a); - void set_focal_length_35mm(double s); - void set_height_dist_ratio(double r); - void set_hide_value(double h); - void set_show_hidden(bool h); - void set_view_lat(double v); - void set_view_long(double v); - void set_view_height(double v); - const char * get_viewpoint() { return pan->get_viewpoint(); }; - double get_center_angle() { return pan->get_center_angle(); }; - double get_nick_angle() { return pan->get_nick_angle(); }; - double get_tilt_angle() { return pan->get_tilt_angle(); }; - double get_focal_length_35mm(); - double get_height_dist_ratio() { return pan->get_height_dist_ratio(); }; - double get_view_lat() { return pan->get_view_lat(); }; - double get_view_long() { return pan->get_view_long(); }; - double get_view_height() { return pan->get_view_height(); }; - void set_track_width(double w); - ProjectionLSQ::Projection_t projection() { - return pan->get_projection(); - }; - void projection(ProjectionLSQ::Projection_t p); - void get_distortion_params(double *k0, double *k1, double *x0) { - pan->get_distortion_params(k0, k1, x0); - }; - void set_distortion_params(double k0, double k1, double x0); - Hills *get_mountains() { return pan->get_mountains(); }; - int comp_params(); - int get_pixel(ScanImage::mode_t m, - double a_alph, double a_nick, int *r, int *g, int *b); - int get_distortion_profile_name(char *buf, int buflen); - int save_distortion_params(const char *prof_name, int force); - int load_distortion_params(const char *prof_name); - void draw(); -}; -#endif diff --git a/src/GipfelWidget.cxx b/src/GipfelWidget.cxx deleted file mode 100644 index 30172a1..0000000 --- a/src/GipfelWidget.cxx +++ /dev/null @@ -1,822 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <stdlib.h> -#include <errno.h> -#include <math.h> -#include <algorithm> - -#include <FL/Fl.H> -#include <FL/Fl_Menu_Item.H> -#include <FL/Fl_Shared_Image.H> -#include <FL/Fl_JPEG_Image.H> -#include <FL/Fl_Preferences.H> -#include <FL/fl_draw.H> - -#include "Fl_Search_Chooser.H" -#include "choose_hill.H" -#include "ScanImage.H" -#include "GipfelWidget.H" - -#define CROSS_SIZE 2 - -static double pi_d, deg2rad; - -GipfelWidget::GipfelWidget(int X,int Y,int W, int H, void (*changed_cb)()): Fl_Group(X, Y, W, H) { - end(); - pi_d = asin(1.0) * 2.0; - deg2rad = pi_d / 180.0; - img = NULL; - pan = new Panorama(); - cur_mountain = NULL; - focused_mountain = NULL; - known_hills = new Hills(); - img_file = NULL; - track_width = 200.0; - show_hidden = false; - have_gipfel_info = false; - md = new ImageMetaData(); - track_points = NULL; - fl_register_images(); - mouse_x = mouse_y = 0; - params_changed_cb = changed_cb; -} - -int -GipfelWidget::load_image(char *file) { - Fl_Image *new_img; - double direction, nick, tilt, fl; - - new_img = new Fl_JPEG_Image(file); - - if (new_img == NULL) - return 1; - - if (img) - delete img; - - img = new_img; - - if (img_file) - free(img_file); - - img_file = strdup(file); - - known_hills->clear(); - - h(img->h()); - w(img->w()); - - // try to retrieve gipfel data from JPEG meta data - md->load_image(file); - set_view_long(md->longitude()); - set_view_lat(md->latitude()); - set_view_height(md->height()); - projection((ProjectionLSQ::Projection_t) md->projection_type()); - - have_gipfel_info = true; - direction = md->direction(); - if (isnan(direction)) { - set_center_angle(0.0); - have_gipfel_info = false; - } else { - set_center_angle(direction); - } - - nick = md->nick(); - if (isnan(nick)) { - set_nick_angle(0.0); - have_gipfel_info = false; - } else { - set_nick_angle(nick); - } - - tilt = md->tilt(); - if (isnan(tilt)) { - set_tilt_angle(0.0); - have_gipfel_info = false; - } else { - set_tilt_angle(tilt); - } - - fl = md->focal_length_35mm(); - if (isnan(fl) || fl == 0.0) { - set_focal_length_35mm(35.0); - have_gipfel_info = false; - } else { - set_focal_length_35mm(fl); - } - - // try to get distortion parameters in the following ordering: - // 1. gipfel data in JPEG comment - // 2. matching distortion profile - // 3. set the to 0.0, 0.0 - md->distortion_params(&pan->parms.k0, &pan->parms.k1, &pan->parms.x0); - if (isnan(pan->parms.k0)) { - char buf[1024]; - if (get_distortion_profile_name(buf, sizeof(buf)) == 0) - load_distortion_params(buf); - - if (isnan(pan->parms.k0)) - pan->parms.k0 = 0.0; - if (isnan(pan->parms.k1)) - pan->parms.k1 = 0.0; - if (isnan(pan->parms.x0)) - pan->parms.x0 = 0.0; - } - - return 0; -} - -const char * -GipfelWidget::get_image_filename() { - return img_file; -} - -int -GipfelWidget::save_image(char *file) { - if (img_file == NULL) { - fprintf(stderr, "Nothing to save\n"); - return 1; - } - - md->longitude(get_view_long()); - md->latitude(get_view_lat()); - md->height(get_view_height()); - md->direction(get_center_angle()); - md->nick(get_nick_angle()); - md->tilt(get_tilt_angle()); - md->focal_length_35mm(get_focal_length_35mm()); - md->projection_type((int) projection()); - md->distortion_params(pan->parms.k0, pan->parms.k1, pan->parms.x0); - - return md->save_image(img_file, file); -} - -int -GipfelWidget::load_data(const char *file) { - int r; - - r = pan->load_data(file); - set_labels(pan->get_visible_mountains()); - - return r; -} - -int -GipfelWidget::load_track(const char *file) { - if (track_points) { - pan->remove_hills(Hill::TRACK_POINT); - track_points->clobber(); - delete track_points; - } - - track_points = new Hills(); - - if (track_points->load(file) != 0) { - delete track_points; - track_points = NULL; - return 1; - } - - for (int i = 0; i < track_points->get_num(); i++) - track_points->get(i)->flags |= Hill::TRACK_POINT; - - pan->add_hills(track_points); - redraw(); - - return 0; -} - -int -GipfelWidget::set_viewpoint(const char *pos) { - int r; - - r = pan->set_viewpoint(pos); - set_labels(pan->get_visible_mountains()); - redraw(); - return r; -} - -void -GipfelWidget::set_viewpoint(const Hill *m) { - pan->set_viewpoint(m); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -static void -draw_flag(int x, int y) { - fl_polygon(x, y - 10, x, y - 20, x + 10, y - 15); - fl_yxline(x, y, y - 10); - fl_circle(x , y, 3); -} - -void -GipfelWidget::draw() { - Hills *mnts; - Hill *m; - int i, height; - - if (img == NULL) - return; - - fl_push_clip(x(), y(), w(), h()); - img->draw(x(),y(),w(),h(),0,0); - - /* hills */ - fl_font(FL_HELVETICA, 8); - mnts = pan->get_visible_mountains(); - - fl_color(FL_YELLOW); - height = fl_height(); - for (i=0; i<mnts->get_num(); i++) { - m = mnts->get(i); - int m_x = w() / 2 + x() + (int) rint(m->x); - int m_y = h() / 2 + y() + (int) rint(m->y); - - if ((m->flags & (Hill::DUPLIC|Hill::TRACK_POINT)) || - (!show_hidden && (m->flags & Hill::HIDDEN)) || - known_hills->contains(m) || m == focused_mountain) - continue; - - if (fabs(m->x) > img->w() / 2 || fabs(m->y) > img->h() / 2) - continue; - - fl_xyline(m_x - CROSS_SIZE, m_y, m_x + CROSS_SIZE); - fl_yxline(m_x, m_y + m->label_y - height, m_y + CROSS_SIZE); - fl_xyline(m_x, m_y + m->label_y - height, m_x + m->label_x); - } - - for (i=0; i<mnts->get_num(); i++) { - m = mnts->get(i); - int m_x = w() / 2 + x() + (int) rint(m->x); - int m_y = h() / 2 + y() + (int) rint(m->y); - - if ((m->flags & (Hill::DUPLIC|Hill::TRACK_POINT)) || - (!show_hidden && (m->flags & Hill::HIDDEN)) || m == focused_mountain) - continue; - - if (fabs(m->x) > img->w() / 2 || fabs(m->y) > img->h() / 2) - continue; - - if (known_hills->contains(m)) { - if (known_hills->get_num() > 3) - fl_color(FL_GREEN); - else - fl_color(FL_RED); - - draw_flag(m_x, m_y); - fl_color(FL_BLACK); - } else if (m->flags & Hill::HIDDEN) { - fl_color(FL_BLUE); - } else { - fl_color(FL_BLACK); - } - - fl_draw(m->name, m_x + 2, m_y + m->label_y); - } - - if (focused_mountain) { - m = focused_mountain; - int m_x = w() / 2 + x() + (int) rint(m->x); - int m_y = h() / 2 + y() + (int) rint(m->y); - - fl_color(FL_YELLOW); - - fl_rectf(m_x, m_y - height, - (int) fl_width(focused_mountain_label) + 2, height + 2); - fl_color(FL_BLACK); - fl_draw(focused_mountain_label, m_x, m_y); - } - - - /* track */ - if (track_points && track_points->get_num() > 0) { - int last_x = 0, last_y = 0, last_initialized = 0; - - for (i=1; i<track_points->get_num(); i++) { - m = track_points->get(i); - int m_x = w() / 2 + x() + (int) rint(m->x); - int m_y = h() / 2 + y() + (int) rint(m->y); - - if (!(track_points->get(i)->flags & Hill::VISIBLE)) - continue; - - if (track_points->get(i)->flags & Hill::HIDDEN) - fl_color(FL_BLUE); - else - fl_color(FL_RED); - - fl_line_style(FL_SOLID | FL_CAP_ROUND | FL_JOIN_ROUND, - get_rel_track_width(track_points->get(i))); - if (last_initialized) { - fl_begin_line(); - fl_vertex(last_x, last_y); - fl_vertex(m_x, m_y); - fl_end_line(); - } - - last_x = m_x; - last_y = m_y; - last_initialized++; - } - fl_line_style(0); - } - - fl_pop_clip(); -} - -static int -overlap(double x1, double l1, double x2, double l2) { - return x1 <= x2 + l2 && x1 + l1 >= x2; -} - -void -GipfelWidget::set_labels(Hills *v) { - int height; - - if (!img) - return; - - - fl_font(FL_HELVETICA, 8); - height = fl_height(); - - for (int i = 0; i < v->get_num(); i++) { - Hill *m = v->get(i); - Hills colliding; - - if (m->flags & (Hill::DUPLIC | Hill::TRACK_POINT)) - continue; - - if (!show_hidden && (m->flags & Hill::HIDDEN)) - continue; - - if (fabs(m->x) > img->w() / 2 || fabs(m->y) > img->h() / 2) - continue; - - m->label_x = (int) fl_width(m->name) + 1; - m->label_y = 0; - - for (int j = i - 1; j >= 0; j--) { - Hill *n = v->get(j); - - if (n->flags & (Hill::DUPLIC | Hill::TRACK_POINT)) - continue; - - if (!show_hidden && (n->flags & Hill::HIDDEN)) - continue; - - if (fabs(m->x) > img->w() / 2 || fabs(m->y) > img->h() / 2) - continue; - - if (overlap(m->x, m->label_x, n->x - CROSS_SIZE, n->label_x)) - colliding.add(n); - else - break; - } - - colliding.sort(Hills::SORT_LABEL_Y); - - for (int j = 0; j < colliding.get_num(); j++) { - Hill *n = colliding.get(j); - - // Check for overlapping labels and - // overlaps between labels and peak markers - if (overlap(m->y + m->label_y - height, height, - n->y + n->label_y - height, height) || - overlap(m->y + m->label_y - height, - height, n->y - CROSS_SIZE, 2 * CROSS_SIZE)) { - - m->label_y = (int) rint(n->y + n->label_y - m->y - height - 2); - } - } - } -} - -Hill * -GipfelWidget::find_mountain(Hills *mnts, int m_x, int m_y) { - Hill *m; - int center_x = w() / 2; - int center_y = h() / 2; - - for (int i = 0; i < mnts->get_num(); i++) { - m = mnts->get(i); - - if (m_x - center_x >= m->x - 2 && m_x - center_x < m->x + 2 && - m_y - center_y >= m->y - 2 && m_y - center_y < m->y + 2) - return m; - } - - return NULL; -} - -int -GipfelWidget::toggle_known_mountain(int m_x, int m_y) { - Hills *mnts = pan->get_visible_mountains(); - int center_x = w() / 2; - int center_y = h() / 2; - - for (int i = 0; i < mnts->get_num(); i++) { - Hill *m = mnts->get(i); - - if (m->flags & (Hill::DUPLIC | Hill::TRACK_POINT)) - continue; - - if (m_x - center_x >= m->x - 2 && m_x - center_x < m->x + 2 && - m_y - center_y >= m->y - 2 && m_y - center_y < m->y + 2) { - - - if (known_hills->contains(m)) - known_hills->remove(m); - else - known_hills->add(m); - - redraw(); - return 0; - } - } - - cur_mountain = NULL; - redraw(); - return 1; -} - -int -GipfelWidget::set_mountain(int m_x, int m_y) { - int old_x, old_y, old_label_y; - int center_x = w() / 2; - int center_y = h() / 2; - - if (!cur_mountain) - return 1; - - old_x = (int) rint(cur_mountain->x); - old_y = (int) rint(cur_mountain->y); - old_label_y = cur_mountain->label_y; - - cur_mountain->x = m_x - center_x; - cur_mountain->y = m_y - center_y; - cur_mountain->label_y = 0; - - damage(4, center_x + x() + old_x - 2*CROSS_SIZE - 1, - center_y + y() + old_y + old_label_y - 2*CROSS_SIZE - 20, - std::max(20, cur_mountain->label_x) + 2*CROSS_SIZE + 4, - std::max(20, old_label_y) + 22 ); - damage(4, - (int) rint(center_x + x() + cur_mountain->x - 2*CROSS_SIZE - 1), - (int) rint(center_y + y() + cur_mountain->y + cur_mountain->label_y - 2*CROSS_SIZE - 20), - std::max(20, cur_mountain->label_x) + 2*CROSS_SIZE + 4, - std::max(20, cur_mountain->label_y) + 22 ); - - return 0; -} - -void -GipfelWidget::set_center_angle(double a) { - pan->set_center_angle(a); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_nick_angle(double a) { - pan->set_nick_angle(a); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_tilt_angle(double a) { - pan->set_tilt_angle(a); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_focal_length_35mm(double s) { - int w = std::max(img->w(), img->h()); // assume sensor is wider than high - pan->set_scale(s * (double) w / 35.0); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::projection(ProjectionLSQ::Projection_t p) { - pan->set_projection(p); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_distortion_params(double k0, double k1, double x0) { - pan->set_distortion_params(k0, k1, x0); - redraw(); -} - -double -GipfelWidget::get_focal_length_35mm() { - int w; - - if (img == NULL) - return NAN; - - w = std::max(img->w(), img->h()); // assume sensor is wider than high - return pan->get_scale() * 35.0 / (double) w; -} - -void -GipfelWidget::find_peak_cb(Fl_Widget *, void *f) { - GipfelWidget *g = (GipfelWidget*) f; - - Hill *m = choose_hill(g->pan->get_close_mountains(), "Find Peak"); - if (m) { - if (!g->known_hills->contains(m)) - g->known_hills->add(m); - - m->flags |= Hill::VISIBLE; - if (! g->pan->get_visible_mountains()->contains(m)) - g->pan->get_visible_mountains()->add(m); - g->set_labels(g->pan->get_visible_mountains()); - - g->cur_mountain = m; - g->set_mountain(g->mouse_x, g->mouse_y); - g->comp_params(); - } -} - -void -GipfelWidget::toggle_hidden_cb(Fl_Widget *, void *f) { - GipfelWidget *g = (GipfelWidget*) f; - - Hill *m = g->find_mountain(g->pan->get_visible_mountains(), g->mouse_x, g->mouse_y); - if (!m) - return; - - if (m->flags & Hill::HIDDEN) - m->flags &= ~Hill::HIDDEN; - else - m->flags |= Hill::HIDDEN; - - g->set_labels(g->pan->get_visible_mountains()); - g->redraw(); -} - -void -GipfelWidget::set_height_dist_ratio(double r) { - pan->set_height_dist_ratio(r); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_hide_value(double h) { - pan->set_hide_value(h); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_show_hidden(bool h) { - show_hidden = h; - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_view_lat(double v) { - pan->set_view_lat(v); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_view_long(double v) { - pan->set_view_long(v); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -void -GipfelWidget::set_view_height(double v) { - pan->set_view_height(v); - set_labels(pan->get_visible_mountains()); - redraw(); -} - -int -GipfelWidget::comp_params() { - int ret; - - fl_cursor(FL_CURSOR_WAIT); - ret = pan->comp_params(known_hills); - set_labels(pan->get_visible_mountains()); - redraw(); - fl_cursor(FL_CURSOR_DEFAULT); - if (params_changed_cb) - params_changed_cb(); - - return ret; -} - -int -GipfelWidget::get_rel_track_width(Hill *m) { - double dist = pan->get_real_distance(m); - - return (int) rint(std::max((pan->get_scale()*track_width)/(dist*10.0), 1.0)); -} - -void -GipfelWidget::set_track_width(double w) { - track_width = w; - redraw(); -} - -int -GipfelWidget::handle(int event) { - Hill *m; - - switch(event) { - case FL_PUSH: - mouse_x = Fl::event_x()-x(); - mouse_y = Fl::event_y()-y(); - if (Fl::event_button() == FL_LEFT_MOUSE) { - m = find_mountain(known_hills, mouse_x, mouse_y); - if (m) - cur_mountain = m; - } else if (Fl::event_button() == FL_MIDDLE_MOUSE) { - toggle_known_mountain(mouse_x, mouse_y); - if (focused_mountain) { - focused_mountain = NULL; - redraw(); - } - } else if (Fl::event_button() == FL_RIGHT_MOUSE) { - focused_mountain = NULL; - - Fl_Menu_Button rclick_menu(Fl::event_x_root(), - Fl::event_y_root(), 80, 1); - Hill *m = find_mountain(pan->get_visible_mountains(), - mouse_x, mouse_y); - char buf[1024]; - - rclick_menu.add("Find Peak", 0, find_peak_cb, this); - if (m) { - if (m->flags & Hill::HIDDEN) - snprintf(buf, sizeof(buf), "Unhide %s", m->name); - else - snprintf(buf, sizeof(buf), "Hide %s", m->name); - - rclick_menu.add(buf, 0, toggle_hidden_cb, this); - } - - rclick_menu.popup(); - } - - Fl::focus(this); - return 1; - case FL_DRAG: - set_mountain(Fl::event_x()-x(), Fl::event_y()-y()); - return 1; - case FL_RELEASE: - cur_mountain = NULL; - if (known_hills->get_num() > 0) - comp_params(); - return 1; - case FL_ENTER: - return 1; - case FL_LEAVE: - return 1; - case FL_MOVE: - m = find_mountain(pan->get_visible_mountains(), Fl::event_x()-x(), Fl::event_y()-y()); - if (m != focused_mountain && (!m || !known_hills->contains(m))) { - if (m) - snprintf(focused_mountain_label, - sizeof(focused_mountain_label) - 1, - "%s (%dm), distance %.2fkm", - m->name, (int) m->height, pan->get_real_distance(m) / 1000.0); - - focused_mountain = m; - redraw(); - } - return 1; - case FL_FOCUS: - return 1; - case FL_UNFOCUS: - return 0; - } - return 0; -} - -int -GipfelWidget::export_hills(const char *file, FILE *fp) { - Hills export_hills, *mnts; - - if (!have_gipfel_info) { - fprintf(stderr, "No gipfel info available for %s.\n", img_file); - return 0; - } - - if (file) { - if (export_hills.load(file) != 0) - return 1; - - for (int i = 0; i < export_hills.get_num(); i++) - export_hills.get(i)->flags |= Hill::EXPORT; - - pan->add_hills(&export_hills); - } - - fprintf(fp, "#\n# name\theight\tx\ty\tdistance\tflags\n#\n"); - - mnts = pan->get_visible_mountains(); - for (int i = 0; i < mnts->get_num(); i++) { - Hill *m = mnts->get(i); - int _x = (int) rint(m->x) + w() / 2; - int _y = (int) rint(m->y) + h() / 2; - - if (m->flags & Hill::DUPLIC || m->flags & Hill::HIDDEN || - (file && !(m->flags & Hill::EXPORT))) { - continue; - } - - if (_x < 0 || _x > w() || _y < 0 || _y > h()) - continue; - - fprintf(fp, "%s\t%d\t%d\t%d\t%d\n", - m->name, (int) rint(m->height), _x, _y, - (int) rint(pan->get_real_distance(m))); - } - - pan->remove_hills(Hill::EXPORT); - - return 0; -} - -int -GipfelWidget::get_pixel(ScanImage::mode_t m, - double a_alph, double a_nick, int *r, int *g, int *b) { - double px, py; - int ret; - - if (img == NULL) - return 1; - - if (pan->get_coordinates(a_alph, a_nick, &px, &py) != 0) - return 1; - - ret = ScanImage::get_pixel(img, m, px + ((double) img->w()) / 2.0, - py + ((double) img->h()) / 2.0, r, g, b); - - return ret; -} - -int -GipfelWidget::get_distortion_profile_name(char *buf, int buflen) { - int n; - - if (md && md->manufacturer() && md->model()) { - n = snprintf(buf, buflen, "%s_%s_%.2f_mm", - md->manufacturer(), md->model(), md->focal_length()); - - return n > buflen; - } else { - return 1; - } -} - -static Fl_Preferences dist_prefs(Fl_Preferences::USER, - "Johannes.HofmannATgmx.de", "gipfel/DistortionProfiles"); - -int -GipfelWidget::load_distortion_params(const char *prof_name) { - int ret = 0; - - Fl_Preferences prof(dist_prefs, prof_name); - ret += prof.get("k0", pan->parms.k0, pan->parms.k0); - ret += prof.get("k1", pan->parms.k1, pan->parms.k1); - ret += prof.get("x0", pan->parms.x0, pan->parms.x0); - - return !ret; -} - -int -GipfelWidget::save_distortion_params(const char *prof_name, int force) { - Fl_Preferences prof(dist_prefs, prof_name); - - if (!force && prof.entryExists("k0")) - return 1; - - prof.set("k0", pan->parms.k0); - prof.set("k1", pan->parms.k1); - prof.set("x0", pan->parms.x0); - - return 0; -} diff --git a/src/Hill.H b/src/Hill.H deleted file mode 100644 index c55db15..0000000 --- a/src/Hill.H +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef HILL_H -#define HILL_H - -class Hill; - -class Hill { - public: - typedef enum { - DUPLIC = 0x01, - TRACK_POINT = 0x02, - VISIBLE = 0x04, - HIDDEN = 0x08, - EXPORT = 0x10, - } flags_t; - - double phi, lam; - double height; - double alph; - double a_nick; - double dist; - double x, y; - int label_x, label_y; - char *name; - int flags; - - Hill(const char *n, double p, double l, double h); - Hill(const Hill& h); - Hill(double x_tmp, double y_tmp); - ~Hill(); -}; - -class Hills { - private: - int num, cap; - Hill **m; - - public: - typedef enum { - SORT_ALPHA, - SORT_PHI, - SORT_NAME, - SORT_LABEL_Y, - SORT_X - } SortType; - - Hills(); - Hills(const Hills *h); - ~Hills(); - - int load(const char *file); - void mark_duplicates(double dist); - void add(Hill *m); - void remove(const Hill *m); - void add(Hills *h); - void sort(SortType t); - void clear(); - void clobber(); - int contains(const Hill *m) const; - inline int get_num() const { return num; }; - Hill *get(int n) const; -}; - -#endif diff --git a/src/Hill.cxx b/src/Hill.cxx deleted file mode 100644 index 2aad3f3..0000000 --- a/src/Hill.cxx +++ /dev/null @@ -1,341 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> -extern "C" { -#include "strsep.h" -} -#include "Hill.H" - -static double pi_d, deg2rad; - -Hill::Hill(const char *n, double p, double l, double h) { - name = strdup(n); - phi = p; - lam = l; - height = h; - alph = 0.0; - x = 0; - y = 0; - flags = 0; -} - -Hill::Hill(const Hill& h) { - name = strdup(h.name); - phi = h.phi; - lam = h.lam; - height = h.height; - alph = h.alph; - dist = h.dist; - x = h.x; - y = h.y; - label_x = h.label_x; - label_y = h.label_y; - flags = h.flags; -} - -Hill::Hill(double x_tmp, double y_tmp) { - name = NULL; - phi = 0.0; - lam = 0.0; - height = 0.0; - alph = 0.0; - x = x_tmp; - y = y_tmp; -} - -Hill::~Hill() { - if (name) - free(name); -} - -Hills::Hills() { - num = 0; - cap = 100; - m = (Hill **) malloc(cap * sizeof(Hill *)); - - pi_d = asin(1.0) * 2.0; - deg2rad = pi_d / 180.0; -} - -Hills::Hills(const Hills *h) { - num = h->num; - cap = h->cap; - m = (Hill **) malloc(cap * sizeof(Hill *)); - memcpy(m, h->m, cap * sizeof(Hill *)); - - pi_d = asin(1.0) * 2.0; - deg2rad = pi_d / 180.0; -} - -int -Hills::load(const char *file) { - FILE *fp; - char buf[4000]; - char *vals[10]; - char **ap, *bp; - double phi, lam, height; - Hill *m; - int n; - - fp = fopen(file, "r"); - if (!fp) { - perror("fopen"); - return 1; - } - - while (fgets(buf, sizeof(buf), fp)) { - bp = buf; - memset(vals, 0, sizeof(vals)); - n = 0; - for (ap = vals; (*ap = strsep(&bp, ",")) != NULL;) { - n++; - if (++ap >= &vals[10]) - break; - } - - // standard format including name and description - if (n == 6 && vals[1] && vals [3] && vals[4] && vals[5]) { - phi = atof(vals[3]) * deg2rad; - lam = atof(vals[4]) * deg2rad; - height = atof(vals[5]); - - m = new Hill(vals[1], phi, lam, height); - - add(m); - // track point format - } else if (n == 3 && vals[0] && vals[1] && vals[2]) { - phi = atof(vals[0]) * deg2rad; - lam = atof(vals[1]) * deg2rad; - height = atof(vals[2]); - - m = new Hill("", phi, lam, height); - - add(m); - } - } - - fclose(fp); - - return 0; -} - -void Hills::mark_duplicates(double dist) { - Hill *m, *n; - int i, j; - - sort(SORT_PHI); - - for(i = 0; i < get_num(); i++) { - m = get(i); - - if (m) { - if (m->flags & Hill::TRACK_POINT) - continue; - - j = i + 1; - n = get(j); - while (n && fabs(n->phi - m->phi) <= dist) { - if (! (n->flags & Hill::DUPLIC)) { - if (fabs(n->lam - m->lam) <= dist && - fabs(n->height - m->height) <= 50.0 ) { - n->flags |= Hill::DUPLIC; - } - } - j = j + 1; - n = get(j); - } - } - } -} - -Hills::~Hills() { - if (m) - free(m); -} - -void -Hills::add(Hill *m1) { - if (num >= cap) { - cap = cap ? cap * 2 : 100; - m = (Hill **) realloc(m, cap * sizeof(Hill *)); - } - - m[num++] = m1; -} - -void -Hills::add(Hills *h) { - for (int i=0; i<h->get_num(); i++) - add(h->get(i)); -} - -static int -comp_mountains_alpha(const void *n1, const void *n2) { - Hill *m1 = *(Hill **)n1; - Hill *m2 = *(Hill **)n2; - - if (m1 && m2) { - if (m1->alph < m2->alph) - return 1; - else if (m1->alph > m2->alph) - return -1; - else - return 0; - } else { - return 0; - } -} - -static int -comp_mountains_phi(const void *n1, const void *n2) { - Hill *m1 = *(Hill **)n1; - Hill *m2 = *(Hill **)n2; - - if (m1 && m2) { - if (m1->phi < m2->phi) - return 1; - else if (m1->phi > m2->phi) - return -1; - else - return 0; - } else { - return 0; - } -} - -static int -comp_mountains_name(const void *n1, const void *n2) { - Hill *m1 = *(Hill **)n1; - Hill *m2 = *(Hill **)n2; - int r; - - if (m1 && m2) { - r = strcasecmp(m1->name, m2->name); - if (r == 0) - return (int) (m1->height - m2->height); - else - return r; - } else { - return 0; - } -} - -static int -comp_mountains_label_y(const void *n1, const void *n2) { - Hill *m1 = *(Hill **)n1; - Hill *m2 = *(Hill **)n2; - - if (m1 && m2) { - if ((m2->y + m2->label_y) > (m1->y + m1->label_y)) - return 1; - else if ((m2->y + m2->label_y) < (m1->y + m1->label_y)) - return -1; - else - return 0; - } else { - return 0; - } -} - -static int -comp_mountains_x(const void *n1, const void *n2) { - Hill *m1 = *(Hill **)n1; - Hill *m2 = *(Hill **)n2; - - if (m1 && m2) { - if (m2->x < m1->x) - return 1; - else if (m2->x > m1->x) - return -1; - else - return 0; - } else { - return 0; - } -} - -void -Hills::sort(SortType t) { - int (*cmp)(const void *, const void *); - - if (!m || num < 2) - return; - - switch (t) { - case SORT_ALPHA: - cmp = comp_mountains_alpha; - break; - case SORT_PHI: - cmp = comp_mountains_phi; - break; - case SORT_NAME: - cmp = comp_mountains_name; - break; - case SORT_LABEL_Y: - cmp = comp_mountains_label_y; - break; - case SORT_X: - cmp = comp_mountains_x; - break; - default: - fprintf(stderr, "ERROR: Unknown sort type %d\n", t); - return; - } - - qsort(m, num, sizeof(Hill *), cmp); -} - -void -Hills::clear() { - if (m) { - free(m); - m = NULL; - } - cap = 0; - num = 0; -} - -int -Hills::contains(const Hill *m) const { - for (int i = 0; i < get_num(); i++) - if (get(i) == m) - return 1; - - return 0; -} - -void -Hills::remove(const Hill *h) { - for (int i = 0; i < get_num(); i++) { - if (get(i) == h) { - memmove(&m[i], &m[i+1], (get_num() - i - 1) * sizeof(Hill*)); - num--; - } - } -} - -void -Hills::clobber() { - int i; - - for (i = 0; i < get_num(); i++) - if (get(i)) - delete(get(i)); - - clear(); -} - -Hill * -Hills::get(int n) const { - if (n < 0 || n >= num) - return NULL; - else - return m[n]; -} diff --git a/src/ImageMetaData.H b/src/ImageMetaData.H deleted file mode 100644 index 109652d..0000000 --- a/src/ImageMetaData.H +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef IMAGE_META_DATA_H -#define IMAGE_META_DATA_H - -class ImageMetaData { - private: - char *_manufacturer; - char *_model; - double _longitude; - double _latitude; - double _height; - double _direction; - double _nick; - double _tilt; - double _k0; - double _k1; - double _x0; - double _focal_length; - double _focal_length_35mm; - double _scale; - int _projection_type; - int _have_gipfel_info; - - int load_image_jpgcom(char *name); - int save_image_jpgcom(char *in_img, char *out_img); - int load_image_exif(char *name); - void clear(); - - public: - ImageMetaData(); - ~ImageMetaData(); - - int load_image(char *name); - int save_image(char *in_img, char *out_img); - - const char *manufacturer() {return _manufacturer;}; - const char *model() {return _model;}; - double longitude() {return _longitude;}; - double latitude() {return _latitude;}; - double height() {return _height;}; - double direction() {return _direction;}; - double nick() {return _nick;}; - double tilt() {return _tilt;}; - double focal_length() {return _focal_length;}; - double focal_length_35mm() {return _focal_length_35mm;}; - int projection_type() {return _projection_type;}; - void distortion_params(double *_k0, double *_k1, double *_x0); - - void longitude(double v) {_longitude = v;}; - void latitude(double v) {_latitude = v;}; - void height(double v) {_height = v;}; - void direction(double v) {_direction = v;}; - void nick(double v) {_nick = v;}; - void tilt(double v) {_tilt = v;}; - void focal_length_35mm(double v) {_focal_length_35mm = v;}; - void projection_type(int v) {_projection_type = v;}; - void distortion_params(double k0, double k1, double x0); -}; - -#endif diff --git a/src/ImageMetaData.cxx b/src/ImageMetaData.cxx deleted file mode 100644 index fa334aa..0000000 --- a/src/ImageMetaData.cxx +++ /dev/null @@ -1,322 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <math.h> -#include <string.h> -#include <fcntl.h> -#include <libgen.h> -#include <assert.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <exiv2/exiv2.hpp> - -#include "../config.h" -#include "ImageMetaData.H" - -#if !defined(O_BINARY) -#define O_BINARY 0 -#endif - -ImageMetaData::ImageMetaData() { - _manufacturer = NULL; - _model = NULL; - clear(); -} - -ImageMetaData::~ImageMetaData() { - if (_manufacturer) - free(_manufacturer); - if (_model) - free(_model); -} - -void -ImageMetaData::clear() { - if (_manufacturer) - free(_manufacturer); - _manufacturer = NULL; - if (_model) - free(_model); - _model = NULL; - _longitude = NAN; - _latitude = NAN; - _height = NAN; - _direction = NAN; - _nick = NAN; - _tilt = NAN; - _k0 = NAN; - _k1 = NAN; - _x0 = NAN; - _focal_length = NAN; - _focal_length_35mm = NAN; - _scale = NAN; - _projection_type = 0; -} - -int -ImageMetaData::load_image(char *name) { - clear(); - load_image_jpgcom(name); - load_image_exif(name); // fill missing values from exif data - return 0; -} - -int -ImageMetaData::save_image(char *in_img, char *out_img) { - return save_image_jpgcom(in_img, out_img); -} - -static void -exifSetValue(double *destVal, Exiv2::ExifData *exifData, const char *name) { - Exiv2::ExifKey key(name); - Exiv2::ExifData::iterator pos = exifData->findKey(key); - pos = exifData->findKey(key); - if (pos != exifData->end() && pos->toFloat() >= 0) - *destVal = pos->toFloat(); -} - -static void -exifSetCoordinate(double *destVal, Exiv2::ExifData *exifData, const char *name) { - Exiv2::ExifKey key(name); - Exiv2::ExifData::iterator pos = exifData->findKey(key); - - if (pos != exifData->end()) { - if (pos->toFloat() >= 0) - *destVal = pos->toFloat(); - if (pos->toFloat(1) >= 0) - *destVal += pos->toFloat(1) / 60; - if (pos->toFloat(2) >= 0) - *destVal += pos->toFloat(2) / 3600; - } -} - -int -ImageMetaData::load_image_exif(char *name) { -#if EXIV2_TEST_VERSION(0,28,0) - Exiv2::Image::UniquePtr image; -#else - Exiv2::Image::AutoPtr image; -#endif - - try { - image = Exiv2::ImageFactory::open(name); - image->readMetadata(); - } catch (const Exiv2::Error &error) { - fprintf(stderr, "Error reading metadata\n"); - return 1; - } - - Exiv2::ExifData &exifData = image->exifData(); - if (exifData.empty()) { - fprintf(stderr, "%s: No Exif data found in the file", name); - return 1; - } - - Exiv2::ExifData::iterator pos = exifData.end(); - - if (!_manufacturer ) { - Exiv2::ExifKey key("Exif.Image.Make"); // tag auch wirklich vorhanden? - pos = exifData.findKey(key); - if (pos != exifData.end() && pos->size()) - _manufacturer = strdup(pos->toString().c_str()); - } - - if (!_model) { - Exiv2::ExifKey key("Exif.Image.Model"); - pos = exifData.findKey(key); - if (pos != exifData.end() && pos->size() ) - _model = strdup(pos->toString().c_str()); - } - - if (std::isnan(_focal_length)) - exifSetValue(&_focal_length, &exifData, "Exif.Photo.FocalLength"); - - if (std::isnan(_focal_length_35mm)) - exifSetValue(&_focal_length_35mm, &exifData, "Exif.Photo.FocalLengthIn35mmFilm"); - - if (std::isnan(_longitude)) - exifSetCoordinate(&_longitude, &exifData, "Exif.GPSInfo.GPSLongitude"); - - if (std::isnan(_latitude)) - exifSetCoordinate(&_latitude, &exifData, "Exif.GPSInfo.GPSLatitude"); - - if (std::isnan(_height)) - exifSetValue(&_height, &exifData, "Exif.GPSInfo.GPSAltitude"); - - return 0; -} - - -#define GIPFEL_FORMAT "gipfel: longitude " FMT_DOUBLE ", latitude " FMT_DOUBLE ", height " FMT_DOUBLE ", direction " FMT_DOUBLE ", nick " FMT_DOUBLE ", tilt " FMT_DOUBLE ", focal_length_35mm " FMT_DOUBLE ", projection type %d, k0 " FMT_DOUBLE ", k1 " FMT_DOUBLE ", x0 " FMT_DOUBLE "" - -#define FMT_DOUBLE "%lf" -static const char *gipfel_format_scan = GIPFEL_FORMAT; - -#undef FMT_DOUBLE -#define FMT_DOUBLE "%f" -static const char *gipfel_format_prnt = GIPFEL_FORMAT; - -#undef FMT_DOUBLE -#undef GIPFEL_FORMAT - -int -ImageMetaData::load_image_jpgcom(char *name) { - double lo, la, he, dir, ni, ti, fr, k0, k1, x0 = 0.0; - int pt = 0; - int n, ret = 1; -#if EXIV2_TEST_VERSION(0,28,0) - Exiv2::Image::UniquePtr image; -#else - Exiv2::Image::AutoPtr image; -#endif - - try { - image = Exiv2::ImageFactory::open(name); - image->readMetadata(); - } catch (const Exiv2::Error &error) { - fprintf(stderr, "Error reading metadata\n"); - return 1; - } - - const char *com = image->comment().c_str(); - - if ((n = sscanf(com, gipfel_format_scan, - &lo, &la, &he, &dir, &ni, &ti, &fr, &pt, &k0, &k1, &x0)) >= 8) { - _longitude = lo; - _latitude = la; - _height = he; - _direction = dir; - _nick = ni; - _tilt = ti; - _focal_length_35mm = fr; - _projection_type = pt; - - if (n >= 10) { - _k0 = k0; - _k1 = k1; - _x0 = x0; - } - - ret = 0; - } - - return ret; -} - -int -ImageMetaData::save_image_jpgcom(char *in_img, char *out_img) { - char buf[1024], *tmpname; - int n, in_fd, tmp_fd, err = 0; - - char* dirbuf = strdup(out_img); -#if HAVE_MKSTEMP - char tmpbuf[MAXPATHLEN]; - snprintf(tmpbuf, sizeof(tmpbuf), "%s/.gipfelXXXXXX", dirname(dirbuf)); - tmp_fd = mkstemp(tmpbuf); - tmpname = tmpbuf; -#else - tmpname = tempnam(dirname(dirbuf), ".gipfel"); - tmp_fd = open(tmpname, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR); -#endif - free(dirbuf); - - if (tmp_fd == -1) { - perror("mkstemp"); - return 1; - } - - in_fd = open(in_img, O_RDONLY|O_BINARY); - if (in_fd == -1) { - perror("open"); - unlink(tmpname); - close(tmp_fd); - return 1; - } - - while ((n = read(in_fd, buf, sizeof(buf))) > 0) { - if (n < 0 || write(tmp_fd, buf, n) != n) { - perror("write"); - err++; - break; - } - } - - close(in_fd); - -#if EXIV2_TEST_VERSION(0,28,0) - Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open(tmpname); -#else - Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(tmpname); -#endif - if (!image.get()) - err++; - - image->readMetadata(); - image->clearComment(); - - snprintf(buf, sizeof(buf), gipfel_format_prnt, - _longitude, - _latitude, - _height, - _direction, - _nick, - _tilt, - _focal_length_35mm, - _projection_type, - _k0, _k1, _x0); - - image->setComment(buf); - - try { - image->writeMetadata(); - } catch (const Exiv2::Error &error) { - fprintf(stderr, "Error writing metadata\n"); - err++; - } - -#ifdef HAVE_FSYNC - fsync(tmp_fd); // make sure data is on disk before replacing orig file -#endif - - close(tmp_fd); - - if (err == 0) { // only overwrite existing image if everything was ok -#ifdef WIN32 - // Workaround as Windows does not seem to replace files on rename() - struct stat stFileInfo; - if (stat(out_img, &stFileInfo) == 0) - unlink(out_img); -#endif - - if (rename(tmpname, out_img) != 0) { - perror("rename"); - err++; - unlink(tmpname); - } - } - - return (err != 0); -} - -void -ImageMetaData::distortion_params(double *k0, double *k1, double *x0) { - *k0 = _k0; - *k1 = _k1; - *x0 = _x0; -} - -void -ImageMetaData::distortion_params(double k0, double k1, double x0) { - _k0 = k0; - _k1 = k1; - _x0 = x0; -} diff --git a/src/JPEGOutputImage.H b/src/JPEGOutputImage.H deleted file mode 100644 index 5ce9069..0000000 --- a/src/JPEGOutputImage.H +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef JPEGOUTPUTIMAGE_H -#define JPEGOUTPUTIMAGE_H - -#include <stdio.h> -extern "C" { -#include <jpeglib.h> -#undef HAVE_STDLIB_H -} - -#include "OutputImage.H" - -class JPEGOutputImage : public OutputImage { - private: - unsigned char *row; - char *file; - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - FILE *fp; - int quality; - - protected: - int init_internal(); - int set_pixel_internal(int x, int r, int g, int b); - int next_line_internal(); - int done_internal(); - - public: - JPEGOutputImage(const char *file, int quality = 90); - ~JPEGOutputImage(); -}; - -#endif diff --git a/src/JPEGOutputImage.cxx b/src/JPEGOutputImage.cxx deleted file mode 100644 index 8bbd40b..0000000 --- a/src/JPEGOutputImage.cxx +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -extern "C" { -#include <jpeglib.h> -} - -#include "JPEGOutputImage.H" - -JPEGOutputImage::JPEGOutputImage(const char *f, int q) : OutputImage() { - file = strdup(f); - fp = NULL; - row = NULL; - quality = q; -} - -JPEGOutputImage::~JPEGOutputImage() { - if (row) - free(row); - if (file) - free(file); -} - -int -JPEGOutputImage::init_internal() { - if (row) - free(row); - row = NULL; - - row = (unsigned char*) calloc(3 * W, sizeof(char)); - if (!row) { - perror("calloc"); - return 1; - } - - if (fp) - fclose(fp); - - if ((fp = fopen(file, "wb")) == NULL) { - fprintf(stderr, "can't open %s\n", file); - return 1; - } - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, fp); - cinfo.image_width = W; - cinfo.image_height = H; - cinfo.input_components = 3; /* # of color components per pixel */ - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - - jpeg_start_compress(&cinfo, TRUE); - - return 0; -} - -int -JPEGOutputImage::set_pixel_internal(int x, int r, int g, int b) { - row[x*3+0] = (unsigned char) (r / 255); - row[x*3+1] = (unsigned char) (g / 255); - row[x*3+2] = (unsigned char) (b / 255); - - return 0; -} - -int -JPEGOutputImage::next_line_internal() { - JSAMPROW row_pointer[1]; - - row_pointer[0] = row; - jpeg_write_scanlines(&cinfo, row_pointer, 1); - memset(row, 0, sizeof(char) * 3 * W); - return 0; -} - -int -JPEGOutputImage::done_internal() { - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - - if (fp) - fclose(fp); - fp = NULL; - - if (row) - free(row); - row = NULL; - - if (fp) - fclose(fp); - fp = NULL; - - return 0; -} - diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 3d4a174..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -bin_PROGRAMS = gipfel - -gipfel_SOURCES = \ - gipfel.cxx \ - GipfelWidget.cxx \ - Panorama.cxx \ - ProjectionLSQ.cxx \ - ProjectionRectilinear.cxx \ - ProjectionCylindrical.cxx \ - Hill.cxx \ - Fl_Value_Dial.cxx \ - Fl_Search_Chooser.cxx \ - choose_hill.cxx \ - Stitch.cxx \ - OutputImage.cxx \ - JPEGOutputImage.cxx \ - TIFFOutputImage.cxx \ - PreviewOutputImage.cxx \ - ImageMetaData.cxx \ - ScreenDump.cxx \ - ScanImage.cxx \ - strsep.c - -noinst_HEADERS = \ - GipfelWidget.H \ - Panorama.H \ - ProjectionLSQ.H \ - ProjectionRectilinear.H \ - ProjectionRectilinear_funcs.cxx \ - ProjectionCylindrical.H \ - ProjectionCylindrical_funcs.cxx \ - Hill.H \ - ViewParams.H \ - Fl_Value_Dial.H \ - Fl_Search_Chooser.H \ - choose_hill.H \ - Stitch.H \ - OutputImage.H \ - JPEGOutputImage.H \ - TIFFOutputImage.H \ - PreviewOutputImage.H \ - ImageMetaData.H \ - ScreenDump.H \ - ScanImage.H \ - strsep.h diff --git a/src/Makefile.lsq_funcs b/src/Makefile.lsq_funcs deleted file mode 100644 index 46bab6e..0000000 --- a/src/Makefile.lsq_funcs +++ /dev/null @@ -1,7 +0,0 @@ -all: ProjectionRectilinear_funcs.cxx ProjectionCylindrical_funcs.cxx - -ProjectionRectilinear_funcs.cxx: lsq_rectilinear.mac expr2c.mac - maxima -b lsq_rectilinear.mac | grep "^double" > ProjectionRectilinear_funcs.cxx - -ProjectionCylindrical_funcs.cxx: lsq_cylindrical.mac expr2c.mac - maxima -b lsq_cylindrical.mac | grep "^double" > ProjectionCylindrical_funcs.cxx diff --git a/src/OutputImage.H b/src/OutputImage.H deleted file mode 100644 index 0718074..0000000 --- a/src/OutputImage.H +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright 2006 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef OUTPUTIMAGE_H -#define OUTPUTIMAGE_H - -class OutputImage { - private: - int initialized; - - protected: - int W, H, line; - - virtual int init_internal() {return 0;}; - virtual int set_pixel_internal(int x, int r, int g, int b) {return 0;}; - virtual int next_line_internal() {return 0;}; - virtual int done_internal() {return 0;}; - - public: - OutputImage(); - virtual ~OutputImage() {}; - - virtual int init(int w1, int h1); - int set_pixel(int x, int r, int g, int b); - int next_line(); - int done(); -}; - -#endif diff --git a/src/OutputImage.cxx b/src/OutputImage.cxx deleted file mode 100644 index b4c4739..0000000 --- a/src/OutputImage.cxx +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> - -#include "OutputImage.H" - -OutputImage::OutputImage() { - W = 0; - H = 0; - initialized = 0; -} - -int -OutputImage::init(int w1, int h1) { - int ret; - - W = w1; - H = h1; - line = 0; - - ret = init_internal(); - - if (ret == 0) - initialized = 1; - - return ret; -} - -int -OutputImage::set_pixel(int x, int r, int g, int b) { - if (!initialized || x < 0 || x >= W) - return 1; - else - return set_pixel_internal(x, r, g, b); -} - -int -OutputImage::next_line() { - if (!initialized || line++ >= H) - return 1; - else - return next_line_internal(); -} - -int -OutputImage::done() { - if (!initialized) { - return 1; - } else { - next_line(); - return done_internal(); - } -} diff --git a/src/Panorama.H b/src/Panorama.H deleted file mode 100644 index 1f4fdeb..0000000 --- a/src/Panorama.H +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef PANORAMA_H -#define PANORAMA_H - -#include "Hill.H" -#include "ProjectionLSQ.H" -#include "ViewParams.H" - -class Panorama { - private: - double view_phi, view_lam, view_height; - char *view_name; - double height_dist_ratio; - double hide_value; - Hills *mountains; - Hills *close_mountains; - Hills *visible_mountains; - ProjectionLSQ *proj; - ProjectionLSQ::Projection_t projection_type; - double pi_d, deg2rad; - - Hill * get_pos(const char *name); - void update_angles(); - void update_coordinates(Hills *excluded_hills = NULL); - void update_close_mountains(); - void update_visible_mountains(Hills *excluded_hills = NULL); - void mark_hidden(Hills *hills); - double distance(double phi, double lam); - double alpha(const Hill *m); - double nick(const Hill *m); - double refraction(const Hill *m); - double comp_center_angle(double alph_a, double alph_b, double d1, double d2); - double comp_scale(double alph_a, double alph_b, double d1, double d2); - int get_matrix(double m[]); - int optimize(Hill *m1, Hill *m2); - int is_visible(double a_alph); - - public: - ViewParams parms; - - Panorama(); - ~Panorama(); - int load_data(const char *name); - void add_hills(Hills *h); - void remove_hills(int flags); - int set_viewpoint(const char *pos); - void set_viewpoint(const Hill *m); - void set_height_dist_ratio(double r); - void set_hide_value(double h); - Hills * get_mountains(); - Hills * get_close_mountains(); - Hills * get_visible_mountains(); - void set_center_angle(double a); - void set_nick_angle(double a); - void set_tilt_angle(double a); - void set_scale(double s); - void set_view_lat(double v); - void set_view_long(double v); - void set_view_height(double v); - const char * get_viewpoint(); - double get_center_angle(); - double get_nick_angle(); - double get_tilt_angle(); - double get_scale(); - double get_height_dist_ratio(); - double get_view_lat(); - double get_view_long(); - double get_view_height(); - double get_earth_radius(double latitude); - double get_real_distance(const Hill *m); - int comp_params(Hills *h); - ProjectionLSQ::Projection_t get_projection(); - void set_projection(ProjectionLSQ::Projection_t p); - void get_distortion_params(double *k0, double *k1, double *x0); - void set_distortion_params(double k0, double k1, double x0); - int get_coordinates(double a_alph, double a_nick, double *x, double *y); -}; -#endif diff --git a/src/Panorama.cxx b/src/Panorama.cxx deleted file mode 100644 index ccba04f..0000000 --- a/src/Panorama.cxx +++ /dev/null @@ -1,476 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "Panorama.H" -#include "ProjectionRectilinear.H" -#include "ProjectionCylindrical.H" - -#define EARTH_RADIUS 6371000.785 - -Panorama::Panorama() { - mountains = new Hills(); - close_mountains = new Hills(); - visible_mountains = new Hills(); - height_dist_ratio = 0.07; - hide_value = 1.2; - pi_d = asin(1.0) * 2.0; - deg2rad = pi_d / 180.0; - parms.a_center = 0.0; - parms.a_nick = 0.0; - parms.a_tilt = 0.0; - parms.scale = 3500.0; - parms.k0 = 0.0; - parms.k1 = 0.0; - parms.x0 = 0.0; - view_name = NULL; - view_phi = 0.0; - view_lam = 0.0; - view_height = 0.0; - proj = NULL; - set_projection(ProjectionLSQ::RECTILINEAR); -} - -Panorama::~Panorama() { - visible_mountains->clear(); - mountains->clobber(); - delete visible_mountains; - delete mountains; -} - -int -Panorama::load_data(const char *name) { - if (mountains->load(name) != 0) { - fprintf(stderr, "Could not load datafile %s\n", name); - return 1; - } - - mountains->mark_duplicates(0.00001); - update_angles(); - - return 0; -} - -void -Panorama::add_hills(Hills *h) { - mountains->add(h); - - mountains->mark_duplicates(0.00001); - update_angles(); -} - -void -Panorama::remove_hills(int flags) { - Hills *h_new = new Hills(); - Hill *m; - - for (int i = 0; i < mountains->get_num(); i++) { - m = mountains->get(i); - if (! (m->flags & flags)) - h_new->add(m); - } - - delete mountains; - mountains = h_new; -} - -int -Panorama::set_viewpoint(const char *name) { - Hill *m = get_pos(name); - if (m == NULL) { - fprintf(stderr, "Could not find exactly one entry for %s.\n", name); - return 1; - } - - set_viewpoint(m); - return 0; -} - -void -Panorama::set_viewpoint(const Hill *m) { - if (m == NULL) - return; - - view_phi = m->phi; - view_lam = m->lam; - view_height = m->height; - - if (view_name) - free(view_name); - - view_name = strdup(m->name); - - update_angles(); -} - -Hills * -Panorama::get_mountains() { - return mountains; -} - -Hills * -Panorama::get_close_mountains() { - return close_mountains; -} - -Hills * -Panorama::get_visible_mountains() { - return visible_mountains; -} - -int -Panorama::comp_params(Hills *h) { - int ret; - - ret = proj->comp_params(h, &parms); - if (ret == 0) - update_visible_mountains(h); - - return ret; -} - -void -Panorama::set_center_angle(double a) { - parms.a_center = a * deg2rad; - update_visible_mountains(); -} - -void -Panorama::set_nick_angle(double a) { - parms.a_nick = a * deg2rad; - update_coordinates(); -} - -void -Panorama::set_tilt_angle(double a) { - parms.a_tilt = a * deg2rad; - update_coordinates(); -} - -void -Panorama::set_scale(double s) { - parms.scale = s; - update_coordinates(); -} - -void -Panorama::get_distortion_params(double *k0, double *k1, double *x0) { - *k0 = parms.k0; - *k1 = parms.k1; - *x0 = parms.x0; -} - -void -Panorama::set_distortion_params(double k0, double k1, double x0) { - parms.k0 = k0; - parms.k1 = k1; - parms.x0 = x0; - update_coordinates(); -} - -void -Panorama::set_height_dist_ratio(double r) { - height_dist_ratio = r; - update_close_mountains(); -} - -void -Panorama::set_view_long(double v) { - view_lam = v * deg2rad; - update_angles(); -} - -void -Panorama::set_view_lat(double v) { - view_phi = v * deg2rad; - update_angles(); -} - -void -Panorama::set_view_height(double v) { - view_height = v; - update_angles(); -} - -void -Panorama::set_projection(ProjectionLSQ::Projection_t p) { - projection_type = p; - - if (proj) { - delete proj; - proj = NULL; - } - - switch(projection_type) { - case ProjectionLSQ::RECTILINEAR: - proj = new ProjectionRectilinear(); - break; - case ProjectionLSQ::CYLINDRICAL: - proj = new ProjectionCylindrical(); - break; - } - - update_angles(); -} - -const char * -Panorama::get_viewpoint() { - return view_name; -} - -double -Panorama::get_center_angle() { - return parms.a_center / deg2rad; -} - -double -Panorama::get_nick_angle() { - return parms.a_nick / deg2rad; -} - -double -Panorama::get_tilt_angle() { - return parms.a_tilt / deg2rad; -} - -double -Panorama::get_scale() { - return parms.scale; -} - -double -Panorama::get_height_dist_ratio() { - return height_dist_ratio; -} - -double -Panorama::get_view_long() { - return view_lam / deg2rad; -} - -double -Panorama::get_view_lat() { - return view_phi / deg2rad; -} - -double -Panorama::get_view_height() { - return view_height; -} - -ProjectionLSQ::Projection_t -Panorama::get_projection() { - return projection_type; -} - -Hill * -Panorama::get_pos(const char *name) { - Hill *m, *ret = NULL; - - for (int i = 0; i < mountains->get_num(); i++) { - m = mountains->get(i); - - if (strcmp(m->name, name) == 0) { - ret = m; - fprintf(stderr, "Found matching entry: %s (%fm)\n", - m->name, m->height); - } - } - - return ret; -} - -void -Panorama::update_angles() { - for (int i = 0; i < mountains->get_num(); i++) { - Hill *m = mountains->get(i); - - m->dist = distance(m->phi, m->lam); - if (m->phi != view_phi || m->lam != view_lam) - m->alph = alpha(m); - } - - mountains->sort(Hills::SORT_ALPHA); - - update_close_mountains(); -} - -void -Panorama::set_hide_value(double h) { - hide_value = h; - update_visible_mountains(); -} - -void -Panorama::mark_hidden(Hills *hills) { - double h; - - for (int i = 0; i < hills->get_num(); i++) { - Hill *m = hills->get(i); - - m->flags &= ~Hill::HIDDEN; - - if (m->flags & Hill::DUPLIC) - continue; - - for (int j = 0; j < hills->get_num(); j++) { - Hill *n = hills->get(j); - - if (n->flags & Hill::DUPLIC || n->flags & Hill::TRACK_POINT) - continue; - - if (m == n || fabs(m->alph - n->alph > pi_d / 2.0)) - continue; - - if (m->dist < n->dist || m->a_nick > n->a_nick) - continue; - - h = (n->a_nick - m->a_nick) / fabs(m->alph - n->alph); - if (isinf(h) || h > hide_value) - m->flags |= Hill::HIDDEN; - } - } -} - -void -Panorama::update_close_mountains() { - close_mountains->clear(); - - for (int i = 0; i < mountains->get_num(); i++) { - Hill *m = mountains->get(i); - - if (m->flags & Hill::TRACK_POINT || - ((m->phi != view_phi || m->lam != view_lam) && - (m->height / (m->dist * EARTH_RADIUS) > height_dist_ratio))) { - - m->a_nick = nick(m); - close_mountains->add(m); - } - } - - mark_hidden(close_mountains); - update_visible_mountains(); -} - -void -Panorama::update_visible_mountains(Hills *excluded_hills) { - visible_mountains->clear(); - - for (int i = 0; i < close_mountains->get_num(); i++) { - Hill *m = close_mountains->get(i); - - if (is_visible(m->alph)) { - visible_mountains->add(m); - m->flags |= Hill::VISIBLE; - } else { - m->flags &= ~Hill::VISIBLE; - } - } - - update_coordinates(excluded_hills); -} - -void -Panorama::update_coordinates(Hills *excluded_hills) { - for (int i = 0; i < visible_mountains->get_num(); i++) { - Hill *m = visible_mountains->get(i); - if (!excluded_hills || !excluded_hills->contains(m)) - proj->get_coordinates(m->alph, m->a_nick, &parms, &m->x, &m->y); - } -} - -double -Panorama::distance(double phi, double lam) { - double d_lam = view_lam - lam; - - return atan2(sqrt(pow(cos(phi) * sin(d_lam), 2.0) + - pow(cos(view_phi) * sin(phi) - sin(view_phi) * cos(phi) * cos(d_lam), 2.0)), - sin(view_phi) * sin(phi) + cos(view_phi) * cos(phi) * cos(d_lam)); -} - -double -Panorama::alpha(const Hill *m) { - double sin_alph, cos_alph; - - sin_alph = sin(m->lam - view_lam) * cos(m->phi) / sin(m->dist); - cos_alph = (sin(m->phi) - sin(view_phi) * cos(m->dist)) / - (cos(view_phi) * sin(m->dist)); - - return fmod(atan2(sin_alph, cos_alph) + 2.0 * pi_d, 2.0 * pi_d); -} - -// approximation of refraction effect as described by Tom Chester at -// http://tchester.org/sgm/analysis/peaks/refraction.html -double -Panorama::refraction(const Hill *m) { - double a, b, c, alpha = 6.5, T0 = 10.0; - - a = 2.9e-4 * exp (-view_height / 10000.0) / (1.0 + 2.9 * T0 / 760.0); - b = 2.9 * alpha / (760.0 * (1.0 + 2.9 * T0 / 760.0)); - c = a * (b - 1.0 / 10.0); - - return c * get_real_distance(m) / (2000.0 * (1.0 + a)); -} - -double -Panorama::nick(const Hill *m) { - double b, c, theta = refraction(m); - - b = m->height + get_earth_radius(m->phi); - c = view_height + get_earth_radius(view_phi); - - return atan((cos(m->dist) * b - c) / (sin(m->dist) * b)) - theta; -} - -// return local distance to center of WGS84 ellipsoid -double -Panorama::get_earth_radius(double phi) { - double a = 6378137.000; - double b = 6356752.315; - double r; - double ata = tan(phi); - - r = a*pow(pow(ata,2)+1,1.0/2.0)*fabs(b)*pow(pow(b,2)+pow(a,2)*pow(ata,2),-1.0/2.0); - return r; -} - -double -Panorama::get_real_distance(const Hill *m) { - double a, b, c, gam; - - a = view_height + get_earth_radius(view_phi); - b = m->height + get_earth_radius(m->phi); - gam = m->dist; - - c = sqrt(pow(a, 2.0) + pow(b, 2.0) - 2.0 * a * b * cos(gam)); - return c; -} - -int -Panorama::is_visible(double a_alph) { - double center_dist; - - center_dist = fabs(a_alph - parms.a_center); - if (center_dist > pi_d) { - center_dist = 2*pi_d - center_dist; - } - - return center_dist < proj->get_view_angle(); -} - -int -Panorama::get_coordinates(double a_alph, double a_nick, double *x, double *y) { - if (is_visible(a_alph)) { - proj->get_coordinates(a_alph, a_nick, &parms, x, y); - return 0; - } else { - return 1; - } -} diff --git a/src/PreviewOutputImage.H b/src/PreviewOutputImage.H deleted file mode 100644 index f37493b..0000000 --- a/src/PreviewOutputImage.H +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef PREVIEWOUTPUTIMAGE_H -#define PREVIEWOUTPUTIMAGE_H - -#include <stdio.h> - -#include <FL/Fl_Widget.H> -#include <FL/Fl_Image.H> - -#include "OutputImage.H" - -class PreviewOutputImage : public OutputImage , public Fl_Widget { - private: - uchar *data; - int d; - - protected: - int init_internal(); - int set_pixel_internal(int x, int r, int g, int b); - int next_line_internal(); - - public: - PreviewOutputImage(int X, int Y, int W, int H); - ~PreviewOutputImage(); - - void draw(); -}; - -#endif diff --git a/src/PreviewOutputImage.cxx b/src/PreviewOutputImage.cxx deleted file mode 100644 index 560f044..0000000 --- a/src/PreviewOutputImage.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <FL/Fl.H> -#include <FL/fl_draw.H> - -#include "PreviewOutputImage.H" - -PreviewOutputImage::PreviewOutputImage(int X, int Y, int W, int H): - OutputImage(), Fl_Widget(X, Y, W, H) { - d = 3; - data = NULL; -} - -PreviewOutputImage::~PreviewOutputImage() { - if (data) - free(data); -} - -int -PreviewOutputImage::init_internal() { - data = (uchar*) malloc(W * H * d); - memset(data, 0, W * H * d); - size(W, H); - return 0; -} - -int -PreviewOutputImage::set_pixel_internal(int x, int r, int g, int b) { - if (!data) - return 1; - - long index = (line * w() * d + (x * d)); - *(data+index+0) = (unsigned char) (r / 255); - *(data+index+1) = (unsigned char) (g / 255); - *(data+index+2) = (unsigned char) (b / 255); - - return 0; -} - -int -PreviewOutputImage::next_line_internal() { - if (line % 10 == 0) { - redraw(); - Fl::check(); - } - return 0; -} - -void -PreviewOutputImage::draw() { - if (!data) - return; - - fl_push_clip(x(), y(), w(), h()); - fl_draw_image(data, x(), y(), w(), h(), d); - fl_pop_clip(); -} diff --git a/src/ProjectionCylindrical.H b/src/ProjectionCylindrical.H deleted file mode 100644 index 06709c4..0000000 --- a/src/ProjectionCylindrical.H +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright 2006 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef PROJECTIONCYLINDRICAL_H -#define PROJECTIONCYLINDRICAL_H - -#include "ProjectionLSQ.H" - -class ProjectionCylindrical : public ProjectionLSQ { - public: - - virtual double get_view_angle() {return 6.2831853;}; /* 360 deg */ - virtual int comp_params(const Hills *h, ViewParams *parms); - -#define ARGS double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick - virtual double mac_x(ARGS); - virtual double mac_y(ARGS); - virtual double mac_x_dc_view(ARGS); - virtual double mac_x_dc_nick(ARGS); - virtual double mac_x_dc_tilt(ARGS); - virtual double mac_x_dscale(ARGS); - virtual double mac_y_dc_view(ARGS); - virtual double mac_y_dc_nick(ARGS); - virtual double mac_y_dc_tilt(ARGS); - virtual double mac_y_dscale(ARGS); -#undef ARGS - -}; - -#endif diff --git a/src/ProjectionCylindrical.cxx b/src/ProjectionCylindrical.cxx deleted file mode 100644 index 094b494..0000000 --- a/src/ProjectionCylindrical.cxx +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <math.h> - -#include "ProjectionCylindrical.H" - -#include "ProjectionCylindrical_funcs.cxx" - -int -ProjectionCylindrical::comp_params(const Hills *h, ViewParams *parms) { - Hills h_monotone(h); - - h_monotone.sort(Hills::SORT_X); - - // ensure that alpha is increasing with x. - for (int i = 1; i < h_monotone.get_num(); i++) - if (h_monotone.get(i)->alph < h_monotone.get(i - 1)->alph) - h_monotone.get(i)->alph += asin(1.0) * 4.0; // += 2pi - - return ProjectionLSQ::comp_params(&h_monotone, parms); -} diff --git a/src/ProjectionLSQ.H b/src/ProjectionLSQ.H deleted file mode 100644 index a380b4c..0000000 --- a/src/ProjectionLSQ.H +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef PROJECTIONLSQ_H -#define PROJECTIONLSQ_H - -#include "Hill.H" -#include "ViewParams.H" - -class ProjectionLSQ { - private: - double comp_scale(double alph_a, double alph_b, double d1, double d2); - - int lsq(const Hills *m, ViewParams *parms, int distortion_correct); - - protected: - static double pi; - double sec(double a); - - public: - typedef enum { - RECTILINEAR = 0, - CYLINDRICAL = 1 - } Projection_t; - - ProjectionLSQ() {}; - virtual ~ProjectionLSQ() {}; - - void get_coordinates(double a_view, double a_nick, - const ViewParams *parms, double *x, double *y); - - virtual int comp_params(const Hills *h, ViewParams *parms); - - virtual double get_view_angle(); - -#define ARGS double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick - virtual double mac_x(ARGS); - virtual double mac_y(ARGS); - virtual double mac_x_dc_view(ARGS); - virtual double mac_x_dc_nick(ARGS); - virtual double mac_x_dc_tilt(ARGS); - virtual double mac_x_dscale(ARGS); - virtual double mac_x_dk0(ARGS); - virtual double mac_x_dk1(ARGS); - virtual double mac_x_dx0(ARGS); - virtual double mac_y_dc_view(ARGS); - virtual double mac_y_dc_nick(ARGS); - virtual double mac_y_dc_tilt(ARGS); - virtual double mac_y_dscale(ARGS); - virtual double mac_y_dk0(ARGS); - virtual double mac_y_dk1(ARGS); - virtual double mac_y_dx0(ARGS); -#undef ARGS - -}; -#endif diff --git a/src/ProjectionLSQ.cxx b/src/ProjectionLSQ.cxx deleted file mode 100644 index bcbb206..0000000 --- a/src/ProjectionLSQ.cxx +++ /dev/null @@ -1,294 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include <gsl/gsl_rng.h> -#include <gsl/gsl_randist.h> -#include <gsl/gsl_vector.h> -#include <gsl/gsl_blas.h> -#include <gsl/gsl_multifit_nlin.h> - -#include "ProjectionLSQ.H" - -double ProjectionLSQ::pi = asin(1.0) * 2.0; - -double -ProjectionLSQ::sec(double a) { - return 1.0 / cos(a); -} - -double -ProjectionLSQ::get_view_angle() { - return 0.0; -} - -int -ProjectionLSQ::comp_params(const Hills *h, ViewParams *parms) { - const Hill *m1, *m2; - double scale_tmp; - int known_hills = h->get_num(); - ViewParams new_parms = *parms; - - if (known_hills < 1) { - fprintf(stderr, "Please position at least 1 hill\n"); - return 1; - } - - m1 = h->get(0); - new_parms.a_center = m1->alph; - new_parms.a_nick = 0.0; - - if (known_hills > 1) { - m2 = h->get(1); - - scale_tmp = comp_scale(m1->alph, m2->alph, m1->x, m2->x); - - if (isnan(scale_tmp) || scale_tmp < 50.0) { - fprintf(stderr, "Could not determine initial scale value (%f)\n", - scale_tmp); - return 1; - } - - new_parms.scale = scale_tmp; - new_parms.a_tilt = 0.0; - } - - if (known_hills > 3) { - fprintf(stderr, "Performing calibration\n"); - new_parms.k0 = 0.0; - new_parms.k1 = 0.0; - new_parms.x0 = 0.0; - } - - if (known_hills == 1) { - lsq(h, &new_parms, 0); - } else if (known_hills < 4) { - lsq(h, &new_parms, 1); - } else { - lsq(h, &new_parms, 1); - lsq(h, &new_parms, 2); - } - - if (isnan(new_parms.scale) || new_parms.scale < 50.0) { - fprintf(stderr, "Could not determine reasonable view parameters\n"); - return 1; - } - - *parms = new_parms; - - return 0; -} - -struct data { - ProjectionLSQ *p; - int level; - const Hills *h; - const ViewParams *old_params; -}; - -#define CALL(A) dat->p->A(parms.a_center, parms.a_nick, parms.a_tilt, parms.scale, parms.k0, parms.k1, parms.x0, m->alph, m->a_nick) - -static int -lsq_f (const gsl_vector * x, void *data, gsl_vector * f) { - struct data *dat = (struct data *) data; - ViewParams parms = *dat->old_params; - - parms.a_center = gsl_vector_get (x, 0); - parms.a_nick = gsl_vector_get (x, 1); - if (dat->level > 0) { - parms.a_tilt = gsl_vector_get (x, 2); - parms.scale = gsl_vector_get (x, 3); - } - if (dat->level > 1) { - parms.k0 = gsl_vector_get (x, 4); - parms.k1 = gsl_vector_get (x, 5); - parms.x0 = gsl_vector_get (x, 6); - } - - for (int i=0; i<dat->h->get_num(); i++) { - Hill *m = dat->h->get(i); - - double mx = CALL(mac_x); - double my = CALL(mac_y); - - gsl_vector_set (f, i*2, mx - m->x); - gsl_vector_set (f, i*2+1, my - m->y); - } - - return GSL_SUCCESS; -} - - -static int -lsq_df (const gsl_vector * x, void *data, gsl_matrix * J) { - struct data *dat = (struct data *) data; - ViewParams parms = *dat->old_params; - - parms.a_center = gsl_vector_get (x, 0); - parms.a_nick = gsl_vector_get (x, 1); - if (dat->level > 0) { - parms.a_tilt = gsl_vector_get (x, 2); - parms.scale = gsl_vector_get (x, 3); - } - if (dat->level > 1) { - parms.k0 = gsl_vector_get (x, 4); - parms.k1 = gsl_vector_get (x, 5); - parms.x0 = gsl_vector_get (x, 6); - } - - for (int i=0; i<dat->h->get_num(); i++) { - Hill *m = dat->h->get(i); - - gsl_matrix_set (J, 2*i, 0, CALL(mac_x_dc_view)); - gsl_matrix_set (J, 2*i, 1, CALL(mac_x_dc_nick)); - if (dat->level > 0) { - gsl_matrix_set (J, 2*i, 2, CALL(mac_x_dc_tilt)); - gsl_matrix_set (J, 2*i, 3, CALL(mac_x_dscale)); - } - if (dat->level > 1) { - gsl_matrix_set (J, 2*i, 4, CALL(mac_x_dk0)); - gsl_matrix_set (J, 2*i, 5, CALL(mac_x_dk1)); - gsl_matrix_set (J, 2*i, 6, CALL(mac_x_dx0)); - } - - gsl_matrix_set (J, 2*i+1, 0, CALL(mac_y_dc_view)); - gsl_matrix_set (J, 2*i+1, 1, CALL(mac_y_dc_nick)); - if (dat->level > 0) { - gsl_matrix_set (J, 2*i+1, 2, CALL(mac_y_dc_tilt)); - gsl_matrix_set (J, 2*i+1, 3, CALL(mac_y_dscale)); - } - if (dat->level > 1) { - gsl_matrix_set (J, 2*i+1, 4, CALL(mac_y_dk0)); - gsl_matrix_set (J, 2*i+1, 5, CALL(mac_y_dk1)); - gsl_matrix_set (J, 2*i+1, 6, CALL(mac_y_dx0)); - } - } - - return GSL_SUCCESS; -} - -static int -lsq_fdf (const gsl_vector * x, void *data, gsl_vector * f, gsl_matrix * J) { - lsq_f (x, data, f); - lsq_df (x, data, J); - - return GSL_SUCCESS; -} - -int -ProjectionLSQ::lsq(const Hills *h, ViewParams *parms, - int level) { - - const gsl_multifit_fdfsolver_type *T; - gsl_multifit_fdfsolver *s; - gsl_multifit_function_fdf f; - struct data dat; - double x_init[8]; - gsl_vector_view x; - int status; - int num_params; - - if (level == 0) - num_params = 2; - else if (level == 1) - num_params = 4; - else - num_params = 7; - - dat.p = this; - dat.level = level; - dat.h = h; - dat.old_params = parms; - - x_init[0] = parms->a_center; - x_init[1] = parms->a_nick; - x_init[2] = parms->a_tilt; - x_init[3] = parms->scale; - x_init[4] = parms->k0; - x_init[5] = parms->k1; - x_init[6] = parms->x0; - - x = gsl_vector_view_array (x_init, num_params); - - f.f = &lsq_f; - f.df = &lsq_df; - f.fdf = &lsq_fdf; - f.n = h->get_num() * 2; - f.p = num_params; - f.params = &dat; - - T = gsl_multifit_fdfsolver_lmsder; - s = gsl_multifit_fdfsolver_alloc (T, h->get_num() * 2, num_params); - gsl_multifit_fdfsolver_set (s, &f, &x.vector); - - for (int i=0; i<100; i++) { - status = gsl_multifit_fdfsolver_iterate (s); - if (status) { - break; - } - } - - parms->a_center = gsl_vector_get(s->x, 0); - parms->a_nick = gsl_vector_get(s->x, 1); - - if (level > 0) { - parms->a_tilt = gsl_vector_get(s->x, 2); - parms->scale = gsl_vector_get(s->x, 3); - } - if (level > 1) { - parms->k0 = gsl_vector_get(s->x, 4); - parms->k1 = gsl_vector_get(s->x, 5); - parms->x0 = gsl_vector_get(s->x, 6); - } - - gsl_multifit_fdfsolver_free (s); - - return 0; -} - -void -ProjectionLSQ::get_coordinates(double alph, double a_nick, - const ViewParams *parms, double *x, double *y) { - - // Normalize alph - parms->a_center to [-pi/2, pi/2] - if (alph - parms->a_center > pi) - alph -= 2.0 * pi; - else if (alph - parms->a_center < -pi) - alph += 2.0 * pi; - - *x = mac_x(parms->a_center, parms->a_nick, parms->a_tilt, parms->scale, - parms->k0, parms->k1, parms->x0, alph, a_nick); - *y = mac_y(parms->a_center, parms->a_nick, parms->a_tilt, parms->scale, - parms->k0, parms->k1, parms->x0, alph, a_nick); -} - -double -ProjectionLSQ::comp_scale(double a1, double a2, double d1, double d2) { - return (fabs(d1 - d2) / fabs(a1 - a2)); -} - -#define ARGS double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick - -double ProjectionLSQ::mac_x(ARGS) {return NAN;} -double ProjectionLSQ::mac_y(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dc_view(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dc_nick(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dc_tilt(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dscale(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dk0(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dk1(ARGS) {return NAN;} -double ProjectionLSQ::mac_x_dx0(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dc_view(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dc_nick(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dc_tilt(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dscale(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dk0(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dk1(ARGS) {return NAN;} -double ProjectionLSQ::mac_y_dx0(ARGS) {return NAN;} diff --git a/src/ProjectionRectilinear.H b/src/ProjectionRectilinear.H deleted file mode 100644 index 2d799e3..0000000 --- a/src/ProjectionRectilinear.H +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef PROJECTIONRECTILINEAR_H -#define PROJECTIONRECTILINEAR_H - -#include "ProjectionLSQ.H" - -class ProjectionRectilinear : public ProjectionLSQ { - public: - - virtual double get_view_angle() {return 1.0471976;}; /* 60 deg */ - -#define ARGS double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick - virtual double mac_x(ARGS); - virtual double mac_y(ARGS); - virtual double mac_x_dc_view(ARGS); - virtual double mac_x_dc_nick(ARGS); - virtual double mac_x_dc_tilt(ARGS); - virtual double mac_x_dscale(ARGS); - virtual double mac_x_dk0(ARGS); - virtual double mac_x_dk1(ARGS); - virtual double mac_x_dx0(ARGS); - virtual double mac_y_dc_view(ARGS); - virtual double mac_y_dc_nick(ARGS); - virtual double mac_y_dc_tilt(ARGS); - virtual double mac_y_dscale(ARGS); - virtual double mac_y_dk0(ARGS); - virtual double mac_y_dk1(ARGS); - virtual double mac_y_dx0(ARGS); -#undef ARGS - -}; - -#endif diff --git a/src/ProjectionRectilinear.cxx b/src/ProjectionRectilinear.cxx deleted file mode 100644 index 33cb3a7..0000000 --- a/src/ProjectionRectilinear.cxx +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <math.h> - -#include "ProjectionRectilinear.H" - -#include "ProjectionRectilinear_funcs.cxx" diff --git a/src/ScanImage.H b/src/ScanImage.H deleted file mode 100644 index 1c1b91a..0000000 --- a/src/ScanImage.H +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef ScanImage_H -#define ScanImage_H - -#include <FL/Fl_Image.H> - -class ScanImage { - private: - static int get_pixel_nearest(Fl_Image *img, double x, double y, - int *r, int *g, int *b); - static int get_pixel_bicubic(Fl_Image *img, double x, double y, - int *r, int *g, int *b); - static int get_pixel(Fl_Image *img, int x, int y, - int *r, int *g, int *b); - - public: - typedef enum { - NEAREST = 0, - BICUBIC = 1 - } mode_t; - - static int get_pixel(Fl_Image *img, mode_t mode, - double x, double y, int *r, int *g, int *b); -}; - -#endif diff --git a/src/ScanImage.cxx b/src/ScanImage.cxx deleted file mode 100644 index 39f66a7..0000000 --- a/src/ScanImage.cxx +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright 2007-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <math.h> - -#include <FL/Fl_Image.H> -#include "ScanImage.H" - -int -ScanImage::get_pixel(Fl_Image *img, mode_t mode, - double x, double y, int *r, int *g, int *b) { - if (mode == BICUBIC) - return get_pixel_bicubic(img, x, y, r, g, b); - else - return get_pixel_nearest(img, x, y, r, g, b); -} - -int -ScanImage::get_pixel_nearest(Fl_Image *img, double x, double y, - int *r, int *g, int *b) { - - if (isnan(x) || isnan(y)) - return 1; - else - return get_pixel(img, (int) rint(x), (int) rint(y), r, g, b); -} - -static inline double -interp_cubic(double x, double x2, double x3, double *v) { - double a0, a1, a2, a3; - - a0 = v[3] - v[2] - v[0] + v[1]; - a1 = v[0] - v[1] - a0; - a2 = v[2] - v[0]; - a3 = v[1]; - - return a0 * x3 + a1 * x2 + a2 * x + a3; -} - -int -ScanImage::get_pixel_bicubic(Fl_Image *img, double x, double y, - int *r, int *g, int *b) { - - double fl_x = floor(x); - double fl_y = floor(y); - double dx = x - fl_x, dx2 = dx * dx, dx3 = dx2 * dx; - double dy = y - fl_y, dy2 = dy * dy, dy3 = dy2 * dy; - int ic[3]; - double c[3][4]; - double c1[3][4]; - - for (int iy = 0; iy < 4; iy++) { - for (int ix = 0; ix < 4; ix++) { - - if (get_pixel(img, (int) fl_x + ix - 1, (int) fl_y + iy - 1, - &ic[0], &ic[1], &ic[2]) != 0) - return 1; - - for (int l = 0; l < 3; l++) - c[l][ix] = (double) ic[l]; - } - - for (int l = 0; l < 3; l++) - c1[l][iy] = interp_cubic(dx, dx2, dx3, c[l]); - } - - *r = (int) rint(interp_cubic(dy, dy2, dy3, c1[0])); - *g = (int) rint(interp_cubic(dy, dy2, dy3, c1[1])); - *b = (int) rint(interp_cubic(dy, dy2, dy3, c1[2])); - return 0; -} - -int -ScanImage::get_pixel(Fl_Image *img, int x, int y, - int *r, int *g, int *b) { - if ( img->d() == 0 ) - return 1; - - if (x < 0 || x >=img->w() || y < 0 || y >= img->h()) - return 1; - - long index = (y * img->w() * img->d()) + (x * img->d()); // X/Y -> buf index - switch (img->count()) { - case 1: - { // bitmap - const unsigned char *buf = (const unsigned char*) img->data()[0]; - switch (img->d()) - { - case 1: - *r = *g = *b = *(buf+index); - break; - case 3: // 24bit - *r = (int) *(buf+index+0); - *g = (int) *(buf+index+1); - *b = (int) *(buf+index+2); - break; - default: // ?? - printf("Not supported: chans=%d\n", img->d()); - return 1; - } - break; - } - default: // ?? pixmap, bit vals - printf("Not supported: count=%d\n", img->count()); - return 1; - } - - *r = *r * 255; - *g = *g * 255; - *b = *b * 255; - - return 0; -} diff --git a/src/ScreenDump.H b/src/ScreenDump.H deleted file mode 100644 index 8564054..0000000 --- a/src/ScreenDump.H +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef SCREENDUMP_H -#define SCREENDUMP_H - -#include <FL/Fl_Widget.H> - -#include "OutputImage.H" - -class ScreenDump { - private: - int w, h; - unsigned char * rgb; - - public: - ScreenDump(Fl_Widget *widget); - ~ScreenDump(); - - int save(OutputImage *out); -}; - -#endif diff --git a/src/ScreenDump.cxx b/src/ScreenDump.cxx deleted file mode 100644 index f331a76..0000000 --- a/src/ScreenDump.cxx +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright 2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <FL/Fl.H> -#include <FL/x.H> -#include <FL/fl_draw.H> - -#include "ScreenDump.H" - -ScreenDump::ScreenDump(Fl_Widget *widget) { - Fl_Offscreen offscreen; - int x, y; - - x = widget->x(); - y = widget->y(); - w = widget->w(); - h = widget->h(); - - Fl::flush(); - widget->resize(0, 0, w, h); - - offscreen = fl_create_offscreen(w, h); - fl_begin_offscreen(offscreen); - widget->redraw(); - widget->draw(); - fl_color(FL_YELLOW); - fl_draw("created with gipfel", w - 80, h - 10); - rgb = fl_read_image(NULL, 0, 0, w, h); - fl_end_offscreen(); - fl_delete_offscreen(offscreen); - - widget->resize(x, y, w, h); -} - -ScreenDump::~ScreenDump() { - if (rgb) - delete[] rgb; -} - -int -ScreenDump::save(OutputImage *out) { - if (!rgb) - return 1; - - out->init(w, h); - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - unsigned char *px = &rgb[(y * w + x) * 3]; - out->set_pixel(x, px[0] * 255, px[1] * 255, px[2] * 255); - } - out->next_line(); - } - - return out->done(); -} diff --git a/src/Stitch.H b/src/Stitch.H deleted file mode 100644 index 7c9a60e..0000000 --- a/src/Stitch.H +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef STITCH_H -#define STITCH_H - -#include "GipfelWidget.H" -#include "OutputImage.H" -#include "ScanImage.H" - -#define MAX_PICS 256 - -class Stitch { - private: - GipfelWidget *gipf[MAX_PICS]; - int num_pics; - OutputImage *merged_image; - - public: - Stitch(); - ~Stitch(); - - int load_image(char *file); - OutputImage * set_output(OutputImage *img); - int resample(ScanImage::mode_t m, - int w, int h, double view_start, double view_end); -}; - -#endif diff --git a/src/Stitch.cxx b/src/Stitch.cxx deleted file mode 100644 index 7b9fc04..0000000 --- a/src/Stitch.cxx +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright 2007-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> -#include <algorithm> - -#include <gsl/gsl_multifit.h> - -#include <FL/Fl.H> - -#include "OutputImage.H" -#include "Stitch.H" - -#define MAX_VALUE 65025 - -static double pi_d = asin(1.0) * 2.0; -static double deg2rad = pi_d / 180.0; - -Stitch::Stitch() { - for (int i = 0; i < MAX_PICS; i++) - gipf[i] = NULL; - - merged_image = NULL; - num_pics = 0; -} - -Stitch::~Stitch() { - for (int i = 0; i < MAX_PICS; i++) - if (gipf[i]) - delete gipf[i]; - else - break; -} - -int -Stitch::load_image(char *file) { - for (int i = 0; i < MAX_PICS; i++) { - if (gipf[i] == NULL) { - gipf[i] = new GipfelWidget(0, 0, 800, 600, NULL); - if (gipf[i]->load_image(file) != 0) { - delete gipf[i]; - gipf[i] = NULL; - } else { - num_pics++; - } - break; - } - } - - return 0; -} - -OutputImage* -Stitch::set_output(OutputImage *img) { - OutputImage *ret = merged_image; - merged_image = img; - return ret; -} - -int -Stitch::resample(ScanImage::mode_t m, - int w, int h, double view_start, double view_end) { - - view_start = view_start * deg2rad; - view_end = view_end * deg2rad; - - double step_view = (view_end - view_start) / w; - int r, g, b; - int y_off = h / 2; - int merged_pixel_set; - double radius = (double) w / (view_end -view_start); - - if (merged_image) - if (merged_image->init(w, h) != 0) - merged_image = NULL; - - for (int y = 0; y < h; y++) { - double a_nick = atan((double)(y_off - y)/radius); - - for (int x = 0; x < w; x++) { - double a_view; - a_view = view_start + x * step_view; - merged_pixel_set = 0; - for (int i = 0; i < num_pics; i++) { - if (merged_pixel_set) - continue; - - if (gipf[i]->get_pixel(m, a_view, a_nick, - &r, &g, &b) == 0) { - - r = std::max(std::min(r, MAX_VALUE), 0); - g = std::max(std::min(g, MAX_VALUE), 0); - b = std::max(std::min(b, MAX_VALUE), 0); - - if (!merged_pixel_set && merged_image) { - merged_image->set_pixel(x, r, g, b); - merged_pixel_set++; - } - } - } - } - - if (merged_image) - merged_image->next_line(); - } - - if (merged_image) - merged_image->done(); - - return 0; -} diff --git a/src/TIFFOutputImage.H b/src/TIFFOutputImage.H deleted file mode 100644 index b5409eb..0000000 --- a/src/TIFFOutputImage.H +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright 2006 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef TIFFOUTPUTIMAGE_H -#define TIFFOUTPUTIMAGE_H - -#include <stdio.h> -#include <tiffio.h> - -#include "OutputImage.H" - -class TIFFOutputImage : public OutputImage { - private: - int bitspersample; - unsigned char *row; - char *file; - TIFF *tiff; - - protected: - int init_internal(); - int set_pixel_internal(int x, int r, int g, int b); - int next_line_internal(); - int done_internal(); - - public: - TIFFOutputImage(const char *file, int b = 8); - ~TIFFOutputImage(); -}; - -#endif diff --git a/src/TIFFOutputImage.cxx b/src/TIFFOutputImage.cxx deleted file mode 100644 index e9f6a2b..0000000 --- a/src/TIFFOutputImage.cxx +++ /dev/null @@ -1,97 +0,0 @@ -// -// Copyright 2006 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "TIFFOutputImage.H" - -TIFFOutputImage::TIFFOutputImage(const char *f, int b) : OutputImage () { - bitspersample = (b==16)?16:8; - file = strdup(f); - tiff = NULL; - row = NULL; -} - -TIFFOutputImage::~TIFFOutputImage() { - if (row) - free(row); - - if (file) - free(file); -} - -int -TIFFOutputImage::init_internal() { - if (row) - free(row); - row = NULL; - - row = (unsigned char*) calloc((bitspersample / 8) * 4 * W, sizeof(char)); - if (!row) { - perror("calloc"); - return 1; - } - - if (tiff) - TIFFClose(tiff); - - if ((tiff = TIFFOpen(file, "w")) == NULL){ - fprintf(stderr, "can't open %s\n", file); - return 1; - } - - TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, W); - TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, H); - TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); - TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); - TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1); - TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bitspersample); - TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4); - - return 0; -} - -int -TIFFOutputImage::set_pixel_internal(int x, int r, int g, int b) { - if (bitspersample == 8) { - row[x*4+0] = (unsigned char) (r / 255); - row[x*4+1] = (unsigned char) (g / 255); - row[x*4+2] = (unsigned char) (b / 255); - row[x*4+3] = 255; - } else if (bitspersample == 16) { - unsigned short *row16 = (unsigned short*) row; - row16[x*4+0] = (unsigned short) r; - row16[x*4+1] = (unsigned short) g; - row16[x*4+2] = (unsigned short) b; - row16[x*4+3] = 65025; - } - - return 0; -} - -int -TIFFOutputImage::next_line_internal() { - TIFFWriteEncodedStrip(tiff, line - 1 , row, W * (bitspersample / 8) * 4); - - memset(row, 0, (bitspersample / 8) * 4 * W * sizeof(char)); - return 0; -} - -int -TIFFOutputImage::done_internal() { - if (tiff) - TIFFClose(tiff); - tiff = NULL; - - if (row) - free(row); - row = NULL; - - return 0; -} diff --git a/src/ViewParams.H b/src/ViewParams.H deleted file mode 100644 index b53a8df..0000000 --- a/src/ViewParams.H +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef VIEWPARAMS_H -#define VIEWPARAMS_H - -class ViewParams { - public: - double a_center; - double scale; - double a_nick; - double a_tilt; - double k0; - double k1; - double x0; -}; - -#endif diff --git a/src/choose_hill.H b/src/choose_hill.H deleted file mode 100644 index 27105dc..0000000 --- a/src/choose_hill.H +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#ifndef CHOOSE_HILL_H -#define CHOOSE_HILL_H - -#include "Fl_Search_Chooser.H" -#include "Hill.H" - -Hill *choose_hill(const Hills *hills, const char *l); - -#endif diff --git a/src/choose_hill.cxx b/src/choose_hill.cxx deleted file mode 100644 index 48e44d5..0000000 --- a/src/choose_hill.cxx +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <string.h> -#include <FL/Fl.H> -#include "choose_hill.H" - -Hill* -choose_hill(const Hills *hills, const char *l) { - Fl_Search_Chooser *sc = new Fl_Search_Chooser(l?l:"Choose Hill"); - Hills *h_sort = new Hills(hills); - Hill *ret; - - h_sort->sort(Hills::SORT_NAME); - - for (int i=0; i<h_sort->get_num(); i++) { - char buf[256]; - - Hill *m = h_sort->get(i); - if (m->flags & (Hill::DUPLIC | Hill::TRACK_POINT)) - continue; - - snprintf(buf, sizeof(buf) - 1, "%s (%dm)", m->name, (int) m->height); - buf[sizeof(buf) - 1] = '\0'; - sc->add(buf, m); - } - - delete h_sort; - - sc->show(); - - while (sc->shown()) - Fl::wait(); - - ret = (Hill*) sc->data(); - delete sc; - - return ret; -} diff --git a/src/expr2c.mac b/src/expr2c.mac deleted file mode 100644 index 1522169..0000000 --- a/src/expr2c.mac +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> - */ - -load("stringproc")$ - -varsubst(v) := ssubst("_", "%", string(v))$ - -printdecl(vars) := ( - sprint("double", varsubst(first(vars))), - for v in rest(vars) do sprint(",", varsubst(v)), - sprint(";"))$ - -printdef(expr) := sprint(varsubst(first(expr)), "=", varsubst(second(expr)), ";")$ - -block2c(expr) := - if ?equal(op(expr), block) then ( - printdecl(first(expr)), - for d in reverse(rest(reverse(rest(expr)))) do printdef(d), - sprint("return", varsubst(last(expr)), ";")) - else - sprint("return", varsubst(expr), ";")$ - -expr2c(funcname, argstr, expr) := ( - sprint("double", funcname, "(", argstr, ") {"), - block2c(subst(pow, "^", optimize(expr))), - sprint("}", " -"))$ diff --git a/src/gipfel.cxx b/src/gipfel.cxx deleted file mode 100644 index bed5035..0000000 --- a/src/gipfel.cxx +++ /dev/null @@ -1,738 +0,0 @@ -// -// Copyright 2007-2023 Johannes Hofmann <Johannes.Hofmann@gmx.de> -// -// This software may be used and distributed according to the terms -// of the GNU General Public License, incorporated herein by reference. - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <stdlib.h> -#include <libgen.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <math.h> -#include <assert.h> -#include <algorithm> - -#include <FL/Fl.H> -#include <FL/Fl_Window.H> -#include <FL/Fl_Scroll.H> -#include <FL/Fl_File_Chooser.H> -#include <FL/Fl_Input.H> -#include <FL/Fl_Int_Input.H> -#include <FL/Fl_Menu_Bar.H> -#include <FL/Fl_Menu_Item.H> -#include <FL/Fl_Valuator.H> -#include <FL/Fl_Value_Slider.H> -#include <FL/Fl_Value_Input.H> -#include <FL/Fl_Text_Display.H> -#include <FL/Fl_Text_Buffer.H> - -#include "Fl_Value_Dial.H" -#include "Fl_Search_Chooser.H" -#include "GipfelWidget.H" -#include "JPEGOutputImage.H" -#include "TIFFOutputImage.H" -#include "PreviewOutputImage.H" -#include "Stitch.H" -#include "ScreenDump.H" -#include "choose_hill.H" -#include "../config.h" - -#ifndef STD_DATADIR -#define STD_DATADIR "/usr/local/share" -#endif - -#define GIPFEL_DATADIR STD_DATADIR "/" PACKAGE_NAME -#define DATAFILE "gipfel.dat" - -static char *run_dir = NULL; -static char *img_file = NULL; -static char *data_file = NULL; - -static GipfelWidget *gipf = NULL; -static Fl_Scroll *scroll; -static Fl_Window *control_win, *view_win; -static Fl_Dial *s_center = NULL; -static Fl_Slider *s_nick, *s_focal_length, *s_tilt, *s_height_dist, *s_track_width; -static Fl_Value_Input *i_view_lat, *i_view_long, *i_view_height; -static Fl_Value_Input *i_distortion_k0, *i_distortion_k1, *i_distortion_x0; -static Fl_Box *b_viewpoint; -static Fl_Menu_Bar *mb; - -#define STITCH_PREVIEW 1 -#define STITCH_JPEG 2 -#define STITCH_TIFF 4 - -static int stitch(ScanImage::mode_t m , int b_16, - int stitch_w, int stitch_h, - double from, double to, int type, const char *path, int argc, char **argv); - -static int export_hills(const char *export_file, double visibility); -static int export_position(); - -static int -confirm_overwrite(const char *f) { - struct stat sb; - - if (stat(f, &sb) == 0) - return fl_choice("%s exists.\n", "Cancel", "Overwrite", NULL, f); - else - return 1; -} - -static char * -file_installed_or_local(const char *inst_dir, const char *name) { - assert(run_dir); - - struct stat sb; - int buflen = strlen(inst_dir) + strlen(run_dir) + strlen(name) + 1; - char *buf = (char *) malloc (buflen); - - snprintf(buf, buflen, "%s/%s", inst_dir, name); - if (stat(buf, &sb) == 0) - return buf; - - snprintf(buf, buflen, "%s/../%s", run_dir, name); - if (stat(buf, &sb) == 0) - return buf; - - free(buf); - return NULL; -} - -void set_values() { - double k0 = 0.0, k1 = 0.0, x0 = 0.0; - - s_center->value(gipf->get_center_angle()); - s_nick->value(gipf->get_nick_angle()); - s_focal_length->value(gipf->get_focal_length_35mm()); - s_tilt->value(gipf->get_tilt_angle()); - s_height_dist->value(gipf->get_height_dist_ratio()); - i_view_lat->value(gipf->get_view_lat()); - i_view_long->value(gipf->get_view_long()); - i_view_height->value(gipf->get_view_height()); - b_viewpoint->label(gipf->get_viewpoint()); - if (gipf->projection() == ProjectionLSQ::RECTILINEAR) { - mb->mode(9, FL_MENU_RADIO|FL_MENU_VALUE); - mb->mode(10, FL_MENU_RADIO); - } else { - mb->mode(10, FL_MENU_RADIO|FL_MENU_VALUE); - mb->mode(9, FL_MENU_RADIO); - } - - gipf->get_distortion_params(&k0, &k1, &x0); - i_distortion_k0->value(k0); - i_distortion_k1->value(k1); - i_distortion_x0->value(x0); -} - -void quit_cb() { - if (Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape) - return; // ignore Escape - exit(0); -} - -void open_cb() { - char *file = fl_file_chooser("Open File?", "*.jpg", img_file); - if (file) { - gipf->load_image(file); - scroll->position(0, 0); - view_win->label(file); - view_win->redraw(); - control_win->label(file); - set_values(); - if (img_file) - free(img_file); - img_file = strdup(file); - } -} - -void track_cb() { - char *file = fl_file_chooser("Track File?", NULL, NULL); - if (file && gipf->load_track(file) == 0) - s_track_width->activate(); -} - -void save_cb() { - char *file = fl_file_chooser("Save Image As?", NULL, img_file); - if (file && confirm_overwrite(file)) - if (gipf->save_image(file)) - fl_message("ERROR: Saving image %s failed.", file); -} - -void dump_cb(Fl_Widget * o, void*) { - fl_cursor(FL_CURSOR_WAIT); - ScreenDump dmp(gipf); // needs to be done before fl_file_chooser() - fl_cursor(FL_CURSOR_DEFAULT); - char *file = fl_file_chooser("Save Screen Dump As?", "*.jpg", NULL); - if (file && confirm_overwrite(file)) { - JPEGOutputImage out(file, 95); - if (dmp.save(&out)) - fl_message("ERROR: Saving image %s failed.", file); - } -} - -void focal_length_cb(Fl_Slider* o, void*) { - gipf->set_focal_length_35mm(o->value()); -} - -void angle_cb(Fl_Slider* o, void*) { - gipf->set_center_angle(o->value()); -} - -void nick_cb(Fl_Slider* o, void*) { - gipf->set_nick_angle(o->value()); -} - -void tilt_cb(Fl_Slider* o, void*) { - gipf->set_tilt_angle(o->value()); -} - -void h_d_cb(Fl_Slider* o, void*) { - gipf->set_height_dist_ratio(o->value()); -} - -void view_lat_cb(Fl_Value_Input* o, void*) { - gipf->set_view_lat(o->value()); -} - -void view_long_cb(Fl_Value_Input* o, void*) { - gipf->set_view_long(o->value()); -} - -void view_height_cb(Fl_Value_Input* o, void*) { - gipf->set_view_height(o->value()); -} - -void track_width_cb(Fl_Value_Input* o, void*) { - gipf->set_track_width(o->value()); -} - -void viewpoint_cb(Fl_Value_Input* o, void*) { - Hill *m = choose_hill(gipf->get_mountains(), "Choose Viewpoint"); - if (m) { - gipf->set_viewpoint(m); - set_values(); - } -} - -void proj_cb(Fl_Value_Input* o, void*d) { - if (d) - gipf->projection(ProjectionLSQ::CYLINDRICAL); - else - gipf->projection(ProjectionLSQ::RECTILINEAR); -} - -void hidden_cb(Fl_Menu_* o, void*d) { - gipf->set_show_hidden(o->mvalue()->value() != 0); -} - -void save_distortion_cb(Fl_Widget *, void *) { - char buf[1024]; - const char * prof_name; - double k0, k1, x0; - - buf[0] = '\0'; - gipf->get_distortion_params(&k0, &k1, &x0); - - gipf->get_distortion_profile_name(buf, sizeof(buf)); - prof_name = fl_input("Save Distortion Profile (k0=%f, k1=%f, x0=%f)", - buf, k0, k1, x0); - - if (prof_name == NULL) - return; - - if (gipf->save_distortion_params(prof_name, 0) != 0) { - if (fl_choice("A profile with this name exists.\n", - "Cancel", "Overwrite", NULL) == 1) - gipf->save_distortion_params(prof_name, 1); - } - set_values(); -} - -void load_distortion_cb(Fl_Widget *, void *) { - char buf[1024]; - const char * prof_name; - - buf[0] = '\0'; - gipf->get_distortion_profile_name(buf, sizeof(buf)); - prof_name = fl_input("Load Distortion Profile", buf); - - if (prof_name == NULL) - return; - - if (gipf->load_distortion_params(prof_name) != 0) - fl_alert("Could not load profile %s.", prof_name); - else - set_values(); -} - -void distortion_cb(Fl_Value_Input*, void*) { - gipf->set_distortion_params( - i_distortion_k0->value(), - i_distortion_k1->value(), - i_distortion_x0->value()); -} - -void about_cb() { - fl_message("gipfel -- Photogrammetry for Mountain Images.\n" - "Version %s\n\n" - "(c) Johannes Hofmann 2006-2023\n\n" - "Homepage: http://flpsed.org/gipfel.html\n\n" - "Default datafile from http://www.viewfinderpanoramas.org/ and\n" - "http://www.alpin-koordinaten.de\n", - VERSION); - -} - -void readme_cb() { - char *readme = file_installed_or_local(DOCDIR, "README"); - if (!readme) { - fprintf(stderr, "README file not found\n"); - return; - } - Fl_Window *win = new Fl_Window(800, 600, "README"); - Fl_Text_Buffer *textBuf = new Fl_Text_Buffer(); - Fl_Text_Display *textDisp = new Fl_Text_Display(0, 0, win->w(), win->h()); - - textDisp->textfont(FL_COURIER); - textDisp->buffer(textBuf); - win->resizable(textDisp); - textBuf->appendfile(readme); - free(readme); - win->end(); - win->show(); -} - -void fill_menubar(Fl_Menu_Bar* mb) { - mb->add("&File/L&oad Image", FL_CTRL+'o', (Fl_Callback*)open_cb); - mb->add("&File/&Save Image", FL_CTRL+'s', (Fl_Callback*)save_cb); - mb->add("&File/Choose &Viewpoint", FL_CTRL+'v', (Fl_Callback*)viewpoint_cb); - mb->add("&File/Load &Track", FL_CTRL+'t', (Fl_Callback*)track_cb); - mb->add("&File/Screen &Dump", FL_CTRL+'d', (Fl_Callback*)dump_cb); - mb->add("&File/&Quit", FL_CTRL+'q', (Fl_Callback*)quit_cb); - - mb->add("&Projection/Normal Projection", 0, (Fl_Callback *)proj_cb, - (void *)0, FL_MENU_RADIO|FL_MENU_VALUE); - mb->add("&Projection/Panoramic Projection", 0, (Fl_Callback *)proj_cb, - (void *)1, FL_MENU_RADIO); - - mb->add("&Distortion/Load Profile", 0, (Fl_Callback *)load_distortion_cb); - mb->add("&Distortion/Save Profile", 0, (Fl_Callback *)save_distortion_cb); - - mb->add("&Option/Show Hidden", 0, (Fl_Callback *) hidden_cb, - (void *)0, FL_MENU_TOGGLE); - - mb->add("&Help/Readme", 0, (Fl_Callback*)readme_cb); - mb->add("&Help/About", 0, (Fl_Callback*)about_cb); -} - -void usage() { - fprintf(stderr, - "usage: gipfel [-v <viewpoint>] [-d <file>]\n" - " [-s] [-j <file>] [-t <dir] [-w <width>] [-h <height>]\n" - " [-e <file>] [-E] [-p]\n" - " [<image(s)>]\n" - " -v <viewpoint> Set point from which the picture was taken.\n" - " This must be a string that unambiguously \n" - " matches the name of an entry in the data file.\n" - " -d <file> Use <file> for GPS data.\n" - " -V <visibility> Set initial visibility.\n" - " -u <k0>,<k1> Use distortion correction values k0,k1.\n" - " -s Stitch mode.\n" - " -4 Create 16bit output (only with TIFF stitching).\n" - " -r <from>,<to> Stitch range in degrees (e.g. 100.0,200.0).\n" - " -b Use bicubic interpolation for stitching.\n" - " -w <width> Width of result image.\n" - " -h <height> Height of result image.\n" - " -j <file> JPEG output file in Stitch mode.\n" - " -t <file> TIFF output file in Stitch mode.\n" - " -p Export position of image to stdout.\n" - " -e <file> Export positions of hills from <file> on image.\n" - " -E Export hills from default data file.\n" - " <image(s)> JPEG file(s) to use.\n"); -} - -Fl_Window * -create_control_window() { - Fl_Window *win = new Fl_Window(400,400); - mb = new Fl_Menu_Bar(0, 0, 400, 30); - fill_menubar(mb); - - s_center = new Fl_Value_Dial(40, 60, 150, 150, NULL); - s_center->type(FL_LINE_DIAL); - s_center->labelsize(10); - s_center->step(0.01); - s_center->bounds(0.0, 360.0); - s_center->angles(180, 540); - s_center->callback((Fl_Callback*)angle_cb); - - win->add(new Fl_Box(95, 40, 40, 20, "North")); - win->add(new Fl_Box(95, 210, 40, 20, "South")); - win->add(new Fl_Box(190, 125, 40, 20, "East")); - win->add(new Fl_Box(0, 125, 40, 20, "West")); - - s_focal_length = new Fl_Value_Slider(235, 60, 160, 15, "Focal Length in 35mm"); - s_focal_length->type(1); - s_focal_length->box(FL_THIN_DOWN_BOX); - s_focal_length->labelsize(10); - s_focal_length->step(0.01); - s_focal_length->bounds(1.0, 300.0); - s_focal_length->slider(FL_UP_BOX); - s_focal_length->callback((Fl_Callback*)focal_length_cb); - s_focal_length->align(FL_ALIGN_TOP); - - s_nick = new Fl_Value_Slider(235, 90, 160, 15, "Nick (deg.)"); - s_nick->type(1); - s_nick->box(FL_THIN_DOWN_BOX); - s_nick->labelsize(10); - s_nick->step(0.01); - s_nick->bounds(-40.0, 20.0); - s_nick->slider(FL_UP_BOX); - s_nick->callback((Fl_Callback*)nick_cb); - s_nick->align(FL_ALIGN_TOP); - - s_tilt = new Fl_Value_Slider(235, 120, 160, 15, "Tilt (deg.)"); - s_tilt->type(1); - s_tilt->box(FL_THIN_DOWN_BOX); - s_tilt->labelsize(10); - s_tilt->step(0.01); - s_tilt->bounds(-10.0, 10.0); - s_tilt->slider(FL_UP_BOX); - s_tilt->callback((Fl_Callback*)tilt_cb); - s_tilt->align(FL_ALIGN_TOP); - - s_height_dist = new Fl_Value_Slider(235, 150, 160, 15, "Visibility"); - s_height_dist->type(1); - s_height_dist->box(FL_THIN_DOWN_BOX); - s_height_dist->labelsize(10); - s_height_dist->step(-0.001); - s_height_dist->bounds(0.1, 0.01); - s_height_dist->slider(FL_UP_BOX); - s_height_dist->callback((Fl_Callback*)h_d_cb); - s_height_dist->align(FL_ALIGN_TOP); - - s_track_width = new Fl_Value_Slider(235, 180, 160, 15, "Track Width"); - s_track_width->type(1); - s_track_width->box(FL_THIN_DOWN_BOX); - s_track_width->labelsize(10); - s_track_width->step(1.0); - s_track_width->bounds(1.0, 500.0); - s_track_width->value(200.0); - s_track_width->slider(FL_UP_BOX); - s_track_width->callback((Fl_Callback*)track_width_cb); - s_track_width->align(FL_ALIGN_TOP); - s_track_width->deactivate(); - - // Viewpoint Stuff - b_viewpoint = new Fl_Box(FL_DOWN_BOX, 30, 305, 300, 80, ""); - b_viewpoint->align(FL_ALIGN_TOP); - - i_view_lat = new Fl_Value_Input(40, 320, 100, 20, "Latitude"); - i_view_lat->labelsize(10); - i_view_lat->align(FL_ALIGN_TOP); - i_view_lat->when(FL_WHEN_ENTER_KEY); - i_view_lat->callback((Fl_Callback*)view_lat_cb); - - i_view_long = new Fl_Value_Input(200, 320, 100, 20, "Longitude"); - i_view_long->labelsize(10); - i_view_long->align(FL_ALIGN_TOP); - i_view_long->when(FL_WHEN_ENTER_KEY); - i_view_long->callback((Fl_Callback*)view_long_cb); - - i_view_height = new Fl_Value_Input(40, 360, 80, 20, "Height"); - i_view_height->labelsize(10); - i_view_height->align(FL_ALIGN_TOP); - i_view_height->when(FL_WHEN_ENTER_KEY); - i_view_height->callback((Fl_Callback*)view_height_cb); - - i_distortion_k0 = new Fl_Value_Input(250, 200, 80, 20, "k0"); - i_distortion_k0->labelsize(10); - i_distortion_k0->textsize(10); - i_distortion_k0->align(FL_ALIGN_LEFT); - i_distortion_k0->when(FL_WHEN_ENTER_KEY); - i_distortion_k0->callback((Fl_Callback*)distortion_cb); - - i_distortion_k1 = new Fl_Value_Input(250, 225, 80, 20, "k1"); - i_distortion_k1->labelsize(10); - i_distortion_k1->textsize(10); - i_distortion_k1->align(FL_ALIGN_LEFT); - i_distortion_k1->when(FL_WHEN_ENTER_KEY); - i_distortion_k1->callback((Fl_Callback*)distortion_cb); - - i_distortion_x0 = new Fl_Value_Input(250, 250, 80, 20, "x0"); - i_distortion_x0->labelsize(10); - i_distortion_x0->textsize(10); - i_distortion_x0->align(FL_ALIGN_LEFT); - i_distortion_x0->when(FL_WHEN_ENTER_KEY); - i_distortion_x0->callback((Fl_Callback*)distortion_cb); - - win->end(); - return win; -} - -int main(int argc, char** argv) { - char c, **my_argv; - char *view_point = NULL; - int err, my_argc, sx, sy, sw, sh; - int stitch_flag = 0, stitch_w = 2000, stitch_h = 500; - int jpeg_flag = 0, tiff_flag = 0, distortion_flag = 0, position_flag = 0; - int export_flag = 0; - int bicubic_flag = 0, b_16_flag = 0; - double stitch_from = 0.0, stitch_to = 380.0; - double dist_k0 = 0.0, dist_k1 = 0.0, dist_x0 = 0.0; - double visibility = 0.07; - const char *outpath = "/tmp"; - const char *export_file = NULL; - - err = 0; - while ((c = getopt(argc, argv, ":?d:v:sw:h:j:t:u:br:4e:V:pE")) != EOF) { - switch (c) { - case '?': - usage(); - exit(0); - break; - case 'd': - data_file = optarg; - break; - case 'e': - export_flag++; - export_file = optarg; - break; - case 'E': - export_flag++; - break; - case 'v': - view_point = optarg; - break; - case 'V': - visibility = atof(optarg); - break; - case 's': - stitch_flag++; - break; - case 'p': - position_flag++; - break; - case '4': - b_16_flag++; - break; - case 'r': - stitch_flag++; - if (optarg && strcmp(optarg, ":")) { - stitch_from = atof(optarg); - if (strchr(optarg, ',')) - stitch_to = atof(strchr(optarg, ',') + 1); - } - break; - case 'u': - char *c; - distortion_flag++; - if (optarg && strcmp(optarg, ":")) { - dist_k0 = atof(optarg); - c = strchr(optarg, ','); - if (c) { - dist_k1 = atof(strchr(optarg, ',') + 1); - c = strchr(c + 1, ','); - if (c) - dist_x0 = atof(strchr(optarg, ',') + 1); - } - } - break; - case 'j': - jpeg_flag++; - outpath = optarg; - break; - case 't': - tiff_flag++; - outpath = optarg; - break; - case 'w': - stitch_w = atoi(optarg); - break; - case 'h': - stitch_h = atoi(optarg); - break; - case 'b': - bicubic_flag++; - break; - default: - err++; - } - } - - my_argc = argc - optind; - my_argv = argv + optind; - - char *exec_file = strdup(argv[0]); - run_dir = strdup(dirname(exec_file)); - free(exec_file); - - if (my_argc >= 1) - img_file = strdup(my_argv[0]); - - if (data_file == NULL) - data_file = file_installed_or_local(GIPFEL_DATADIR, "gipfel.dat"); - - if (data_file == NULL || err) { - usage(); - exit(1); - } - - if (stitch_flag) { - int type = 0; - if (jpeg_flag) { - type = STITCH_JPEG; - } else if (tiff_flag) { - type = STITCH_TIFF; - } else { - type = STITCH_PREVIEW; - } - - return stitch(bicubic_flag ? ScanImage::BICUBIC : ScanImage::NEAREST, - b_16_flag, - stitch_w, stitch_h, stitch_from, stitch_to, - type, outpath, my_argc, my_argv); - - } else if (export_flag) { - return export_hills(export_file, visibility); - } else if (position_flag) { - return export_position(); - } - - Fl::get_system_colors(); - if (getenv("FLTK_SCHEME")) - Fl::scheme(NULL); - else - Fl::scheme("plastic"); - - control_win = create_control_window(); - control_win->callback((Fl_Callback*) quit_cb); - - view_win = new Fl_Window(800, 600); - view_win->callback((Fl_Callback*) quit_cb); - - // The Fl_Group is used to avoid FL_DAMAGE_ALL in Fl_Scroll::position - Fl_Group *g = new Fl_Group(0, 0, view_win->w(), view_win->h()); - view_win->resizable(g); - scroll = new Fl_Scroll(0, 0, view_win->w(), view_win->h()); - - gipf = new GipfelWidget(0, 0, 800, 600, set_values); - if (img_file) { - gipf->load_image(img_file); - view_win->label(img_file); - control_win->label(img_file); - } - - if (distortion_flag) - gipf->set_distortion_params(dist_k0, dist_k1, dist_x0); - - Fl::screen_xywh(sx, sy, sw, sh); - view_win->size(std::min(gipf->w(), sw), std::min(gipf->h(), sh)); - scroll->size(view_win->w(), view_win->h()); - - gipf->load_data(data_file); - gipf->set_height_dist_ratio(visibility); - - scroll->end(); - - set_values(); - - view_win->resizable(scroll); - - view_win->end(); - view_win->show(1, argv); - control_win->show(1, argv); - - if (view_point) - gipf->set_viewpoint(view_point); - else if (img_file && - (isnan(gipf->get_view_lat()) || isnan(gipf->get_view_long()))) - viewpoint_cb(NULL, NULL); - - return Fl::run(); -} - -static int -stitch(ScanImage::mode_t m, int b_16, - int stitch_w, int stitch_h, double from, double to, - int type, const char *path, int argc, char **argv) { - - Fl_Window *win; - Fl_Scroll *scroll; - Stitch *st = new Stitch(); - - for (int i = 0; i < argc; i++) - st->load_image(argv[i]); - - if (type & STITCH_JPEG) { - - st->set_output(new JPEGOutputImage(path, 90)); - st->resample(m, stitch_w, stitch_h, from, to); - - } else if (type & STITCH_TIFF) { - - st->set_output(new TIFFOutputImage(path, b_16 ? 16 : 8)); - st->resample(m, stitch_w, stitch_h, from, to); - - } else { - win = new Fl_Window(0,0, stitch_w, stitch_h); - scroll = new Fl_Scroll(0, 0, win->w(), win->h()); - PreviewOutputImage *img = - new PreviewOutputImage(0, 0, stitch_w, stitch_h); - - win->resizable(scroll); - win->show(0, argv); - st->set_output(img); - - st->resample(m, stitch_w, stitch_h, from, to); - - img->redraw(); - Fl::run(); - } - - return 0; -} - -static int -export_hills(const char *export_file, double visibility) { - int ret; - - if (!img_file) { - fprintf(stderr, "export: No image file given.\n"); - return 1; - } - - gipf = new GipfelWidget(0,0,800,600, NULL); - gipf->load_image(img_file); - gipf->load_data(data_file); - gipf->set_height_dist_ratio(visibility); - ret = gipf->export_hills(export_file, stdout); - delete gipf; - gipf = NULL; - - return ret; -} - -static int -export_position() { - ImageMetaData md; - - if (!img_file) { - fprintf(stderr, "export: No image file given.\n"); - return 1; - } - - if (md.load_image(img_file) == 0) { - printf(",%s,,%f,%f,%d\n", img_file, - md.latitude(), - md.longitude(), - (int) rint(md.height())); - - return 0; - } else { - return 1; - } -} diff --git a/src/lsq_cylindrical.mac b/src/lsq_cylindrical.mac deleted file mode 100644 index ab706f5..0000000 --- a/src/lsq_cylindrical.mac +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> - */ - -/* - * cylindrical projection model - */ - -x : m_view - c_view$ -y : tan(c_nick - m_nick)$ -x_rot : y * sin(c_tilt) + x * cos(c_tilt)$ -y_rot : y * cos(c_tilt) - x * sin(c_tilt)$ -x_scale : x_rot * scale$ -y_scale : y_rot * scale$ - -/* - * Some mangling for C code generation - */ - -load("expr2c.mac")$ - -x_expand : trigexpand(x_scale)$ -y_expand : trigexpand(y_scale)$ - -args: "double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick"$ - -expr2c("ProjectionCylindrical::mac_x", args, x_expand)$ -expr2c("ProjectionCylindrical::mac_y", args, y_expand)$ - -expr2c("ProjectionCylindrical::mac_x_dc_view", args, diff(x_expand, c_view))$ -expr2c("ProjectionCylindrical::mac_x_dc_nick", args, diff(x_expand, c_nick))$ -expr2c("ProjectionCylindrical::mac_x_dc_tilt", args, diff(x_expand, c_tilt))$ -expr2c("ProjectionCylindrical::mac_x_dscale", args, diff(x_expand, scale))$ -expr2c("ProjectionCylindrical::mac_y_dc_view", args, diff(y_expand, c_view))$ -expr2c("ProjectionCylindrical::mac_y_dc_nick", args, diff(y_expand, c_nick))$ -expr2c("ProjectionCylindrical::mac_y_dc_tilt", args, diff(y_expand, c_tilt))$ -expr2c("ProjectionCylindrical::mac_y_dscale", args, diff(y_expand, scale))$ - diff --git a/src/lsq_rectilinear.mac b/src/lsq_rectilinear.mac deleted file mode 100644 index 95ed273..0000000 --- a/src/lsq_rectilinear.mac +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2006-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de> - */ - -/* - * rectilinear (pinhole) projection model with distortion correction - */ - -/* switch to cartesian coordinates */ -c_x : cos(m_nick) * cos(m_view - c_view)$ -c_y : cos(m_nick) * sin(m_view - c_view)$ -c_z : sin(m_nick)$ - -/* nick rotation */ -c_x_rot : cos(c_nick) * c_x + sin(c_nick) * c_z$ -c_y_rot : c_y$ -c_z_rot : -sin(c_nick) * c_x + cos(c_nick) * c_z$ - -/* pinhole projection */ -x : c_y_rot / c_x_rot$ -y : -c_z_rot / c_x_rot$ - -/* rotation around view axis */ -x_rot : y * sin(c_tilt) + x * cos(c_tilt) + x0$ -y_rot : y * cos(c_tilt) - x * sin(c_tilt)$ - -/* distortion correction */ -d : sqrt(x_rot ^ 2 + y_rot ^ 2)$ -dist_fact : d ^ 2 * k1 + d * k0$ -x_dist : x_rot * (1 + dist_fact) * scale$ -y_dist : y_rot * (1 + dist_fact) * scale$ - -/* - * Some mangling for C code generation - */ - -load("expr2c.mac")$ - -x_expand : trigexpand(x_dist)$ -y_expand : trigexpand(y_dist)$ - -args: "double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick"$ - -expr2c("ProjectionRectilinear::mac_x", args, x_expand)$ -expr2c("ProjectionRectilinear::mac_y", args, y_expand)$ - -expr2c("ProjectionRectilinear::mac_x_dc_view", args, diff(x_expand, c_view))$ -expr2c("ProjectionRectilinear::mac_x_dc_nick", args, diff(x_expand, c_nick))$ -expr2c("ProjectionRectilinear::mac_x_dc_tilt", args, diff(x_expand, c_tilt))$ -expr2c("ProjectionRectilinear::mac_x_dscale", args, diff(x_expand, scale))$ -expr2c("ProjectionRectilinear::mac_x_dk0", args, diff(x_expand, k0))$ -expr2c("ProjectionRectilinear::mac_x_dk1", args, diff(x_expand, k1))$ -expr2c("ProjectionRectilinear::mac_x_dx0", args, diff(x_expand, x0))$ -expr2c("ProjectionRectilinear::mac_y_dc_view", args, diff(y_expand, c_view))$ -expr2c("ProjectionRectilinear::mac_y_dc_nick", args, diff(y_expand, c_nick))$ -expr2c("ProjectionRectilinear::mac_y_dc_tilt", args, diff(y_expand, c_tilt))$ -expr2c("ProjectionRectilinear::mac_y_dscale", args, diff(y_expand, scale))$ -expr2c("ProjectionRectilinear::mac_y_dk0", args, diff(y_expand, k0))$ -expr2c("ProjectionRectilinear::mac_y_dk1", args, diff(y_expand, k1))$ -expr2c("ProjectionRectilinear::mac_y_dx0", args, diff(y_expand, x0))$ diff --git a/src/strsep.c b/src/strsep.c deleted file mode 100644 index c8e4be0..0000000 --- a/src/strsep.c +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (C) 1992, 93, 96, 97, 98, 99, 2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#include "../config.h" - -#ifndef HAVE_STRSEP - -#include "string.h" - -char * -strsep (char **stringp, const char *delim) -{ - char *begin, *end; - - begin = *stringp; - if (begin == NULL) - return NULL; - - /* A frequent case is when the delimiter string contains only one - character. Here we don't need to call the expensive `strpbrk' - function and instead work using `strchr'. */ - if (delim[0] == '\0' || delim[1] == '\0') - { - char ch = delim[0]; - - if (ch == '\0') - end = NULL; - else - { - if (*begin == ch) - end = begin; - else if (*begin == '\0') - end = NULL; - else - end = strchr (begin + 1, ch); - } - } - else - /* Find the end of the token. */ - end = strpbrk (begin, delim); - - if (end) - { - /* Terminate the token and set *STRINGP past NUL character. */ - *end++ = '\0'; - *stringp = end; - } - else - /* No more delimiters; this is the last token. */ - *stringp = NULL; - - return begin; -} - -#endif diff --git a/src/strsep.h b/src/strsep.h deleted file mode 100644 index 28295ba..0000000 --- a/src/strsep.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2008 Johannes Hofmann <Johannes.Hofmann@gmx.de> - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include "../config.h" - -#ifndef HAVE_STRSEP -char * strsep (char **stringp, const char *delim); -#endif diff --git a/tests/gen_golden.sh b/tests/gen_golden.sh new file mode 100755 index 0000000..c23ec28 --- /dev/null +++ b/tests/gen_golden.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# Generate golden reference data from the original C++ implementation. +# Requires: the gipfel C++ sources built (objects in $SRC), g++, GSL, maxima. +set -e +cd "$(dirname "$0")" + +SRC=${GIPFEL_SRC:-$HOME/gipfel/src} +DATA=${GIPFEL_DATA:-$HOME/gipfel/gipfel.dat} +BUILD=harness/build +mkdir -p $BUILD golden + +# rename class-scoped generated functions to free functions +sed 's/ProjectionRectilinear::/rect_/g' $SRC/ProjectionRectilinear_funcs.cxx > $BUILD/rect_funcs.cxx +sed 's/ProjectionCylindrical::/cyl_/g' $SRC/ProjectionCylindrical_funcs.cxx > $BUILD/cyl_funcs.cxx + +g++ -O2 -o $BUILD/funcsdump harness/funcsdump.cxx -I$BUILD -lm +$BUILD/funcsdump > golden/funcs.txt + +g++ -O2 -o $BUILD/fitdump harness/fitdump.cxx \ + -I$SRC $SRC/Panorama.o $SRC/Hill.o $SRC/ProjectionLSQ.o \ + $SRC/ProjectionRectilinear.o $SRC/ProjectionCylindrical.o \ + -lm -lgsl -lgslcblas + +$BUILD/fitdump "$DATA" 0 > golden/fit_rect.txt +$BUILD/fitdump "$DATA" 1 > golden/fit_cyl.txt + +g++ -O2 -o $BUILD/exportdump harness/exportdump.cxx \ + -I$SRC $SRC/Panorama.o $SRC/Hill.o $SRC/ProjectionLSQ.o \ + $SRC/ProjectionRectilinear.o $SRC/ProjectionCylindrical.o $SRC/ImageMetaData.o \ + -lexiv2 -ltiff -lgsl -lgslcblas -lm + +$BUILD/exportdump golden/test.jpg "$DATA" 2>/dev/null > golden/export.txt + +g++ -O2 -o $BUILD/stitchdump harness/stitchdump.cxx \ + -I$SRC $SRC/Panorama.o $SRC/Hill.o $SRC/ScanImage.o $SRC/ProjectionLSQ.o \ + $SRC/ProjectionRectilinear.o $SRC/ProjectionCylindrical.o \ + -lfltk -lgsl -lgslcblas -lm + +# stitch: cylindrical resample of one image (projtype center pitch roll scale +# k0 k1 x0 out_w out_h from to bicubic) +$BUILD/stitchdump golden/stitch.ppm 0 20 2 0 200 0 0 0 64 48 10 30 0 > golden/stitch_rect_near.txt +$BUILD/stitchdump golden/stitch.ppm 0 20 2 0 200 0 0 0 64 48 10 30 1 > golden/stitch_rect_bic.txt +$BUILD/stitchdump golden/stitch.ppm 1 20 2 0 200 0 0 0 64 48 10 30 1 > golden/stitch_cyl_bic.txt +$BUILD/stitchdump golden/stitch.ppm 0 20 2 3 200 -0.3 0.1 0 64 48 10 30 1 > golden/stitch_dist_bic.txt + +echo "golden written:" +wc -l golden/*.txt diff --git a/tests/golden/export.txt b/tests/golden/export.txt new file mode 100644 index 0000000..4928658 --- /dev/null +++ b/tests/golden/export.txt @@ -0,0 +1,17 @@ +# +# name height x y distance flags +# +AMIESBERG 1780 163 502 16561 +FELDERNKREUZ 2048 1466 450 19944 +SCHOTTELKARSPITZE 2049 1453 450 20154 +ZWIRCHKOPF 1773 1444 581 4118 +SCHOETTELKOPF 1907 1429 461 21275 +SEINSKOPF 1961 1406 458 19374 +OCHSENSTAFFEL 1871 1401 464 21971 +SIGNALKOPF 1895 1392 465 18879 +OBERE WETTERSTEINSPITZE 2297 879 415 8077 +SIMETSBERG 1840 770 475 24291 +ROTPLATTENSPITZE 2390 683 395 7420 +WETTERSTEINWAND 2482 524 373 7038 +WETTERSTEINKOPF 2433 408 384 6903 +OFELEKOPF 2478 223 336 4954 diff --git a/tests/golden/fit_cyl.txt b/tests/golden/fit_cyl.txt new file mode 100644 index 0000000..cdde13c --- /dev/null +++ b/tests/golden/fit_cyl.txt @@ -0,0 +1,14 @@ +ret 0 +a_center 6.6322511575784517 +a_nick 7.5149662500849354e-18 +a_tilt 0.017453292519943285 +scale 3000.0000000000009 +k0 0 +k1 0 +x0 0 +hill 0 -1136.4890178984479 91.333004952545252 +hill 1 -1404.1585409517218 -150.78485966743139 +hill 2 -1578.4832174937635 -134.52393246509686 +hill 3 -1747.4097596895024 100.5675701918971 +hill 4 -1817.5350955613387 -72.855082700985704 +hill 5 -2000.1065521517946 77.132392173389633 diff --git a/tests/golden/fit_rect.txt b/tests/golden/fit_rect.txt new file mode 100644 index 0000000..d37b380 --- /dev/null +++ b/tests/golden/fit_rect.txt @@ -0,0 +1,14 @@ +ret 0 +a_center 6.6322511575784384 +a_nick 3.8358482613767501e-16 +a_tilt 0.01745329251994306 +scale 3000.0000000000027 +k0 1.1252597395717405e-14 +k1 1.6387771195849632e-15 +x0 -1.3533330861078106e-14 +hill 0 -1194.2901436402242 97.812455966247811 +hill 1 -1516.1807642651722 -169.86071632098324 +hill 2 -1741.859869105966 -156.929589386619 +hill 3 -1976.4828429431623 118.42826203922655 +hill 4 -2077.8688492890328 -90.908545475464734 +hill 5 -2361.0905544534971 94.952405220629913 diff --git a/tests/golden/funcs.txt b/tests/golden/funcs.txt new file mode 100644 index 0000000..148f758 --- /dev/null +++ b/tests/golden/funcs.txt @@ -0,0 +1,200 @@ +-5.0020872795986167 -99.958363572649731 -1997.5010209985358 100.20872675351576 -99.958363572649731 -0.0025010436397993083 -0.25031293023198864 -0.012526086657295765 2000.0005033366022 99.958363572649702 2002.504111200557 5.0020872795986167 -0.049979181786324864 -5.0020860269899527 -0.2503129302319887 5.0000020849885135e-05 -5.0020860269899492 -99.958338541356667 -1997.5005207899326 100.20865147158865 -99.958338541356667 -0.0025010430134949748 99.958338541356667 2002.5026068169227 5.0020860269899492 -0.049979169270678331 +13516.721449252176 -90.435970179787844 -2094.321714207947 94.176090161439177 -91.370816903521415 6.6356020860344502 88574.515499403147 584080.25339792401 2071.384290138119 115.21423411228621 2050.7511282384571 -230.33272928265995 -0.044396647118207094 -592.62316475689147 -3907.8902807322411 -0.14422847327746321 228.03281699198908 -89.172833284302698 -2034.4667315076003 101.70618777767493 -89.172833284302698 0.11194541825821752 101.5584481408693 2037.4263215910285 -228.03281699198908 -0.043776550458666026 +27767.208948336269 -78.370118821736924 -2312.2369447710262 96.198434307327048 -81.368351460862215 13.388239608648153 354725.43413069751 4639113.3306955835 2205.8452849230935 133.34349798825696 2122.6111715925726 -475.50400455376513 -0.037786942536999484 -1001.1764046447552 -13093.42482460415 -0.23325238856520261 457.28344700366785 -73.773629498937709 -2071.458501120499 102.70564419274925 -73.773629498937709 0.22048382208470002 102.64345149894163 2072.7136185431309 -457.28344700366785 -0.035570698890519628 +42786.744822628083 -61.932343554367023 -2634.2026668600679 106.92714827045366 -66.624046266450847 20.268472203992459 789395.13857445191 15265034.215475645 2383.1289907217902 152.27561820791109 2210.6997071117912 -726.15863833436924 -0.029337917363508777 -1142.6223500995281 -22095.612725883831 -0.2468093463902078 670.06180036875242 -53.275492511765933 -2108.4764212385344 103.19971537509598 -53.275492511765933 0.31741440093261603 103.19002413577427 2108.6744418299004 -670.06180036875242 -0.025237087878619578 +58255.898888001211 -38.939949770053168 -3015.7298274621357 126.83658315586665 -43.332816846375451 27.120995757914905 1370955.9700713323 34635237.551917978 2570.4534376670372 168.40131317582612 2303.1275504441019 -967.4577948495205 -0.018128468235592721 -916.38714071113111 -23151.207625144576 -0.17681797849218084 854.14521581782822 -27.415304235208286 -2145.5205371727593 103.18179930160902 -27.415304235208286 0.39764674851854198 103.17763599693059 2145.6071107369216 -854.14521581782822 -0.012763177018253392 +73448.676763104959 -7.6238731169898015 -3378.0856362827617 154.70173216420562 -8.6914456947891701 33.614955040322634 2066121.5359411193 63534274.830888338 2728.4322527630902 177.06618184881916 2385.7482157252502 -1173.2050997310059 -0.0034891867812310308 -214.46061560918022 -6594.7716315287762 -0.035280785149196019 998.47377689819257 3.7918322863667067 -2182.5903304250364 102.64390350515058 3.7918322863667067 0.45696740361473343 102.5887398067225 2183.7639461162553 -998.47377689819257 0.0017353923507399116 +87330.743415746212 32.308481451665855 -3619.1785045295851 185.66520251611638 37.15379620431473 39.302764813567151 2831907.5016284138 101098935.64888549 2818.6991604203467 174.34612947024877 2444.9201382215633 -1311.1096581278914 0.014540270680317668 1047.6795159480805 37402.0987276542 0.13779202463105583 1093.8050340665045 40.010774483566486 -2219.6847131940563 101.5749028791941 40.010774483566486 0.49226149147907489 101.40894444091182 2223.3172863414416 -1093.8050340665045 0.01800664918252317 +98784.776633330446 78.800201325402142 -3649.5512327121278 210.792964951936 89.621357264118274 43.729427460526985 3618692.7031834144 144833661.69047368 2811.8012527034261 159.4377832902091 2470.1936043395458 -1353.9048084061174 0.034882780577867263 2886.6159671955425 115533.20348404509 0.27412551441404837 1133.2878414841853 80.56359514154228 -2256.8020353140378 99.959478646501879 80.56359514154228 0.50167677799211385 99.627172008521384 2264.3296031634854 -1133.2878414841853 0.035663388730209064 +106896.71978013412 127.50539161514627 -3436.9406133486041 219.96376379307009 140.08681915762014 46.557804782288379 4374117.5957616027 190919326.8967135 2693.7088398612641 135.77857074067586 2456.399155041936 -1291.3040874871499 0.055533707149453951 5217.4059051149061 227726.75899629967 0.29177564638545916 1112.9205641904543 124.42970301454353 -2293.9401037752423 97.777996944129058 124.42970301454353 0.48472150008295045 97.235797377460798 2306.7314148332634 -1112.9205641904543 0.054194121522013736 +111208.94690609786 173.11258385086157 -3024.6817974795981 207.17672773470983 178.74116385900462 47.66778692931755 5047210.4625721155 234757791.7619946 2469.4976518726803 109.54643057642629 2404.8592263451469 -1135.2122568654108 0.074201707608599038 7856.7027988559448 365433.97848517011 0.12226068648735365 1031.8644260261244 170.27121106397678 -2331.0962146994229 95.007471632557071 170.27121106397678 0.4422907955534181 94.230768934684491 2350.310413409959 -1031.8644260261244 0.072983802427765448 +111855.43935949082 211.31288651648168 -2507.5075653240669 174.27322649260614 198.63299981941364 47.196387915396969 5592103.368888028 271637046.94951057 2163.4203747729871 86.60138873451352 2323.6157289528869 -914.4770032823094 0.089161555492186365 10564.381234787095 513166.00072738226 -0.26318881182204934 892.59007720218904 216.48413339014562 -2368.2671973644124 91.623607705915347 216.48413339014562 0.37662028573932027 90.611709440397348 2394.7146122084346 -892.59007720218904 0.091343516198373678 +109534.8921475643 240.27410245586734 -1986.785774372137 129.95323597871536 198.59610217223391 45.506810198406441 5971053.9465199793 297398634.17386824 1815.4074117123682 70.310061328018648 2226.5579705356972 -662.87848732622797 0.099823058768536505 13098.014701861881 652369.19940963306 -0.84189088613913809 700.84419839648479 261.27387415908657 -2405.4494695962917 87.603751476764415 261.27387415908657 0.29116917257851466 86.381995918816273 2439.4712727230062 -700.84419839648479 0.10854751730747261 +105328.29105262848 260.92024568533162 -1538.838424045844 85.066592840869092 183.65379909924218 43.096682100093489 6156843.2530330233 309020443.18338138 1474.685244319008 61.398905866519684 2131.3902303267814 -407.59644179481364 0.10675951132787707 15251.790740863476 765508.38479768345 -1.5454189983856206 465.4326760302805 302.75117281933569 -2442.6391035811575 82.930401222564072 302.75117281933569 0.19043890181271705 81.548817626246986 2484.0218018895171 -465.4326760302805 0.12387527529432721 +100410.56337582441 276.12628246617305 -1208.6794556529276 47.600317869478047 163.12971030835899 40.471811114802257 6134665.014151738 305051888.7217381 1191.6126533132633 59.069742146243442 2056.6585869565693 -162.97207902807421 0.11129636536322976 16870.159747961919 838884.28826701269 -2.2760833245662622 197.8270684546639 339.04239653565259 -2479.8319008974745 77.594786244176447 339.04239653565259 0.079736827269110799 76.123211251351535 2527.7707430952992 -197.8270684546639 0.13665554072376163 +95734.822294933256 289.36893075003775 -1019.9131088692203 19.909257080194696 147.58325249402307 38.020183596081516 5903459.6738222893 285846599.74117625 1009.1046087400678 62.127941863409049 2018.3825537580958 69.749923140585679 0.11492014723988792 17843.850049433193 864002.49086810986 -2.9261034496663907 -88.388815213315112 368.40609632565497 -2517.0234753402683 71.599939376664565 368.40609632565497 -0.035102786025939282 70.120072632571691 2570.1446315999788 88.388815213315112 0.1463090136321108 +91777.764352078346 303.34253291296068 -986.88827951873145 -1.1908071251816681 145.64543878745653 35.920847104531639 5476515.9218860399 253549212.374001 955.14320684023187 69.465857502724404 2026.990554298427 298.21824788685711 0.11872506180546406 18100.900833780765 838027.17186010792 -3.3951331059570724 -378.20594514854037 389.34638056468975 -2554.2093419055277 64.962702444037348 389.34638056468975 -0.14802581023426237 63.558144423279046 2610.6542751304059 378.20594514854037 0.15238605892942847 +88420.90665787262 318.86772038495747 -1120.0766846241718 -21.985600321771891 161.46370961379839 34.113004111833575 4881146.040891408 211819830.3689281 1037.7797065064137 79.846855782865859 2085.2121506296075 532.09322942543861 0.12301995385222124 17602.623290750034 763874.8458372067 -3.605499149967776 -655.97616714621461 400.71320646707989 -2591.3850101322209 57.714235423148033 400.71320646707989 -0.25307722497924945 56.459979295321027 2648.9525220067208 655.97616714621461 0.15459614447032402 +85005.004779554467 334.31491020417934 -1420.7647008094982 -48.011098872984689 193.2255784198162 32.333588733189224 4157295.4959459621 165318984.33801159 1243.6638194532334 91.251617575550227 2187.4110460424563 775.12815633635023 0.1271642868787293 16350.165193493422 650180.55757232022 -3.5159336716229399 -906.25625538918086 401.78035574474148 -2628.5460798647664 49.89883936650002 401.78035574474148 -0.34471519794187178 48.851878444623054 2684.8793287508024 906.25625538918086 0.15282630496186439 +80543.164819928847 345.75479162535362 -1865.964968093753 -80.164806488697039 233.19248523435783 30.211239617377665 3355032.9943088028 119019225.95347808 1540.5276659919978 100.30748499451735 2320.6042203049496 1018.3510420380387 0.12969046947687685 14402.447885390297 510924.39390677586 -3.1334118937172679 -1114.6643737900249 392.29368950596307 -2665.6883374043732 41.571207900290872 392.29368950596307 -0.41810366608778132 40.763805346254522 2718.4872248880952 1114.6643737900249 0.14714692029480986 +54774.217574596754 257.49377349037019 -1770.2454571947612 -83.496260484232778 199.49904379488538 27.387108787298377 1872740.2568401687 57306478.397251055 1393.4786906399324 75.898507984537375 1825.4473280738157 915.64068958305029 0.12874688674518509 8803.7579878593115 269397.93978538067 -1.8640823594196654 -938.73578906254966 275.60866024220729 -1999.8578246143652 24.263798650344896 275.60866024220729 -0.46936789453127481 23.847040250279846 2034.8079709887807 938.73578906254966 0.13780433012110366 +48123.134387004189 250.12072570895006 -2154.4298989760705 -101.19887903877954 215.95995786342363 23.624513690232789 1296694.430322838 32716277.756276436 1652.3685702195667 70.064002341145269 1939.0079936551665 1039.1202961373237 0.12278877059840454 6739.5890992231016 170043.3531425437 -1.3253414913261368 -1009.9434073571433 255.0346825311295 -2036.9264422601732 17.564324176574839 255.0346825311295 -0.49579941451013415 17.310945130573277 2066.7407865852392 1009.9434073571433 0.12520112053565513 +39144.867598011784 230.73531911834888 -2443.9182365940419 -105.77728096281756 214.95933267200758 18.874092380912145 785356.90178847103 15282696.666336469 1877.8375225614002 55.651239239010522 2037.4833881677116 1094.7517850030306 0.11125135926632057 4629.2039410337538 90082.253656036744 -0.78866233641615024 -1028.7027731939904 227.86234110639955 -2073.9737495975405 10.555138994008658 227.86234110639955 -0.4959994084831198 10.434844527754331 2097.8828327247247 1028.7027731939904 0.10986612396644145 +28029.411969285215 200.1891667298209 -2585.8326096705459 -96.132794738625648 195.42180922580309 13.277788711172532 378172.33221566555 5061695.8951167613 2046.1616622696645 35.052754007775761 2113.1933344423355 1068.0301698889602 0.094831438526679718 2700.9487087879743 36151.193060830374 -0.34311615386843514 -992.07052534817126 195.09249930885326 -2110.997498164802 3.2773625323959545 195.09249930885326 -0.46995287794797314 3.250037222234929 2128.7461137781447 992.07052534817126 0.092417100572644836 +15285.329697875801 161.5081888356157 -2580.6389936836877 -77.080318123627251 161.17649395223046 7.1160752783406895 108410.16346497693 770193.73465574556 2147.4797992973772 12.686563717829159 2163.8989093996547 960.89623312800927 0.075190032046376018 1145.4858677359573 8138.0380793536342 -0.04392939889664068 -900.07562294222282 157.92011404154971 -2147.9958754633485 -4.2326566931929515 157.92011404154971 -0.41902961961928437 -4.2093933581590219 2159.8668372506445 900.07562294222282 0.073519606164594839 +1571.1854761697573 118.59411974415893 -2475.229040573498 -56.203967606724859 118.70005749950093 0.71907802113032371 1131.5946007087739 815.5064561035947 2187.6746591065626 -7.2210064167260697 2193.0129907530581 789.12053458748335 0.054276485008768388 85.413509489315373 61.554967108692267 0.098131512615200936 -755.81759653190193 117.65276824653115 -2184.9675579864338 -11.943723900683633 117.65276824653115 -0.34591194349286131 -11.906743752986035 2191.7536638005058 755.81759653190193 0.053845660524728216 +-12651.711321168666 75.690546150458431 -2389.9195909142018 -41.079576534061978 75.889822314151871 -5.6938394784737474 -71436.019414410082 -405049.15325528709 2237.5014053659365 -22.329699939710647 2233.9994409602841 580.68095767100704 0.034064152182924579 427.37548992636914 2423.2604467014853 -0.036836437157529246 -565.39451098978486 75.626981678741899 -2221.9117548085806 -19.828971444290584 75.626981678741899 -0.25445297524292748 -19.802874626009491 2224.8398563290393 565.39451098978486 0.034035545309964853 +-27218.812708129048 33.059981632027252 -2335.533163343106 -33.807952250453106 33.172131908181605 -12.049053876993822 -323042.96410069067 -3863069.0813206779 2283.9511564260865 -32.600820221140381 2276.4999264254625 342.78680153495827 0.014634786025687144 392.36812325524011 4692.0853690863541 -0.0094980096847612581 -337.6596889224374 33.129293861677176 -2258.828240533885 -27.864004235443886 33.129293861677176 -0.14947308053228747 -27.85641338350289 2259.4437695504162 337.6596889224374 0.01466546873026878 +-41912.260532359367 -8.8053248031529101 -2343.0335741851613 -36.446434308703608 -8.8725936657602116 -18.254468872978819 -747464.03960285056 -13486508.126187572 2340.6892960193786 -38.214749959206785 2322.6496422406062 84.818560832738598 -0.0038350717783767027 -157.03432751612903 -2833.3734091902902 0.0037358089593695237 -83.81600892090556 -8.6718836740158878 -2295.7173766156243 -36.024343777050518 -8.6718836740158878 -0.036505230366248066 -36.023974032230136 2295.7409395242667 83.81600892090556 -0.0037769528196933311 +-56625.954106544195 -49.899219413378177 -2441.3855115162701 -50.295537972401938 -50.958102045168282 -24.27173343615268 -1324028.9468637998 -31541978.763935465 2427.2473936709089 -40.083961798430593 2377.6660983425936 -186.93939152185413 -0.021388435239339126 -1166.7443307870972 -27795.03045745298 0.044302187389976024 183.13391920548389 -48.779094986344667 -2332.580120299785 -44.282588643962448 -48.779094986344667 0.078497179256529742 -44.260392929122546 2333.7498632633033 -183.13391920548389 -0.02090831332462266 +-71331.808475325714 -90.508261175079767 -2652.7081590040157 -76.831375717898226 -94.849809770533014 -30.097809483259798 -2020905.3834406107 -59012630.386017576 2559.1933724100554 -39.271203559232688 2445.9145221276663 -468.51490407865339 -0.038189139736320578 -2564.1945180436182 -74877.262721492196 0.14770761705529684 449.05473763333339 -86.400482981510422 -2369.4180207064474 -52.60574409178296 -86.400482981510422 0.18947457284106894 -52.518978965132625 2373.3324695153374 -449.05473763333339 -0.03645589999219849 +-86004.614391498297 -130.81615700789706 -2990.1591666891886 -117.70288521020588 -142.41025913256681 -35.73104046177744 -2797097.6835988224 -95350649.66699487 2744.595661711699 -36.99198540093078 2529.8869453224111 -754.90312142893788 -0.054348216455295827 -4254.4876496796333 -145031.81772158036 0.33715244548891438 699.42818749912578 -120.96511766730254 -2406.233201839721 -60.953152400398196 -120.96511766730254 0.29058088387998576 -60.751776633819418 2414.2092164097712 -699.42818749912578 -0.050255553663191746 +-100531.5157252807 -170.53858960331937 -3449.2800208000563 -173.48437277269915 -194.51627442714974 -41.134008070900443 -3605259.3064122223 -138469610.80238709 2981.7536508985868 -34.825922435601903 2629.4234316525162 -1036.0463160970191 -0.069778473651112671 -6115.8516593933018 -234895.61416431845 0.61792715890855388 920.14596624912895 -152.11950098585802 -2443.0283326030781 -69.275379214695562 -152.11950098585802 0.37649180288425899 -68.909840361332385 2455.9876105632484 -920.14596624912895 -0.062242021679974641 +-114636.36554565781 -208.70931434657749 -3994.6022919548859 -241.41098727134258 -250.16671682670511 -46.205709611309075 -4395004.8127875039 -184980526.07792667 3258.6699081958086 -34.873421471062919 2741.360927976215 -1293.6312376780636 -0.08412306100224809 -8001.6357519765479 -336779.33334179781 0.97400911233840282 1098.3023023407784 -179.70714192156635 -2479.8065841862317 -77.514282040696358 -179.70714192156635 0.44268532944005579 -76.943518418466653 2498.2016799332368 -1098.3023023407784 -0.072433350230377405 +-127844.80617588977 -243.76745995257508 -4551.0591269552442 -313.42090126457094 -306.21996250320859 -50.772361467787832 -5116292.6498334138 -230624583.31038514 3554.3684373818078 -39.485991535862041 2859.7358107730201 -1500.992978125161 -0.096809952324295109 -9755.4660289291241 -439742.2981646001 1.3694039457580796 1222.9418711869287 -203.73422285446833 -2516.5715754756334 -85.604314350710894 -203.73422285446833 0.48567985352936011 -84.802744696675163 2540.3586287641247 -1222.9418711869287 -0.080911129012894489 +-139503.47318276973 -274.00175868793673 -5015.2610333639877 -376.69452422762743 -358.07661579040553 -54.600185198735709 -5722487.0730161443 -270820912.48907447 3841.7871528379665 -50.365048337812105 2976.5632444774096 -1628.0686232905957 -0.10724139283285195 -11239.659388416352 -531925.15296202258 1.7560264015007541 1285.720062764299 -224.3252234172424 -2553.3273074169274 -93.474922931561665 -224.3252234172424 0.50321724570031268 -92.43733660714966 2581.9877772353393 -1285.720062764299 -0.087798521885417771 +-148852.99017070685 -298.16141013208153 -5292.3131842504463 -417.81353585862149 -401.10280655196902 -57.427851146106043 -6172985.5798118589 -301249150.35571134 4091.741114667228 -67.443284683176174 3083.0609607550746 -1650.6167576796211 -0.11503140822996974 -12364.857992378538 -603420.00095626176 2.0862752609317661 1281.4364472040579 -241.67295186237206 -2590.0780875179985 -101.05370973025317 -241.67295186237206 0.4943813453719359 -99.797297353714455 2622.6862467733076 -1281.4364472040579 -0.093238021551841077 +-155143.68914697925 -315.84460086189893 -5338.7390444411667 -428.43628738699016 -431.99909791694614 -59.012434061232128 -6435642.3116548872 -318414623.13668442 4277.4023016964948 -88.509745779168952 3171.0442610733749 -1559.1014562415614 -0.12013868423807489 -13101.808319698343 -648234.80804250424 2.3243089881075454 1208.4084130874874 -255.98777527553398 -2626.8284459196784 -108.26989159872141 -255.98777527553398 0.45964564970996097 -106.83312083435723 2662.1559762269635 -1208.4084130874874 -0.097370777967110675 +-157772.55065519176 -327.40715259022193 -5179.0820600396937 -408.4231823834084 -449.34374537011405 -59.17950137103967 -6489225.1055106027 -320154532.48685879 4378.7138856598594 -110.09741583441708 3234.2057248504379 -1361.8263926007694 -0.12280838431741257 -13466.339394838802 -664379.72533929395 2.450396168293496 1068.6600838295647 -267.45092974861535 -2663.5830446723003 -115.05753657482508 -267.45092974861535 0.40084774337192969 -113.49609744056373 2700.2276773697758 -1068.6600838295647 -0.10031917845034334 +-115729.10171651257 -246.78329264278511 -3614.8761191063422 -269.76836215489169 -335.38813268708526 -57.86455085825628 -4680519.491845252 -226426150.51451358 3245.3010735825956 -95.440204605127505 2418.86466986819 -799.77393088254257 -0.12339164632139255 -9980.8431444128037 -482836.12449771259 1.8190123119504944 642.1837497260409 -204.34819182594165 -1998.0366866704851 -89.795117461150141 -204.34819182594165 0.32109187486302043 -88.596832442420066 2025.0604228753475 -642.1837497260409 -0.10217409591297083 +-112304.94049120303 -248.89821148266691 -3376.1269962554234 -228.23637930877098 -330.77194461370135 -55.132518650566041 -4424845.0802564314 -206230133.97067246 3198.3464202489913 -106.4818401611588 2435.254267761803 -553.4006774466842 -0.12218861633906083 -9806.6569622551233 -457061.91797636834 1.7480647027048928 457.4793861312267 -209.78626080399161 -2034.8616657611653 -94.506342076444582 -209.78626080399161 0.22458487291665524 -93.31131343891343 2060.921935136656 -457.4793861312267 -0.10298785508296103 +-106121.64510680575 -247.62935754668334 -3147.2423989495483 -183.42318003281443 -318.21434221390456 -51.167620591516751 -4025357.4147032052 -177338441.24059913 3093.6270643339881 -113.70699041228956 2432.8674073419911 -283.2621491472969 -0.11939699013822726 -9392.9628540483936 -413810.05947036081 1.5979311266333971 241.44285198036036 -213.15221007391983 -2071.6988483862042 -98.814561465025292 -213.15221007391983 0.11641410413710722 -97.672317446009558 2095.9266509063914 -241.44285198036036 -0.10277348605299895 +-97603.276947706501 -243.07084493681123 -2961.0544359537425 -141.28732745106507 -299.38601368686108 -46.235564636526057 -3507341.5985413915 -142963000.97673935 2949.2680912618316 -117.29321995054326 2416.3609641093549 -5.5658243483738543 -0.11514488154278125 -8734.6707252078086 -356034.53622501309 1.3815194571748537 4.811421323760297 -214.28814182482623 -2108.5516254855552 -102.68892201877564 -214.28814182482623 0.0022792142699006616 -101.64173681231357 2130.2754186685393 -4.811421323760297 -0.10151025193028244 +-87271.583189094832 -235.30151186895819 -2835.2527360454487 -105.97329712778193 -276.34307490928489 -40.629228672762949 -2905537.1201156238 -106861998.77034977 2788.0458105473786 -117.66583586671474 2392.0080324102823 267.66475523246305 -0.10954446548834179 -7833.9048310059197 -288121.16101434571 1.1193430324259641 -240.16399326967178 -212.98449918169314 -2145.4231625255275 -106.10368614023756 -212.98449918169314 -0.11180819053522895 -105.18295346188334 2164.2034035210527 240.16399326967178 -0.099154794777324556 +-75630.722490994638 -224.45180863231582 -2776.8365032295665 -79.834428954324721 -251.26030430854217 -34.613602970706928 -2261848.8409303064 -72773054.205737576 2633.4152709755322 -115.229624926521 2366.6764308530805 527.58610079102937 -0.10272393987749007 -6712.5639750455048 -215971.01149100158 0.83908888470779164 -480.35281245191402 -209.00969589532841 -2182.3163232039078 -109.03679384641663 -209.00969589532841 -0.21984110409698582 -108.26109863555014 2197.9527091434566 480.35281245191402 -0.095656611393742971 +-63071.923666331604 -210.72246150949599 -2784.5991200901412 -63.694684885163042 -226.00206533697195 -28.385204170266249 -1622549.0874879055 -43845593.324902646 2505.3767166588805 -110.31438316706861 2346.6813113420853 766.44318152346398 -0.094834591138387034 -5420.9150087799135 -146487.54651350222 0.57239600523712486 -702.4481410406961 -202.1487463493888 -2219.2335977806611 -111.46834055874849 -202.1487463493888 -0.31613327679599285 -110.84330598418374 2231.7476392496742 702.4481410406961 -0.090976033460571024 +-49824.892807761564 -194.39267300317866 -2848.0802534866984 -57.175579073187102 -201.71777062084661 -22.056172114989625 -1035233.5023928528 -22161595.033865135 2416.9405027858847 -103.32484526110537 2336.7150321936147 975.13120675641869 -0.086052533423275179 -4038.9812475670874 -86463.842747613875 0.34901026797692297 -893.70189499157448 -192.24661093927608 -2256.1770377368366 -113.37925087051653 -192.24661093927608 -0.39561836874350353 -112.89895654536677 2265.7752577817032 893.70189499157448 -0.08510252808290221 +-35968.546905028961 -175.84928284931851 -2944.1822448659659 -58.914435988680815 -178.68391184492091 -15.665743425535261 -545755.97362869279 -8414230.4096599817 2371.8258248578209 -95.0120040431067 2339.0357883670599 1141.5108968103214 -0.076589408906497597 -2668.1866472598313 -41136.951881165907 0.18988561440776192 -1042.6512791046566 -179.25105436989597 -2293.1481983600479 -114.75035883984013 -179.25105436989597 -0.45411641075986792 -114.39991415235428 2300.1728679997714 1042.6512791046566 -0.078071016711627167 +-21496.215549166001 -155.61388173617851 -3035.9204109631828 -66.619227901905788 -156.49203400175725 -9.2139800896553794 -195260.80633173219 -1786368.0305773818 2363.8067767195021 -86.659243385936577 2353.0515064336032 1250.8528423780435 -0.066701192342982649 -1413.518205319443 -12931.748977477286 0.10192058993804047 -1139.7955697532129 -163.24948348175013 -2330.1480907135269 -115.56200943999471 -163.24948348175013 -0.4885536089812314 -115.32074984193387 2335.0229340748324 1139.7955697532129 -0.069974060643699154 +-6414.0875366397804 -134.30129055468018 -3079.2412749434611 -77.203836967834974 -134.46728902572696 -2.7063660492150969 -17301.36124939713 -46751.314757134125 2377.868391934097 -79.90778834574968 2375.3967393048224 1289.1589093198309 -0.0566672112045064 -362.26433313756371 -978.9018112310489 0.076901191710395805 -1178.1838223194502 -144.49455957232806 -2367.1771442697773 -115.79419350159178 -144.49455957232806 -0.49712397566221528 -115.63895384679927 2370.3549641175423 1178.1838223194502 -0.060968168595919012 +9194.8283387350457 -112.91183295940621 -3061.8592051755672 -88.118619793681987 -112.98292006002312 3.8200367007623783 35041.535501642458 133706.65342012836 2411.2766702793419 -76.301024053686405 2409.6452133956655 1252.9532038248697 -0.046909776883841389 -430.307544354534 -1641.9081205611387 -0.016397333596859385 -1153.8780363606941 -123.41434929304825 -2404.2351812824049 -115.42713777898777 -123.41434929304825 -0.47938431091013467 -115.33513377961937 2406.1530639331754 1153.8780363606941 -0.051273099000020042 +25041.60348447362 -90.954706859778753 -2928.5508499152929 -95.184201897694663 -89.940330857204074 10.246155271879552 258776.64827717104 2662799.4392022979 2407.6488019832677 -76.174485348908291 2432.3536274462758 1134.339394740972 -0.037215510171758899 -939.91401951554826 -9671.6706887078635 0.094307926621917559 -1066.2623121466409 -100.60428463271356 -2441.3214037347266 -114.44220631059626 -100.60428463271356 -0.43627754179486128 -114.39319764175592 2442.36732180219 1066.2623121466409 -0.041163782582943356 +40305.564349555054 -68.529801426973179 -2669.6539720287037 -95.915150152551107 -65.52790130217663 16.245693006672735 684278.17487977201 11364124.009645762 2323.0728759912754 -78.278029218701008 2425.1661319272998 941.06213657578235 -0.027621846604987173 -1163.4484767074121 -19321.926744864439 0.17663229406621303 -918.17634147111585 -76.800077099352833 -2478.4343934458338 -112.82293719832805 -76.800077099352833 -0.37008316867034091 -112.80052032141631 2478.9269333611942 918.17634147111585 -0.030955291051734314 +54087.561298045483 -46.400509798126066 -2329.4797067453892 -90.004421242074727 -41.707724678481441 21.480365884847291 1292423.7014457677 29280574.110803679 2149.008043172932 -81.239704268647216 2385.3826974645799 697.18305582399444 -0.018427525733965871 -1108.7414034404203 -25119.150010044887 0.20450026893616982 -715.86080775204141 -52.832765885152256 -2515.5721256390052 -110.5560423281359 -52.832765885152256 -0.28429738195077103 -110.54809228591822 2515.7530324659524 715.86080775204141 -0.020982035697042198 +65705.893805513639 -25.450036623264285 -1959.0795226077521 -78.615728327008938 -20.777266976636266 25.716592487480881 2050218.9511369111 58077100.94949688 1896.4194019558081 -83.684321043243031 2317.5426417063777 430.08713141922914 -0.009960875390710092 -794.1166974547175 -22495.156226208768 0.16388387907079269 -468.71244942744045 -29.569056148659559 -2552.7319959893748 -107.6322320538642 -29.569056148659559 -0.18344909958021152 -107.63064922271006 2552.7695368177597 468.71244942744045 -0.011573016105150512 +74918.690821725424 -6.5695645426702933 -1601.449155546441 -63.968054484427057 -4.6846315385562995 28.903815903443448 2912982.1806380036 97653821.705673084 1594.9861020596518 -84.708259666135518 2232.9970898233078 163.03584689868288 -0.0025345542217092183 -255.43725120461315 -8563.1913411358983 0.05610485331592921 -188.85712334984839 -7.8418184070829584 -2589.910860873551 -104.04678244875029 -7.8418184070829584 -0.07286154450225632 -104.04678144576214 2589.910885839724 188.85712334984839 -0.0030253929039671906 +82019.853877232774 9.5532557710943777 -1289.4430773293552 -48.863489953335893 5.7309213640881431 31.198118629605467 3827347.3826290471 146033176.29381499 1288.6109887734324 -83.946998264668892 2148.1197793307056 -89.506346553151204 0.0036337983153649218 445.78997331319692 17009.202263250838 -0.10028805881028932 109.44039105425327 11.623095559115457 -2627.1050902497032 -99.799825586031005 11.623095559115457 0.041628144181914517 -99.799021959640228 2627.1262448756743 -109.44039105425327 0.0044211089992831709 +87769.499045290882 22.615151877355629 -1051.9613638287246 -36.013652475653011 11.205269784611442 32.921792590131609 4734782.7166266851 199535427.18084303 1028.0375305330981 -81.334503892602172 2081.4955380453553 -323.19757696364189 0.0084828026546720295 1219.9890782962837 51413.350192180587 -0.27174613047620838 410.50147036549748 28.276834372841797 -2664.3106323088782 -94.896398861885359 28.276834372841797 0.15397654552344242 -94.893912164405435 2664.3804506395172 -410.50147036549748 0.010606464505942159 +68938.822614063611 24.204915005454922 -681.92689627100924 -20.173922885457348 10.251007413087954 34.469411307031805 4125333.5437748227 187358593.6066848 637.73442424742598 -56.773193632978746 1517.2761452967475 -400.98198848273626 0.012102457502727461 1448.434191503751 65782.945832704572 -0.30904002720379686 516.50531123900328 30.934026580315333 -1998.9072058922795 -66.10900904432863 30.934026580315333 0.25825265561950161 -66.105841133141368 1999.0029971323768 -516.50531123900328 0.015467013290157667 +73713.29270455298 29.873527056957929 -696.7790074009572 -17.177426955238506 12.087319589255113 36.18718345829798 4677514.3535836618 224144040.61125106 615.40269326366899 -51.732638005032058 1537.8256729267257 -559.79023075924545 0.014665452654373063 1895.6398021880764 90838.067547652783 -0.37400892492074567 710.81289715461912 38.794060364629686 -2036.0616413657704 -61.826675330973686 38.794060364629686 0.34895085770968048 -61.822265883129262 2036.2068626301061 -710.81289715461912 0.019044703173603184 +79352.588354306718 33.923815710909111 -847.49475032370151 -16.981346604232748 15.137373251817269 38.260650122616546 5103268.5099463603 253144370.00518355 705.73985321855855 -44.013496875567917 1599.415046391739 -717.00209514006804 0.016356709600245475 2181.6848579874986 108221.08181231063 -0.3821788565927432 873.73812425818051 44.565497365976832 -2073.2157592496865 -57.035537787971414 44.565497365976832 0.42128164139738694 -57.029953522215528 2073.4187646363985 -873.73812425818051 0.021487703648011974 +85799.996548822295 36.692285445755537 -1143.5622297149812 -18.391586986788827 19.751336107476824 40.644242799063143 5370150.2744383775 270854191.96947515 907.69407888380385 -32.944570950506147 1700.9609009128478 -867.26799342944412 0.017381471078046204 2296.5395650589921 115830.5329332774 -0.3393132489791576 995.17752856052562 48.641483393514797 -2110.3657932263118 -51.748666670880631 48.641483393514797 0.47142469377571083 -51.741847476483294 2110.6439238941434 -995.17752856052562 0.023041915392475035 +92483.186923639412 38.807559664677896 -1555.6379549040771 -19.786509966319343 25.577636426009377 43.055487394617977 5455745.5008139582 274956528.15914619 1202.67404289256 -18.765624413872079 1835.0252176808351 -994.6227644482392 0.018066834108323042 2289.3260502900916 115376.55899054362 -0.26536483970943897 1066.9780742559717 51.624377752892336 -2147.5079406171062 -45.982645731043768 51.624377752892336 0.49673094704654175 -45.974394900585111 2147.893344796214 -1066.9780742559717 0.024033695415685445 +98409.876566492792 41.405120952125067 -2011.3795844579004 -19.262219620020147 32.516465637390318 45.038845110523013 5350203.5289204419 264746211.30936471 1557.0944140724973 -3.2965326282253762 1988.8734891901065 -1075.373733118392 0.018949712106235729 2251.0527597680557 111389.72309831619 -0.18161365929203546 1083.4603176657747 54.286114707416473 -2184.6384449397419 -39.757462626169435 54.286114707416473 0.49586284561362687 -39.747514277821779 2185.185234963189 -1083.4603176657747 0.024844903756254678 +102395.72545248146 45.994471292259533 -2420.0905093109322 -15.247611899891114 41.366473293173286 46.082684722088864 5057804.2538219169 241307611.3188138 1927.9438320624433 10.405492883132142 2146.614950649885 -1086.4134006691297 0.020699582039720762 2271.8822638962292 108391.39966401726 -0.098043014319430313 1041.8188107109074 57.509996943293544 -2221.7536796928021 -33.096355433005435 57.509996943293544 0.46886535135504387 -33.084539765490547 2222.5471470635225 -1041.8188107109074 0.02588208683316541 +103358.74986723096 53.906524911773936 -2712.9170857342187 -7.6289448812756833 53.421871378453531 45.754205341846372 4597074.4949594308 207378737.54649466 2270.404948048697 19.470110910713 2292.0500301300258 -1014.5373439424025 0.023863003502334633 2397.5939250633096 108157.91692606932 -0.010850074384730974 942.3731771362103 62.217781145666663 -2258.8502315235655 -26.02558951702423 62.217781145666663 0.41716386770084563 -26.012142278821884 2260.0179668373712 -942.3731771362103 0.027542178462003835 +100588.52540632928 65.696281483995904 -2868.1235414830567 1.6787260564365636 69.367900357774232 43.810333365125999 3999428.9296148154 166921039.27672553 2545.9605429745643 22.638541599132807 2411.6244097788617 -861.23836254765797 0.02861336301567766 2612.1031964017493 109019.30948512425 0.088731748023088544 788.65105870083721 69.287464098498731 -2295.9249809075882 -18.574161573356101 69.287464098498731 0.34348913706482459 -18.560227490245854 2297.6486456372186 -788.65105870083721 0.030177466941854848 +93907.915908457988 80.965389950546069 -2906.9736714522546 9.5482593442042187 88.539540586642872 40.251999960762099 3306760.6979082641 124493553.12642671 2729.3007274332545 20.631910807640971 2496.8614473673856 -641.01642510906424 0.034704410608892441 2851.0181148122851 107335.67003058492 0.20256513507302609 587.29319437086099 79.468297900133635 -2332.9751784838381 -10.773460602984638 79.468297900133635 0.25173304516539263 -10.761811106989981 2335.5005884475695 -587.29319437086099 0.034062708058351321 +83683.228376475628 98.711097586873464 -2872.5074219376856 13.238548108888233 109.1758029425238 35.309379061804066 2568467.7638374269 84554583.98780632 2812.7019640353483 15.506915699178661 2545.8179250140684 -375.52634050021612 0.04165025214636011 3029.714280910403 99738.931603935751 0.31931373875353386 347.78077115380148 93.300133999823657 -2369.9985152464292 -2.6569490806486256 93.300133999823657 0.1467429414151061 -2.6528719759719941 2373.6408817524803 -347.78077115380148 0.039367145147604921 +70686.822746744278 117.93608652672005 -2810.2174048718675 11.514883727871823 129.41273577680303 29.367188511318769 1838184.1759203456 50797903.861562289 2807.1146571990153 9.7580000962338822 2563.3348439986244 -87.146460468098311 0.048997127763489842 3066.8834670939341 84752.797655899005 0.41580988932684421 81.995318166039738 111.04415215374554 -2406.9931848929227 5.7400497524423919 111.04415215374554 0.034065358606580699 5.7278162526214542 2412.1340534888573 -81.995318166039738 0.046133839698274011 +55859.174574750323 138.17544800833323 -2759.0443868793882 4.6948445110670551 148.34073531390013 22.855636078048413 1170306.130652582 25609392.932813536 2739.7844914193715 5.7419993682183437 2560.0498913616252 205.68217716119713 0.056536599021412941 2894.9152066939987 63348.400126780252 0.46283391449711614 -196.36877514267883 132.63136867348081 -2443.9579367682513 14.380370565240026 132.63136867348081 -0.080347289338248293 14.338873995979775 2451.0307285244712 196.36877514267883 0.054268154121718828 +40045.85110244355 159.73376270229684 -2750.7131252658191 -6.0907613937490508 166.61505346545698 16.141012133189662 616376.77683941741 9715334.2432751171 2648.8047742245194 5.5201050895066581 2550.3220322306342 490.47432334738596 0.064382814470897554 2458.5863228379826 38752.251528351539 0.43129589964027648 -472.51012518283852 157.63409459050521 -2480.892118020035 23.224767087206242 157.63409459050521 -0.19045148133125292 23.136524070503338 2490.3542742169398 472.51012518283852 0.063536515352884002 +23791.992466011983 183.57896960965081 -2811.6813773106774 -19.76879018154602 186.45373373871359 9.4487658721254899 221452.06150574764 2076816.8191769174 2575.5271303284085 10.916602067819747 2549.4044570167039 759.95409116153292 0.072906659892633369 1708.7236946310525 16024.714713454085 0.29705848111233563 -731.30813685911494 185.26383304032501 -2517.7957037920478 32.23140268700142 185.26383304032501 -0.29043214331180101 32.074818258379864 2530.0872029515504 731.30813685911494 0.073575787545800242 +7259.5238887401911 210.91571149909086 -2962.0772216540113 -35.746861457498746 211.06286364656111 2.8413009349276677 20641.494237703289 58682.398562459261 2556.1750054572713 23.492077425700206 2570.3517382991463 1008.816803638259 0.082550180625867275 599.71087777563525 1704.9382349263431 0.045464571296937289 -958.1506549270938 214.39710307560304 -2554.6693145235577 41.355126935440111 214.39710307560304 -0.37501004106735569 41.105880743955012 2570.1595943965294 958.1506549270938 0.083912760499257555 +-9663.1923255777183 241.4123540960654 -3179.5649459975557 -52.769746208599145 241.52342034338264 -3.7280834589420206 -36097.728067876487 -134731.78022649506 2591.1624639270194 43.581643218730605 2609.6057305055588 1223.5799758207097 0.09313748229014869 901.8176619872545 3365.9597305062757 -0.034082047145649615 -1139.7561160276837 243.6285069241053 -2591.5142196882789 50.546670777978761 243.6285069241053 -0.43972072377611254 50.180166933275494 2610.442014934195 1139.7561160276837 0.093992479523188774 +-26936.768629395639 273.20156539498771 -3415.9725444517226 -68.819859785449054 276.45898984385587 -10.24601317207898 -274829.33868409024 -2810027.1385879791 2667.7478268359455 70.403367496487036 2659.0688321760972 1382.8389343995746 0.10391843491631333 2787.4095285143085 28500.21929604622 -0.33584402658157181 -1264.9440123768736 271.34817501076776 -2628.3323275920493 59.751958799539445 271.34817501076776 -0.48115025195012312 59.246736066729092 2650.745262538951 1264.9440123768736 0.10321345569066862 +-44537.064861402774 304.63622928808775 -3647.4397351345774 -80.263992862651108 315.69306782621271 -16.705575716955281 -727766.80322484707 -12024398.740302075 2796.8143112496418 100.46978427526803 2724.9308723687 1471.679065201535 0.11426715277122571 4977.9691460448921 82247.617868428191 -0.69209229109384962 -1325.3092579929962 295.83867920402503 -2665.1261611470268 68.911774669043822 295.83867920402503 -0.49711525056001354 68.253535950255724 2690.828701027167 1325.3092579929962 0.11096724651313768 +-46159.813575564534 246.02350942771818 -2833.8585115916953 -60.668702124286568 263.03700660109024 -23.079906787782267 -1013954.2564786517 -22830510.909397878 2195.2986931710093 95.242985798782342 2075.050510239435 1092.4127835127745 0.12301175471385908 5404.1939352654699 121682.51950937776 -0.77393573991075482 -973.55559293045235 233.35904202435819 -1999.1852163119727 57.685541404030914 233.35904202435819 -0.48677779646522618 57.08301743645157 2020.2870617040678 973.55559293045235 0.1166795210121791 +-59636.518248913002 262.4635507300502 -2920.1342961002824 -51.360255723301108 289.71064669570859 -29.276641261125675 -1609201.1357261601 -45229530.9649591 2345.958259076333 111.63777684663792 2149.2262420549873 1033.8894906482954 0.1288480857781297 7082.1814606726421 199057.61844395223 -0.98652647496659096 -918.05373587269708 244.1335296020487 -2035.9992891529741 64.555548159315379 244.1335296020487 -0.45068911923058275 63.842733091437616 2058.7315705140818 918.05373587269708 0.11984954816006319 +-72839.597006329612 271.04802447769572 -2934.9190562875924 -29.442599310693343 307.10584457408805 -35.120345711827198 -2286611.5879000272 -75925162.03767772 2489.2214934318808 120.84353183984344 2222.0990555127646 909.68198746760015 0.13068853639233158 8508.8547866931694 282529.91537931189 -1.0996840961668113 -810.43598980565412 249.05648943047723 -2072.8017353463042 71.293944427628333 249.05648943047723 -0.39075987936627488 70.490892641176146 2096.415667219806 810.43598980565412 0.12008509615741429 +-85208.955418882819 270.05342027434244 -2887.8482815284415 3.3945632668413781 310.68120775414764 -40.364261212166184 -3004877.8320096848 -113369753.8204072 2601.1874572493875 122.59713723167785 2286.0773773281999 728.1431643302559 0.12792677417069753 9523.3832177801596 359303.66267671087 -1.0861324565867765 -654.74498249135422 247.26146568740532 -2109.5958378098871 77.843888551424783 247.26146568740532 -0.31015868426876086 76.983122145052633 2133.1837253573394 654.74498249135422 0.11713001690545018 +-96079.115741800691 258.88625121757485 -2799.5464202514886 42.226814155833566 298.07707759722996 -44.729569712197716 -3718841.3771332116 -154737341.61372355 2660.4965201572536 118.30243899400408 2334.1730940365151 502.22686964986309 0.12052432552028623 10020.459655208489 416941.49643720849 -0.94683675488032937 -457.83395628684133 238.17521765527235 -2146.3851683449493 84.150416577153791 238.17521765527235 -0.21314429994731907 83.274900833471065 2168.9513195872187 457.83395628684133 0.11088231734416776 +-104798.02708691417 238.03336652775434 -2691.0738656956619 80.444473904887062 269.73513871732553 -47.962483792638068 -4382231.8162590237 -196253703.44313517 2652.479801566059 110.18403398083233 2361.2736658598883 247.16227974127662 0.10893975584794248 9953.5976117571572 445761.53790916316 -0.70955803790997041 -229.07651894953628 221.56177102209497 -2183.1735178097751 90.162800942631037 221.56177102209497 -0.10484051210505092 89.321840185321847 2203.7279897179387 229.07651894953628 0.10140126820233179 +-110863.20102677068 208.80634797545474 -2577.1667325955859 111.72472407524485 228.69561660768451 -49.89342980502731 -4950692.6714638285 -233683101.72205514 2572.3718403565722 100.58973351124384 2365.1547726492959 -21.290245311260701 0.093972253814336063 9324.4290901142977 440132.65540092404 -0.42128402878018545 20.07444329571852 197.54251826549915 -2219.9648195667692 95.836621555237173 197.54251826549915 0.0090344029233656708 95.079965744002337 2237.6315200995059 -20.07444329571852 0.088903023521826802 +-114035.78578717713 172.99523550591852 -2464.5805814399246 131.6171320042703 179.77325364892482 -50.480648865505586 -5384954.2608909048 -262914877.80392924 2426.9353658822633 91.65099141176016 2347.0833041257815 -287.94035007168389 0.076580449537812526 8169.1148451416475 398848.66745764046 -0.13847612770025855 276.65809488685142 166.59049995020368 -2256.7630677484303 101.13518161301883 166.59049995020368 0.12246927617833175 100.50600005319581 2270.8907188972712 -276.65809488685142 0.07374524123514993 +-114396.08171077596 132.51133741397845 -2354.2234030075729 138.39021525491518 128.39917025846216 -49.824077400163745 -5653893.588873487 -280566393.40775102 2234.0752893040326 85.193449666568299 2311.8789666217658 -538.83751842002403 0.057713997131523719 6549.219080353957 324995.64205244434 0.08247876865378094 526.87872739333829 129.49944830637179 -2293.5722320343934 106.02997629765252 129.49944830637179 0.22947679764518217 105.55764510811413 2303.8351144579215 -526.87872739333829 0.056402198739708961 +-112319.29806782401 89.082921092543231 -2245.6091092578231 133.10587957816844 79.397747151951918 -48.143719703310765 -5737167.5871925214 -284504410.33011359 2020.3818722910241 82.695218359620796 2267.3725397974008 -761.6609160462732 0.038183849589602761 4550.2745855476242 225646.74434320079 0.19399072349963375 756.84562591952476 87.330645714219912 -2330.3961707433232 110.50006203350419 87.330645714219912 0.32440875521625578 110.1938627367997 2336.8717189345034 -756.84562591952476 0.037432767129969957 +-108373.8765429138 44.061621136338388 -2142.2227483866095 119.00245473953812 35.925267942089427 -45.727374068740005 -5627092.2387105571 -274190179.6580022 1816.9649730599031 85.132845563465807 2223.3081719926613 -945.89766387499594 0.018591401323349532 2287.8097031355724 111477.63834591188 0.16553423873909021 953.34012725236641 41.342826382880439 -2367.2385441172191 114.53034988726293 41.342826382880439 0.40225321824994364 114.37515134761209 2370.4507100529436 -953.34012725236641 0.017444230541299763 +-103169.0979201119 -1.6247500224200679 -2055.2505141580687 100.32691938357083 -1.2217352342529808 -42.862109646909808 -5329468.8999921866 -250777086.93555358 1654.3287946571504 92.674351713592117 2189.8530569089357 -1082.9095574453399 -0.00067501039568760614 -83.930701046299973 -3949.3422530115117 -0.0084758305979377065 1104.5677400328241 -7.0880616906424905 -2404.1027297095525 118.10902406773792 -7.0880616906424905 0.45889810553918742 118.06381750171873 2405.0232592254301 -1104.5677400328241 -0.0029447701249034029 +-97195.511801507775 -47.384892057203565 -2001.9481320857135 80.959939457368534 -33.70432611762439 -39.769031015346876 -4863191.509977947 -216935927.67675236 1557.2798565111414 104.36504105267798 2175.9677498579681 -1166.0563018318999 -0.019388253705893442 -2370.9099369158221 -105760.90732754151 -0.30305031654149217 1200.8529516455417 -56.551626504047974 -2440.9917417812721 121.22443346684108 -56.551626504047974 0.49134736155709563 121.2242407921419 2440.9956215114844 -1200.8529516455417 -0.023138963381361691 +-90704.379545425938 -92.805870034049164 -1997.2451607485054 63.507860640247443 -64.957438119377684 -36.559604814762572 -4258750.9963245997 -176445303.78298426 1540.8503988673165 118.11447078337964 2187.9294412991248 -1190.9664377793936 -0.037406638465960963 -4357.4201538343395 -180533.28861371093 -0.66344927337164517 1235.2364449245786 -105.68082620205871 -2477.9081565596844 123.86189933462838 -105.68082620205871 0.49787845422191801 123.82313053297825 2478.6839851905343 -1235.2364449245786 -0.042596060540934587 +-83659.705594626183 -137.50225014759948 -2043.8977558721924 49.494645467205764 -98.788277602681092 -33.224664652353532 -3556060.1246077828 -133636801.72559817 1607.9360775101109 131.20867224646128 2228.2780346548384 -1155.7958507819671 -0.054607724443049829 -5844.7046319169867 -219644.10236914197 -1.0161366560104819 1203.9388263887568 -153.21823571526198 -2514.8540451249328 126.00091672484787 -153.21823571526198 0.47813297314883113 125.82977278356125 2518.2745554184989 -1203.9388263887568 -0.060849180188745827 +-75774.471566354085 -181.01229248942357 -2130.2460688041442 40.42995091007468 -137.76092809559034 -29.657327423230562 -2802055.7517294353 -92794091.774343237 1748.951909883385 141.16693030434939 2295.3792455698008 -1061.1903643478458 -0.070846298430302768 -6693.6334205850617 -221669.2632008548 -1.2880033520170746 1106.6614421220916 -198.06588854633435 -2551.8309155586103 127.61320209633199 -198.06588854633435 0.43313559378555438 127.21626625281039 2559.7930511158579 -1106.6614421220916 -0.077520895712851021 +-66619.795541362066 -222.73556452594505 -2238.0652670798786 38.601333905118167 -182.11216893456003 -25.702081613179811 -2048200.7369442021 -57576132.901065379 1943.5189131105085 146.42524140514934 2383.693129011891 -909.94148604620193 -0.08593193075846646 -6847.9217580678305 -192499.12673444749 -1.4256602430259677 946.70312820263814 -239.31590362578731 -2588.8396658210208 128.66195932245245 -239.31590362578731 0.36524040439916594 127.95774564951147 2603.0873089040483 -946.70312820263814 -0.092328666522294489 +-55776.666081171687 -261.90556246702732 -2353.1596475078404 46.713286882917927 -229.65832610971137 -21.215924717067967 -1347670.2710650391 -30512835.878292985 2164.005399126404 146.48827023488309 2484.7234047877942 -706.68856871047353 -0.099621743045655117 -6328.1362111122735 -143276.42730632963 -1.4064660999444678 730.88140601312909 -276.26136966674107 -2625.8805486207748 129.10260616117114 -276.26136966674107 0.27800738151887755 128.03259106594629 2647.8259908077112 -730.88140601312909 -0.10508230112846752 +-42986.942868097438 -297.61504191236253 -2470.5860029519167 66.67962593516755 -276.58219930458193 -16.124134609188836 -752050.68775197503 -12631234.582231587 2380.4866127613918 141.64601484342151 2588.5116571672538 -457.97373983138249 -0.11163354910441205 -5206.7344645165094 -87450.866676672726 -1.2390984932187785 469.257196699112 -308.38907358565547 -2662.9531493064133 128.88502757631139 -308.38907358565547 0.17601545262532334 127.42262200666943 2693.5153638952593 -469.257196699112 -0.11567482129994579 +-20908.51212071293 -243.3653001683347 -1918.7998662300472 72.895025234645047 -235.76277568531671 -10.454256060356466 -227675.24585434594 -2429256.8270170935 1898.8758392072309 98.104075364530928 1987.0027428903622 -127.69349544029963 -0.12168265008416734 -2650.0333561923967 -28275.413070034985 -0.70824500691796366 129.24382857557001 -248.13715327860433 -1997.8219589708578 94.677920836629923 -248.13715327860433 0.064621914287785007 93.313558788867752 2027.0326384732123 -129.24382857557001 -0.12406857663930217 +-8836.6541451041703 -263.85749530005307 -2027.7208520213001 104.05747370915809 -261.89951998606119 -4.3380727271007222 -38808.497848149345 -169430.46176307928 2009.758227365377 89.327593417238703 2057.7979126526848 102.72382939829063 -0.12953239828181298 -1158.7997980257576 -5059.0977687076274 -0.45395342540821282 -102.50020119611308 -265.37764260103677 -2034.9098248608148 93.872901007575038 -265.37764260103677 -0.050319195481646088 92.255106552038185 2070.5942000159216 102.50020119611308 -0.13027866597989041 +4170.2522620950022 -278.97273588910576 -2133.3689037114495 139.03753652037889 -278.20529324170042 2.0107291524083908 8442.0766571251588 17051.176528209355 2064.0555477143093 77.777626216166567 2106.9300481941477 342.41401245155015 -0.13450951585781379 -564.74022999285648 -1140.6536265057971 0.35191243899936842 -337.57459489370558 -278.65366171244119 -2072.0186296294864 92.446158127948166 -278.65366171244119 -0.16276499271634792 90.635525421022805 2113.4115016047795 337.57459489370558 -0.134355671028178 +17454.290093523421 -287.50370964093776 -2222.8380436695629 171.10757144905941 -282.90656948041607 8.268256794658182 147640.09302699537 1234786.301492929 2054.9358011976133 65.427672250130584 2128.3344848369143 575.08578061571177 -0.13619313578443284 -2431.8992184474291 -20339.162486176316 0.53220485688473718 -563.21844366393691 -287.87592313923227 -2109.1463225621746 90.368928431488925 -287.87592313923227 -0.26680172603691943 88.446537651030781 2154.9887439029435 563.21844366393691 -0.13636945672156905 +30711.984331090513 -292.83372684332977 -2347.1348481588007 200.67350582217512 -284.22972180318845 14.297944288217185 456210.52670916723 6648760.4344799882 2047.0630173512186 53.794050765248571 2150.8493743087711 792.4212281744658 -0.13632855067194125 -4349.8924498396382 -63394.838833200163 0.57554657124675224 -766.74170259372886 -292.97737772753061 -2146.2903761201051 87.624673934051799 -292.97737772753061 -0.35695609990397059 85.683261925642142 2194.9210399871004 766.74170259372886 -0.13639542724745374 +44002.511484184681 -295.91090196615067 -2521.537250238257 226.96100846695828 -286.17556236881796 20.138449191846533 922551.63234269491 18956808.014506523 2072.564799471621 44.223410311636783 2185.9964480312128 986.25700202367273 -0.13542833041929092 -6204.0341887073937 -127481.95429681205 0.46340330126132528 -936.23653147706852 -293.89175230862742 -2183.4478270424406 84.210839662574841 -293.89175230862742 -0.42848353843344095 82.344317251682156 2232.9406692714092 936.23653147706852 -0.13450423446619103 +57602.246589690672 -297.18530272636622 -2756.3759542937305 249.39539514999606 -291.77690462157301 25.923603325693374 1524260.2842122414 39922668.586580686 2160.058713449378 38.016547478505572 2243.5590059491055 1147.4147165778372 -0.13374676090295509 -7864.0640047269471 -205971.65999547666 0.2024640177126725 -1061.2676224335305 -290.53889876676158 -2220.6153282421847 80.139054663750088 -290.53889876676158 -0.47761819191428012 78.431906618767357 2268.9492178503233 1061.2676224335305 -0.13075558000304302 +71844.407569616073 -296.38396100667444 -3047.9385674084642 266.05186613951906 -301.87894124735777 31.803633275615791 2229276.1425091173 70030872.103061453 2328.693947750482 36.083018654743341 2329.5322037793258 1264.0579726505086 -0.13120139929467661 -9196.5640144544759 -288902.47645990981 -0.17189751485159074 -1133.5018212080142 -282.81878967649686 -2257.789210604938 75.433726774740094 -282.81878967649686 -0.50177150119876679 73.951879461784955 2303.0307771385237 1133.5018212080142 -0.12519645404006058 +86913.492984646145 -292.51669141139138 -3371.8704342629499 272.93211413661538 -314.51393055428298 37.854308791222188 2997382.0837782114 108299997.99673843 2583.6713128698129 38.445692630532371 2444.7233704245641 1321.9578259890427 -0.12740274016175582 -10088.011192893538 -364495.27002053236 -0.59969268879606208 -1147.239667066822 -270.61549058799466 -2294.9655535580828 70.129274463051118 -270.61549058799466 -0.49966884454129873 68.913772076727582 2335.4441984329324 1147.239667066822 -0.11786388962891754 +102657.78085676646 -284.09944497822784 -3688.4176882373326 265.28029173000471 -325.21683988397888 44.002477864023348 3782133.5743993907 152282064.66215509 2914.0255969294667 43.91423761900775 2584.2258243590791 1307.2835679324451 -0.121774301319429 -10466.834957396844 -421431.76766133326 -1.0083708458343135 -1099.8159778330366 -253.81181228303549 -2332.1402630568523 64.266482953348444 -253.81181228303549 -0.47141705007845547 63.330825267923231 2366.5956005862045 1099.8159778330366 -0.10879203269740055 +118483.37605058675 -269.48403080678122 -3959.7206587780852 240.39178351112406 -328.18698078697361 49.992985675353054 4533491.6940381415 198278500.21499151 3293.1966549238314 50.258271764555907 2737.8646434624084 1210.7172397742365 -0.11370634211256593 -10311.181670050086 -450973.72510254814 -1.328628103128503 -991.84308310442577 -232.31448950064902 -2369.3091554382686 57.888609613818133 -232.31448950064902 -0.41849919118330203 57.219978647299136 2396.9951753920313 991.84308310442577 -0.098022991350484812 +133375.09771958171 -247.18489624222596 -4166.4889900154085 199.82005758858554 -317.79986149561159 55.411340971990732 5201267.3664339893 241783186.03094304 3682.559510905277 54.842376848788469 2891.6287221045441 1030.7333669833401 -0.1026941820698903 -9639.5410858717059 -448098.27901928459 -1.5074316908712726 -827.27844161258542 -206.08859945188149 -2406.4680454290119 51.037870360888505 -206.08859945188149 -0.34369690137622994 50.601841162862577 2427.2042539900067 827.27844161258542 -0.085620523245484623 +146053.00166120779 -216.17069031530249 -4311.9002261021969 149.49781151605717 -289.96955087606841 59.759820646975363 5739051.3766821399 278105646.09602517 4037.507604350526 55.319353954982134 3029.9450937115134 774.70852241899718 -0.088449545955524747 -8494.2773085222871 -411619.67787977756 -1.5148974094097716 -613.30775576052577 -175.19858725268014 -2443.6128364632923 43.752826293625724 -175.19858725268014 -0.25094425358450317 43.500637602504774 2457.7793304918423 613.30775576052577 -0.071685183000278291 +155228.53167643765 -176.12929099737124 -4413.0667223600767 98.059264980421389 -243.12713182602801 62.566921272244116 6108036.3118727235 303067364.97631902 4315.0312613348106 50.028885510853243 3138.4168297462302 458.00989617032911 -0.070991249898174624 -6930.4533991133458 -343873.89702935057 -1.3463073405111123 -360.04482942756488 -139.85188366330988 -2480.7396113753589 36.066996675141084 -139.85188366330988 -0.1451208502327952 35.944130998438261 2489.2193754594691 360.04482942756488 -0.056369159074288543 +159889.02567948098 -127.6820049276882 -4488.6019888980891 54.481732822690347 -178.75356592614793 63.498421636013099 6280211.1145149041 313641535.28986132 4481.3148052246042 38.074878473812056 3206.4716185084353 101.93741694184351 -0.050707706484387688 -5015.1656316792505 -250463.59425997193 -1.0219820237801429 -80.059760531927864 -100.4399364281233 -2517.8447214812099 28.00879986767206 -100.4399364281233 -0.031794980354220757 27.963521044531902 2521.9216417537605 80.059760531927864 -0.039888775388452466 +159527.60880417743 -72.478345667285296 -4549.0640256600509 25.853514271937591 -101.39445572897709 62.4374202756076 6240639.5549515644 308424038.38628972 4517.8486137373138 19.278814530127942 3229.3585552307072 -268.74148993691063 -0.028367258578193853 -2835.3163081879575 -140126.61653884331 -0.58607481988907806 212.24295693704198 -57.570795312540895 -2554.9248720573378 19.602711613649564 -57.570795312540895 0.08306965046459569 19.593318830582042 2556.1496699225972 -212.24295693704198 -0.022532600905104068 +154250.77400192543 -13.104889463401703 -4592.2855108852345 15.628808276218738 -18.049676154350053 59.510329476051481 5988720.9131104555 287861519.80078834 4424.8665943502274 -5.792103065713146 3209.074000535943 -628.28017276141122 -0.0050558987127321386 -508.79177820200096 -24456.236425167386 -0.10329292267031828 501.36580190967356 -12.0874045327294 -2591.9772022619231 10.871359700571066 -12.0874045327294 0.19342816431700369 10.87119839095145 2592.0156626819717 -501.36580190967356 -0.0046633505141702933 +144732.24547781638 47.212002064476714 -4603.538404263616 22.65628629830988 63.099208098107788 55.052204441923315 5538442.0493327286 254206202.16943258 4221.4526354164218 -35.652215551070178 3154.0289533415839 -952.28752569803419 0.017958159781086617 1806.6529445724625 82922.666624871519 0.3484303586006699 771.54088533438141 34.93342563094658 -2628.9993576257275 1.8381603469698944 34.93342563094658 0.2934731401043672 1.8378257566612346 2629.4779871711817 -771.54088533438141 0.013287723708994515 +132027.31870486165 105.07195122512267 -4560.9487837477645 41.387716687488066 134.47403630450029 49.522625170615768 4917710.659715944 211209989.75102758 3942.2912745332305 -67.687496984325506 3077.5311193652083 -1219.8125380267536 0.039411834668088022 3913.6858920237432 168088.28626587451 0.69096810364534511 1007.590119560028 82.204790473123651 -2665.9895533650115 -7.4699508400937216 82.204790473123651 0.37794078003001802 -7.4633336134819697 2668.3533036584877 -1007.590119560028 0.030834505053684791 +86794.410861857643 116.4990344874959 -3290.0059540731299 47.04256363428091 141.30971371148198 43.397205430928814 3083181.6960925544 121055318.96080308 2687.1210142906916 -72.655266186387053 2216.3468079854892 -1047.0559221631975 0.058249517243747953 4138.3735102020255 162485.43700533366 0.63962507930630486 884.77681239401647 94.914819531907384 -1999.9605082760015 -12.593512517410812 94.914819531907384 0.44238840619700825 -12.568426170275513 2003.9523926128134 -884.77681239401647 0.047457409765953688 +75502.828504921956 150.31361945462479 -3166.2392214556348 60.322142751406588 171.95260467113144 37.065698824213037 2480739.2767702625 86571830.397868901 2479.7821661943976 -91.438668112734973 2173.1457335566306 -1136.5566053148107 0.07379166394434207 4938.7407994957175 172350.1679022169 0.62954928736427407 984.74403733291319 127.57984302913388 -2036.9034123344131 -19.903100191641389 127.57984302913388 0.48342858975597114 -19.836552634581409 2043.736804635739 -984.74403733291319 0.062631243509638626 +63805.416568952409 177.97497750295469 -3003.2068540744908 66.321389610024823 192.57203536686075 30.764424575194028 1856747.6210966874 55555336.562597707 2310.6374601332104 -103.57972261786564 2145.893587293097 -1163.8134614595272 0.085812428882813249 5179.0997357761335 154962.7023312723 0.49692297824583331 1034.7341880970855 157.32826987453626 -2073.8208292605104 -27.392005293911662 157.32826987453626 0.49890751595809329 -27.26111012502647 2083.7783521360911 -1034.7341880970855 0.07585741073989212 +51846.4051446464 199.44915881966637 -2829.4319186949797 63.706646204416728 206.45651716318537 24.560116127260255 1258266.4111695907 30719659.336122315 2199.8995044898088 -107.98371319092885 2140.3266957959295 -1130.2408850748834 0.094480890014053229 4840.4547350710418 118176.18206551732 0.29341764635078815 1030.2063292066312 183.01891523384424 -2110.7131153429286 -35.01672895352808 183.01891523384424 0.48801815689560929 -34.801504555815669 2123.7664865920015 -1030.2063292066312 0.086697733412526873 +39496.246011534502 215.1744678114228 -2671.9205219253554 52.912957594062753 216.74119919769055 18.387451588237663 732956.18435386021 13539514.893278619 2154.1344215178665 -105.48969456055812 2157.3141710787595 -1040.0106770839966 0.10017433324554134 3993.1252416083862 73762.906751574352 0.087109404901005633 969.48325510681298 203.64498373381832 -2147.5811829108702 -42.728159796418439 203.64498373381832 0.45134229753576022 -42.415360509463873 2163.4188854465278 -969.48325510681298 0.09480678944777389 +26496.562826570709 225.59500586818231 -2544.9958702635181 34.865172311404201 224.89418197001837 12.126573376004902 325223.40909062669 3967846.3515937091 2164.8781667582202 -98.445428896242973 2192.5789046200721 -898.59250267244931 0.10324714227376765 2768.9922410878175 33782.733512672727 -0.05946145097531913 853.94005934005122 218.41189362937729 -2184.4264969690967 -50.473110503700802 218.41189362937729 0.39081924912588156 -50.058758867163199 2202.5076622715987 -853.94005934005122 0.09995967671825047 +12651.775233750654 230.79541650537132 -2449.0730655898192 10.544756880683472 230.22772224668819 5.6938682420119955 72435.343066056535 413608.90387364564 2210.2983858816788 -89.816083912362629 2237.4276265093213 -712.27945938188668 0.10386832425984308 1321.3754484068525 7545.1102692052054 -0.10536891262034055 688.02670072325543 226.80497633862905 -2221.2510594646515 -58.19619696311193 226.80497633862905 0.30964297962342729 -57.686487387989672 2240.8777170238932 -688.02670072325543 0.10207244659704277 +-1999.7368406082458 230.33014989326779 -2374.4914134740366 -18.428614801451417 230.27604941061813 -0.88523100513866571 -1782.572197251292 -1588.7058581220422 2258.1684455157811 -82.462785849608821 2279.6858712002177 -488.65790367297103 0.10196111106386356 205.3170762526826 182.98750665925684 0.049104251449975815 479.11849791386072 228.64036146140282 -2258.0573812449629 -65.841755792732172 228.64036146140282 0.21209318190077942 -65.252302680751569 2278.4554192562446 -479.11849791386072 0.10121308608295831 +-17132.878504450819 224.20993601286213 -2322.7420236395619 -49.883059839594267 223.61725527385636 -7.4620550977573261 -128075.64961933305 -956604.68081437226 2288.0624647175387 -78.496593754052043 2314.052587535467 -238.9486507778212 0.097652411155427749 1676.0658869142476 12518.636271148283 0.078266642901364328 237.19811596434309 224.09367582545676 -2294.8484420438535 -73.355510690619255 224.09367582545676 0.10330928395659542 -72.709201954769085 2315.2472437865513 -237.19811596434309 0.097601775185303469 +-32383.455862776354 212.99476590107764 -2304.6708470534481 -80.883538803205951 210.34122083637104 -13.880606885030586 -452498.02734051977 -6301912.4125500331 2296.2968886858498 -80.09898452549433 2343.4921949317768 25.626834318234025 0.091296513459527495 2976.2021636139998 41449.385906433999 0.19068817923273362 -25.617745060794132 213.70303737949877 -2331.6276391059296 -80.685768920271684 213.70303737949877 -0.010980602254948192 -80.009702894768253 2351.3294274377467 25.617745060794132 0.091600101748606416 +-47215.915028895004 196.01613307442196 -2298.6262583169901 -107.67706778809296 188.89133115748481 -19.922327016411398 -962249.12087666057 -19389159.475002717 2257.5529463039143 -88.083005849287218 2358.9518768293296 294.90157426367796 0.082707229145325731 3994.7621816304172 80493.792432645918 0.35581629258176706 -295.68463671461228 198.34499848037078 -2368.3987253247783 -87.784037847788852 198.34499848037078 -0.1247614500905537 -87.106129979270833 2386.8309083647987 295.68463671461228 0.083689872776527757 +-60940.093340227351 173.23940499041001 -2277.5662789974649 -126.04412288709933 159.58147320397245 -25.317861794859724 -1631021.9719298158 -42457317.319807634 2154.7971225611341 -102.49193566137521 2353.6531874678712 554.67173343468744 0.071973163685255512 4636.6400255722419 120696.90062514538 0.52949844506752308 -558.53924711693912 179.18436734690692 -2405.1657380202237 -94.605071334486226 179.18436734690692 -0.23204787998211013 -93.950905549828732 2421.9125391610478 558.53924711693912 0.074443027564149111 +-72869.83520309368 145.74232704182353 -2211.0194117644519 -132.72835085805292 125.07186485767812 -29.815808184571878 -2420292.3541237032 -76164321.359454989 1983.6009963686704 -122.21554453681475 2324.8051693961429 787.20609605049117 0.059632703372268218 4840.6729455118375 152331.4194625346 0.66402469014056209 -799.67398430296328 157.60101055940157 -2441.9329197089814 -101.10646019953577 157.60101055940157 -0.3271988479144694 -100.49684393834686 2456.7457432589472 799.67398430296328 0.064484865204337799 +-82507.193955276351 115.86815850635072 -2072.6200898272727 -127.03313112659049 89.905959816465611 -33.255620296362899 -3281426.1555520501 -119338557.81012763 1754.2772369271736 -144.61275393181376 2274.5827273783952 973.70490571474147 0.046702200123478727 4608.2382358620425 167591.91858783233 0.72240267144944037 -1005.3357618348298 135.09864792593169 -2478.7046324260159 -107.24794975314984 135.09864792593169 -0.40521393060654165 -106.69744696949738 2491.4934463926033 1005.3357618348298 0.054453304282922887 +-89692.992989089966 86.881861527179794 -1854.1443377532109 -111.62954968494607 59.050350799215082 -35.620727954364561 -4158648.8108562757 -169005410.78663623 1491.2775429039189 -165.6343906474012 2210.132474076529 1098.440852711846 0.034504313553288243 4028.3096603648278 163708.49280383289 0.69332999569344744 -1163.3051168490122 113.20216527085556 -2515.4852673260316 -112.99069097373561 113.20216527085556 -0.4619956778590199 -112.50719917269234 2526.2953889123883 1163.3051168490122 0.044957174452285767 +-94669.328081493193 62.138335170503005 -1579.5276509361524 -91.788709929625654 36.003985418588016 -37.052574591582463 -4992906.6059711194 -220716478.43751693 1229.7413540359944 -180.88104773494533 2142.5683520143989 1153.3091326063859 0.024320287737965952 3277.2061494910158 144872.20721580941 0.59848556691416399 -1263.6118174060603 93.350994188877863 -2552.2791514316723 -118.29661169483812 93.350994188877863 -0.49456431209630541 -117.88186106107476 2561.2589841732747 1263.6118174060603 0.036536592637525581 +-98022.651165816991 44.09252206240344 -1305.2950822118148 -73.3015494440385 21.611569567301938 -37.817380851009638 -5726463.2268571742 -269161064.7727102 1009.6521037309178 -187.35197312562843 2085.1512798140675 1140.366823922747 0.017011003882100091 2575.8761180884399 121073.95633234411 0.48391715609504227 -1299.1455906722388 76.796570909728629 -2589.0904534937422 -123.12804041591878 76.796570909728629 -0.50121357664824029 -122.77875886149438 2596.4559094286874 1299.1455906722388 0.029628306678136044 +-100513.94725165098 33.693690162094306 -1099.562832393314 -60.474854280047992 14.470201506285974 -38.232768068334337 -6307701.5945955832 -308966525.05769366 868.6774750748009 -184.74636109451026 2051.0140122261751 1070.1359156788906 0.012816162100454283 2114.4303748329903 103569.92885464347 0.39668016070692103 -1266.1254380110124 64.511745854296223 -2625.9230909864737 -127.44764354688567 64.511745854296223 -0.48159963408558859 -127.15706910763298 2631.9237493457681 1266.1254380110124 0.024538511165574827 +-102843.39807443629 30.493120555280054 -1011.7286758352002 -55.349144266119708 12.578006505681198 -38.57591825747798 -6695331.1984658511 -335527433.39483267 835.19423812490595 -175.27128355206537 2050.8517549612084 955.96696085352357 0.011437779653143307 1985.1691524678938 99484.057000013825 0.36084393442831825 -1164.3979761517135 57.119259519220392 -2662.780640275796 -131.21866404308437 57.119259519220392 -0.43675843066455872 -130.97809653686687 2667.6713702159359 1164.3979761517135 0.021425078589354985 +-78003.874379456538 24.612086284980105 -781.44779032589838 -42.873154464172345 10.961607490635284 -39.001937189728274 -5076871.80893471 -255787495.36043221 682.9087955103231 -120.03905110629975 1547.1571587289463 597.69575269733116 0.012306043142490052 1601.874368591519 80707.06687984601 0.27302682372579834 -738.1013769586358 40.58087881343873 -1997.5332977948501 -99.449049710622319 40.58087881343873 -0.36905068847931788 -99.301179251964712 2000.5078462055926 738.1013769586358 0.020290439406719363 +-80455.692553226734 30.201715437543001 -905.94708336122699 -49.628779850942095 15.763155151922861 -39.497149019748029 -5050108.216441296 -251452250.88054436 838.84564708618802 -110.51884112943458 1614.8207266207246 467.54810923490925 0.014826566243270986 1895.7257894567813 94390.950922456483 0.291675669895268 -574.51032236981916 42.747535986544385 -2034.4593802885238 -101.83048750865282 42.747535986544385 -0.28203746802642082 -101.70560434920034 2036.957469915088 574.51032236981916 0.020985535584950605 +-82711.637514587026 38.364559879472168 -1101.0250572344776 -59.217743830453173 24.019165355156588 -39.8802495248732 -4849252.7205338608 -234481117.11861387 1068.5126425797489 -102.15433342658089 1709.5968145279357 311.50001664650239 0.018497859151143766 2249.2535749253511 108760.57020048838 0.2977955025326337 -373.92740107376341 48.167454583934251 -2071.4096882448948 -103.7313199062595 48.167454583934251 -0.18029286454858409 -103.62385557963034 2073.5578677936601 373.92740107376341 0.023224423618097519 +-84123.742005319655 48.390041723905796 -1350.8824999884489 -69.52909434865353 35.734644189343278 -39.850185696503864 -4485950.5531708691 -206793861.3335782 1346.9067184713656 -96.171965306014201 1824.0588108886086 126.37001858611433 0.022922805174754049 2580.428892780229 118952.90603628462 0.27494482507356938 -146.00451816034035 56.170086132840872 -2108.385643744391 -105.12308840871336 56.170086132840872 -0.069163675111482872 -105.02845925057811 2110.2852694259968 146.00451816034035 0.026608283340995201 +-83916.069002380827 59.675792671922338 -1643.9369596279253 -78.627704385986632 50.284728015599626 -39.067071230158675 -3983377.740967948 -171537925.93019018 1642.3290228502588 -93.632276531280937 1947.9735113225438 -88.742322093445978 0.027782026383576512 2832.7259252004451 121986.90697595915 0.21784480821989186 97.758418506803267 65.850953279835082 -2145.3881609437785 -105.97973597950076 65.850953279835082 0.045511368019927036 -105.89447049904071 2147.1156123532251 -97.758418506803267 0.030656868379811488 +-81377.351269137172 71.669801821319894 -1969.4177689590224 -85.066757099692026 66.498127997556026 -37.243639024776733 -3375134.8214595541 -132651159.96092509 1921.7334849198826 -95.550026749047419 2070.011416375009 -329.40481172193154 0.03280082463218302 2972.5131133136588 116827.12938548691 0.13105600512731541 344.59219735017706 76.136056395424674 -2182.4176106627351 -106.27789861081938 76.136056395424674 0.15770809947376524 -106.19968299933301 2184.0249518818009 -344.59219735017706 0.034844877068844246 +-76040.70827849998 83.76943447703745 -2311.0804953268903 -87.9807006225424 82.856754561253183 -34.221740899414925 -2703370.2575116558 -94294508.583589405 2156.4745428522524 -102.71902310833056 2179.7114507098677 -583.92568387253323 0.037700015516218474 2978.1389834558472 103878.53870859892 0.025966631923676074 581.12659424536355 85.858922666964219 -2219.4737960438306 -105.99714262557518 85.858922666964219 0.26153312072248586 -105.92482558299952 2220.9890761504594 -581.12659424536355 0.038640379238057701 +-67798.194712833734 95.147985854996335 -2641.5683389830629 -87.120027931551917 97.608393472645957 -30.012481059244678 -2016135.0832656103 -60231203.19080513 2327.4014987503897 -115.2087873886115 2269.3376297509558 -833.07329830699223 0.042119515650728787 2829.4439578640554 84528.470020505149 -0.081358270467567245 794.11339223523282 93.844132405216584 -2256.5559400967609 -105.12019063636654 93.844132405216584 0.35153315282657494 -105.05374440744271 2257.9832060492581 -794.11339223523282 0.041542333955385827 +-56915.646428877553 104.56529945050416 -2922.0029605869977 -83.006115980323898 108.81629487013565 -24.789044611880467 -1364058.6990356483 -33247884.251827203 2428.2856542410486 -131.6856461209083 2335.2598588507385 -1052.0628940025169 0.045542377809453027 2506.04561807031 61082.939244701032 -0.17124031100787981 971.16517496493566 98.990620347215128 -2293.6626856915868 -103.63317610853706 98.990620347215128 0.42298134798124376 -103.57356934110254 2294.9826925152283 -971.16517496493566 0.043114381684327144 +-43943.172382420635 110.33304866457691 -3111.4162533115355 -77.108252007933899 114.5607366128697 -18.835478946601214 -796564.19933877944 -14718870.711663073 2466.8216133558635 -149.02494121725022 2378.5676699614905 -1215.2303887725632 0.04729234833458075 2000.0230253122197 36956.318592219031 -0.22264016847049628 1101.4689282850338 100.3481002992816 -2330.7921083021988 -101.52594517639285 100.3481002992816 0.47212555863053318 -101.47486327258996 2331.96541659177 -1101.4689282850338 0.043012473338740503 +-29550.44435108787 110.60954677106375 -3185.399296984353 -71.630954131749505 113.42580343341426 -12.468541920290241 -358022.0288019625 -4400398.1359969918 2462.9102154366478 -162.85403610455529 2404.7851703475085 -1302.6396057286429 0.046670694840111285 1340.1035148354358 16471.022826326556 -0.21946164737282425 1176.4337240112429 97.180594284406013 -2367.9417415256471 -98.792390643529288 97.180594284406013 0.49638553755748643 -98.751753100828168 2368.9161782380443 -1176.4337240112429 0.041004470162196632 +-14342.171641356439 103.98021749472829 -3153.0307817096877 -68.685847890556474 105.01601560076419 -5.9585258169324629 -84376.252382668463 -499571.90863109659 2444.5320967705416 -169.17155859363055 2422.7552391909494 -1305.8858055284211 0.043199093267440085 611.72472994544273 3621.8779842189292 -0.16034796058102307 1190.2342980069393 89.012206272611081 -2405.1086151211839 -95.430767061481731 89.012206272611081 0.49448869879806373 -95.402041224810418 2405.832800435961 -1190.2342980069393 0.036980559315584166 +1284.852607915062 90.04627483395457 -3062.4161609088837 -69.301724971927314 90.048102740133857 0.52571710634822499 676.50540719820538 356.35944564795972 2446.2950204134117 -166.43480150373645 2445.0308354875924 -1230.5612172090282 0.036843811306855385 47.411501870300903 24.974725026680403 0.082323571120992714 1140.2170921880308 75.651866626215991 -2442.2893050308585 -91.443909265560222 75.651866626215991 0.46653727176269677 -91.427296426647672 2442.7330823313814 -1140.2170921880308 0.030954118914163661 +17418.12982734574 70.009557857359397 -3033.9497973514149 -75.014853650809414 71.035495418225437 7.0206085559636184 119585.19736508153 830241.63540480484 2548.1154731975207 -156.66340908754179 2508.2046621523396 -1101.1379819694712 0.028218282086803468 480.6547474844607 3337.0316093411925 0.15774619432085443 1027.1421003155053 57.19565409039258 -2479.4799945674995 -86.839270206235312 57.19565409039258 0.41400326493974421 -86.832923131457122 2479.6612327933826 -1027.1421003155053 0.023053467992903094 +34326.425491232672 43.560231038539598 -3047.3788183850102 -82.760306405240655 45.529604560176317 13.632416795565 440621.68788873177 5828692.1306578415 2715.4504328013913 -140.38131068281862 2593.7825052922453 -915.59435135624881 0.017299535757958538 559.1488845795111 7396.6098197093597 0.15295530058775825 855.24231509455024 34.007320063869564 -2516.6765456921466 -81.628713346788928 34.007320063869564 0.3396514357007745 -81.628208133236754 2516.6921219142196 -855.24231509455024 0.013505687078582036 +51996.55961218437 11.61130749364643 -3113.2170788371682 -89.350092430349989 12.616654717036475 20.350903957802103 949952.07415712322 18317116.01801531 2933.1933003362979 -119.68919098315681 2695.3903686583071 -680.0636795692044 0.0045445430503508535 212.1329895580412 4090.3795956556614 0.052829720168347701 632.09228968040361 6.6796205183767743 -2553.8745790574681 -75.828039659112207 6.6796205183767743 0.2473942425363615 -75.826343997588921 2553.931689906161 -632.09228968040361 0.002614332883904804 +70093.302766640481 -24.498628916095768 -3231.8290273971843 -90.835830389089665 -27.713789943831426 27.042169277253276 1620795.0650189607 40529819.833847076 3173.0252237684176 -96.558076551744691 2802.3497950474293 -400.44127964810275 -0.0094516315262715162 -566.49145181723009 -14165.761591394159 -0.12931366565662125 368.28778788494282 -24.01917834505003 -2591.0695612722775 -69.456291482098678 -24.01917834505003 0.14208633791857361 -69.444428489896893 2591.5121862411629 -368.28778788494282 -0.0092666583121334984 +87915.663386189961 -63.067699140210976 -3396.7120908618213 -83.37668402937112 -73.868298259851713 33.440723996268531 2414654.0078333947 73179110.793303221 3398.1817234338846 -72.664245007327892 2901.5129225064325 -84.84345041467904 -0.023989235123701397 -1732.1904496675024 -52496.198801193234 -0.35672627550162878 76.94913520588014 -57.207892423841763 -2628.2568976503885 -62.534941535313891 -57.207892423841763 0.029269355346473996 -62.503439530609249 2629.5815505299183 -76.94913520588014 -0.021760324238813907 +104475.48294667031 -102.19164226477552 -3594.0021552206726 -64.35508511899674 -122.59958600633229 39.188103130784064 3283504.1085927188 115232869.68621983 3570.0484186352992 -49.532691332544658 2979.6830948305496 253.92419414921363 -0.038331448711468694 -3211.7265005753366 -112713.87184808389 -0.58010481187277685 -226.92889842687717 -91.957575591083085 -2665.4320285494964 -55.08712881088038 -91.957575591083085 -0.085119616814282512 -55.028185346382273 2668.2871072179632 226.92889842687717 -0.034492714025162449 +87816.121315044409 -103.61064083136213 -2810.8757721849443 -24.69536873646549 -125.49501108824579 43.908060657522199 3087596.6107981992 121315414.55756415 2704.7183000831815 -21.261611232055362 2239.1212941085896 441.72043579600341 -0.051805320415681068 -3642.9286409252682 -143135.08336285627 -0.55419167844867268 -390.12108291058911 -94.230536164179696 -1999.6970235197125 -34.877638713941622 -94.230536164179696 -0.19506054145529456 -34.81112072890555 2003.5180960362525 390.12108291058911 -0.047115268082089851 +96342.832272912288 -130.16215833435541 -2952.9349652937558 5.3264755072603638 -156.46605287192824 47.296432141832248 3736259.2129800003 160014810.70613202 2700.9541393910581 -8.8725410787454191 2256.3047496028212 683.76747530417435 -0.063898948617749349 -5047.8022265370982 -216184.9785355482 -0.60985420740332352 -600.44129576664579 -120.83823380487195 -2036.7979337200281 -28.778373512441537 -120.83823380487195 -0.29476745005726351 -28.691064703565715 2042.9960446424061 600.44129576664579 -0.059321666079956772 +102037.14566615756 -154.11864638204722 -3032.877423549653 38.91272691708491 -180.36628071925693 49.19823802611262 4321519.9375445116 197265279.94292948 2612.7435362313545 -0.47014139610629457 2245.1774703392457 892.03518841584628 -0.074309858429145226 -6527.2974732797629 -297952.84574550134 -0.57002838535758549 -786.03197376790661 -146.95934206857348 -2073.8813765526329 -22.280866485848247 -146.95934206857348 -0.37899323711085181 -22.181884233665638 2083.1356602396427 786.03197376790661 -0.070857927709051818 +104806.44676978358 -175.06062834506358 -3009.8377613803777 70.724367673940719 -195.35804817994693 49.647772036846789 4804674.945818386 229219839.0771251 2449.046447373486 3.3215146377604841 2210.125739850153 1046.2665897315167 -0.082927820153985587 -8025.3595168274378 -382871.19060661522 -0.42124949218494578 -935.81922208897947 -172.09997843761346 -2110.944474090456 -15.404908259591428 -172.09997843761346 -0.44330612131169089 -15.311019135539407 2123.8890551036661 935.81922208897947 -0.08152533322482873 +104948.6460286009 -192.8545557707792 -2860.2302274444683 95.14832618094286 -200.9267761913186 48.858773756331885 5153173.0181267019 252403466.02228123 2231.4065420340303 2.9263834747141351 2159.6298617309003 1131.8154212429961 -0.089783312742448426 -9469.5160998070805 -463818.83098774211 -0.16304769208237052 -1040.3261200336592 -195.8472245754985 -2147.9846938868336 -8.1736685333257988 -195.8472245754985 -0.48432314712926405 -8.1089350648214378 2165.1320132842702 1040.3261200336592 -0.091176547753956477 +103061.48311710363 -207.61586522170586 -2597.2456018146004 108.30594752946931 -198.19392680740521 47.167726827049719 5342664.0677534342 264187047.68953276 1990.6733745871757 -0.012815133477871665 2104.8795619677944 1143.9273564310117 -0.095018702618629686 -10762.719392997618 -532200.98166167224 0.18844886613839168 -1092.2560837526946 -217.85780117435374 -2184.9999151336174 -0.61502497875855677 -217.85780117435374 -0.49988836785020346 -0.60898775468744759 2206.6609977768744 1092.2560837526946 -0.099706087493983409 +99873.948139441723 -219.76350572247154 -2274.6082461182932 109.84905234967295 -190.08486937987135 44.947771439892769 5358386.2823862126 263135550.06193233 1762.0996387005316 -3.1070475025385198 2057.9482988545128 1088.8668063662419 -0.098903467921904381 -11790.639865245823 -579005.75714783592 0.59784688808978936 -1086.971918166096 -237.83591345806585 -2221.9884874587942 7.2371099707588122 -237.83591345806585 -0.48918628180292345 7.1527337835861484 2248.1998525376343 1086.971918166096 -0.10703686474260389 +96048.157441890638 -230.05907873348858 -1966.0759661908107 102.99598375638162 -180.97248542007452 42.517997982244637 5196159.8853357276 249210459.61951318 1579.6775896693268 -4.1077174394428715 2029.8574274519394 980.3964089377879 -0.10184111497719726 -12446.08733796358 -596920.65186675906 1.0131396940004136 -1022.8402514340813 -255.50401277289882 -2258.9492808700234 15.341755217884405 -255.50401277289882 -0.45278452918728701 15.137584245990766 2289.4172777867593 1022.8402514340813 -0.11310491933284587 +92004.890961623518 -239.46229906722627 -1734.2855498815225 92.352642825388926 -175.57967383157728 40.071816620916159 4863181.309093927 223818244.71204141 1470.58686184251 -1.549607007597775 2028.7872080407269 833.27803480567945 -0.10429542642300796 -12657.463803116119 -582534.59019142936 1.3756019172324134 -901.4178916345777 -270.57107856425375 -2295.8817255179779 23.650419929504316 -270.57107856425375 -0.39260361133910177 23.304558193433184 2329.954786796839 901.4178916345777 -0.11784454641300252 +87818.435975539935 -248.78560629049269 -1612.0201227401485 81.158630662548433 -177.49943777359471 37.641849968083982 4378433.654778149 189680052.85649982 1450.6122543108258 5.2322422551156293 2058.6664833155528 657.71575571486289 -0.10663763664401743 -12403.901974638671 -537354.90078940545 1.6332873903048255 -727.4650100707828 -282.70329363029555 -2332.7858401540161 32.107502155602369 -282.70329363029555 -0.31181526363942685 31.610504218066751 2369.4631972524899 727.4650100707828 -0.1211758652508768 +83210.304492910524 -258.32340335687934 -1604.0281320189004 70.058247137739869 -188.04586923616756 35.10983311937153 3772285.4386216495 150499027.70235848 1521.3787760615414 16.474914928478139 2118.4147025594848 457.6220475821533 -0.1089972166062782 -11710.924733142481 -467218.829144975 1.7518883831767227 -508.77948665763807 -291.50166387273907 -2369.6622483863043 40.651655574208164 -291.50166387273907 -0.21467488888507935 40.01035581901624 2407.6440105743682 508.77948665763807 -0.12299648264672534 +77646.154614115294 -267.6464753986005 -1699.5440746440347 57.881789890603336 -205.90807894442304 32.258477197388991 3084995.7951944652 110444758.41277225 1670.0639772337404 32.297601067865756 2202.0752106587006 232.69216811511805 -0.11119504586564209 -10633.987675330409 -380703.33891402633 1.7193593152493898 -255.8567304082724 -296.49031638303347 -2406.512182092411 49.218082032179268 -296.49031638303347 -0.10629693826683523 48.45737756857632 2444.290630916676 255.8567304082724 -0.12317836160491627 +70509.198345233061 -275.61358471265635 -1882.9619129485354 43.403665646445411 -227.57478998611026 28.849917489866225 2364179.1277541541 73531195.403923601 1871.7969539107442 52.719760370306801 2299.8961186563747 -17.597448457185088 -0.11277151583987574 -9241.3458044544113 -287426.27670014248 1.5449392944764846 18.610744535742352 -297.11805788849654 -2443.3374716190638 57.741514939595689 -297.11805788849654 0.0076148709229715023 56.903425047718748 2479.3236435531935 -18.610744535742352 -0.12157040011804277 +61297.633198151809 -280.52796092053342 -2137.3080696834049 26.879641334722351 -248.19490863337998 24.706825150403791 1661522.8493535391 42998011.24845095 2094.3730510644541 77.399419472930774 2400.1764981224474 -289.99160738866135 -0.11307052032266562 -7603.9415004048888 -196779.93732928977 1.2553580671032816 300.37031221547397 -292.77447113782523 -2480.1405226854158 66.159487268253073 -292.77447113782523 0.12106824353707134 65.299216945637824 2512.8145942498259 -300.37031221547397 -0.11800663891085257 +49784.597777177121 -280.34669601019004 -2441.3720092534627 10.739837202674376 -262.54725146443525 19.771484423025068 1029132.9183140844 20805721.695196252 2304.4045229566327 105.20147836126543 2491.5180651847604 -574.63502718530401 -0.1113370516323233 -5795.2464474252038 -117161.04168325232 0.89072389361138016 574.35083932454256 -282.82052242597786 -2516.924280198476 74.415372487418281 -282.82052242597786 0.22809802991443309 73.594617652268312 2544.9939657078157 -574.35083932454256 -0.11231950850912545 +36091.090335442801 -272.91381325894702 -2764.1246401648177 -0.73026694609272924 -265.96300100593061 14.125671364165481 515872.17934353912 7330336.5912590139 2473.7234586677055 133.77665211649341 2565.0508257546585 -854.06258928195678 -0.10681558248882468 -3900.9251953961875 -55430.581148928984 0.5010329076944614 825.47025884638379 -266.6324385883176 -2553.6921794819482 82.460679000035725 -266.6324385883176 0.32308033614339876 81.738928624849052 2576.2411450686104 -825.47025884638379 -0.10435711882125934 +20651.027829068811 -256.2630489419613 -3061.7565861533599 -2.711892443787038 -255.19214868928788 7.967217526646917 164002.79686485755 1304596.1751585049 2584.8097638545955 159.4734317269008 2616.2231223494241 -1104.5223426745004 -0.098866917030077661 -2035.1460037470026 -16189.014718848231 0.14224265867642408 1039.4657974502209 -243.65650803045747 -2590.4480857060153 90.256176030955672 -243.65650803045747 0.40102847123851115 89.681186778724808 2607.0566951729943 -1039.4657974502209 -0.09400328241915798 +4082.8959848921268 -229.04003490493645 -3283.7289299425647 8.0795424170377643 -229.17807577817408 1.5530224362465297 6336.788840032983 9845.7702303495589 2634.2317721254176 178.00625491651712 2645.8338615786129 -1300.044185660694 -0.087120591443490478 -355.47766645951248 -552.32255868620371 -0.13055363116768529 1203.7011893985207 -213.47144778746991 -2627.1962225792581 97.77161162296423 -213.47144778746991 0.45785515001845595 97.370468137299071 2638.019654677435 -1203.7011893985207 -0.081198724909650022 +-13071.308381918281 -192.18045654019554 -3434.5428337572994 32.125320480101294 -192.02999065339344 -4.9029663848155591 -63936.797706469886 -313126.42204354296 2667.4145174703699 186.88993664663988 2677.0226416399901 -1427.8650523567503 -0.072085692625729766 -940.02854296857106 -4603.730321776311 -0.027700773614035723 1307.9053697938505 -175.85403992105171 -2663.9410916186666 104.98401119902479 -175.85403992105171 0.49058716046280959 104.75619497454991 2669.7344387506764 -1307.9053697938505 -0.065961755409246703 +-22469.114931484979 -106.7558946699472 -2544.5899532476278 47.902400492513159 -105.2425837442006 -11.234557465742489 -254745.92746820618 -2875069.3698467887 1963.9761993114148 136.4267688706837 1992.5146664187666 -1081.9309995553176 -0.053377947334973593 -1210.3560591203259 -13660.110945717179 -0.12792859350186173 995.04244539242109 -96.813097814511778 -1998.2888520487288 82.778225156342643 -96.813097814511778 0.49752122269621052 82.714350494785549 1999.83199448235 -995.04244539242109 -0.048406548907255889 +-35097.569993115889 -64.974871938978566 -2455.924647734028 73.557123879896963 -62.707344911560348 -17.230029451701469 -624453.05195492075 -10933382.610586943 1936.6336962937446 127.85676178744643 2003.3484109582068 -1039.3955856190144 -0.031897335267048875 -1156.0275281346692 -20240.57890965329 -0.12578353007766543 974.3233383383315 -58.578323239527379 -2035.0967309396358 88.041480705033152 -58.578323239527379 0.47831288087301499 88.035763862239065 2035.22888550468 -974.3233383383315 -0.028757154265845547 +-47270.020470001466 -18.683817303985521 -2315.2861428985716 95.768385025892087 -17.672451128396965 -22.79171671649058 -1141223.3347040769 -26770199.373178933 1908.1708402737481 115.86561258866116 2012.8913826067117 -933.82612614769698 -0.0090085907926641864 -451.07677290276899 -10581.114823023207 -0.042279669851482828 900.1031682182537 -15.255153128540949 -2071.9113410798432 93.069547905382166 -15.255153128540949 0.43399381302712331 93.055868728020528 2072.215911260098 -900.1031682182537 -0.0073554258093254336 +-59018.452679818394 30.073092056644295 -2187.9754917386226 112.33773372319536 28.209658218756466 -27.957580615735857 -1778592.8070566077 -51626265.313827813 1908.9219975856788 104.88006119026512 2032.066524237197 -781.48656074249607 0.014245898653076409 906.28918226772021 26306.37298721305 0.063358797502722899 774.56121050312481 32.379947527771819 -2108.7363575536419 97.836228123763888 32.379947527771819 0.36691672690815957 97.73420246464454 2110.937687398904 -774.56121050312481 0.015338677180375092 +-70604.436971834264 80.520294273323699 -2128.925402052897 123.39512549524636 76.510129996785395 -32.869849614447979 -2500058.8709964543 -85292011.330957636 1967.3360180363493 98.335439875263404 2071.4451877671463 -596.94257469573779 0.037486170518307126 2851.1731645643363 97270.626975923151 0.11655955582737126 602.79501879611894 83.246909237251884 -2145.5753515463525 102.30775908620346 83.246909237251884 0.28063082811737383 102.03142083077223 2151.3863511846494 -602.79501879611894 0.038755544337640543 +-82380.102814357102 133.20238622615736 -2173.6597002175804 129.28884413712871 131.42225463300832 -37.702564217097077 -3261548.4500676533 -126011538.70294885 2103.8796912677585 98.351938925762795 2139.2244304398973 -388.00315880513426 0.060962190492520538 5273.6767920799639 203751.1131187529 0.045859096349653725 392.63247258360985 135.98045853380739 -2182.4317087927047 106.44151132801497 135.98045853380739 0.17969449546160635 105.90956734948173 2193.3932435728575 -392.63247258360985 0.062233619466273406 +-94592.992745583106 189.37182164961118 -2341.5529288247112 128.60326892615379 197.95923410470118 -42.571103845896992 -4014546.0034072758 -170640873.76707619 2326.1769561351584 106.15937315684609 2239.4648817286375 -155.25247228199001 0.08522584232655768 8036.9789314748205 341616.98636669316 -0.20169932044110336 154.28475501846501 188.97237367168256 -2219.3085499387671 110.18594105363637 188.97237367168256 0.069435083266635916 109.33233816528242 2236.6356118142066 -154.28475501846501 0.085046072759533103 +-107198.13189376429 250.0266902622694 -2641.8160096472161 117.66690957803974 279.66804291625289 -47.453798979089989 -4709514.4096615268 -215033770.56922084 2626.3907290891843 122.52378808280966 2370.8931132247408 104.65032485644633 0.11068025244013698 10984.373326179506 501540.28806509398 -0.64982006814958948 -100.14481459739751 240.4372857474786 -2256.2086546322357 113.4819535263985 240.4372857474786 -0.044331480565470344 112.26534087864374 2280.6590501302244 100.14481459739751 0.10643527478861381 +-119741.45152135978 314.90179842037787 -3074.6122485540664 91.887692858898561 376.47912550814897 -52.152200139965061 -5299325.2507954482 -254592539.07720467 2981.4050219965116 147.69299721069967 2526.5222192585015 393.59680489831669 0.13715235122838756 13936.419098713928 669539.64062746358 -1.2859607968853257 -357.27265982169217 288.49932628397124 -2293.1343911111339 116.26564505791588 288.49932628397124 -0.15560655915578928 114.67634587555159 2324.914934736054 357.27265982169217 0.1256530166742035 +-131358.73439683038 381.64995726971671 -3624.2693295717481 48.561891907346038 483.17794976059668 -56.304643976352502 -5742492.9401203543 -284901182.6384787 3355.8461894168181 180.72696262136185 2694.2165921713563 706.15544100696468 0.16358763706374485 16684.251681336737 827752.52577868022 -2.0574808382637388 -603.13671954138715 331.29447064235012 -2330.0876529764769 118.47217685093136 331.29447064235012 -0.25852409753167044 116.53552868792475 2368.8102642143399 603.13671954138715 0.14200363079397776 +-140900.77989438578 445.4597229421671 -4246.1063838570553 -9.7716639883362983 589.00487449389925 -59.45180586261003 -6006072.7954721078 -302351967.97131234 3707.4807327330541 218.41377838508782 2858.189705697976 1024.8002159612695 0.18795768900513379 18988.280444202825 955889.83953442518 -2.8723684990534184 -823.95146411859082 367.08123194524813 -2367.0698047114392 120.04032221815156 367.08123194524813 -0.3476588456196586 117.81570193887522 2411.7653028781924 823.95146411859082 0.15488659575748867 +-147158.83581378291 499.42703357757421 -4854.8406439114651 -73.177458415820809 678.98220834614074 -61.137862822510563 -6068155.2915989244 -304683008.3056311 3994.120122175641 254.46870735152919 3001.3322656711989 1318.4069275061781 0.20748941984942842 20594.079722173665 1034031.9028626127 -3.6084368038305796 -1006.8755716555343 394.35076786068038 -2404.0816373500506 120.9170658551829 394.35076786068038 -0.41831141323453852 118.49253544548017 2453.2726603562642 1006.8755716555343 0.1638349679520899 +-149134.46993028419 535.96113700867386 -5329.9890032514413 -125.72736269558618 737.37684526826945 -61.020650544306136 -5919934.6305721365 -291357477.34578872 4180.8990168563751 280.22511934911643 3108.1820131230161 1546.5529494689476 0.21929670090371273 21275.127722663168 1047083.7822216832 -4.1353878459197873 -1140.7498930426016 411.92678114998006 -2441.1233355052318 121.06156836221872 411.92678114998006 -0.46675527538567985 118.54476307206338 2492.9504425448431 1140.7498930426016 0.16854614613337973 +-146281.33880149154 549.09764017677026 -5551.8061444999712 -151.64876207968649 752.76290808689396 -58.960636356909127 -5567253.0479158172 -263723961.15261441 4246.664006638799 287.66969771202207 3168.1163194330679 1670.5728678893997 0.22132109640337372 20897.850237930259 989943.11860573618 -4.3491261004688875 -1216.7638336687235 419.0460217461914 -2478.1944567426608 120.44787457600401 419.0460217461914 -0.49043282292169427 117.9543749504402 2530.5823139337012 1216.7638336687235 0.16890206438782401 +-138646.2299657921 536.80890355800955 -5458.0515869971232 -143.52013284222721 722.58847157100877 -55.062045260441664 -5031286.1567448014 -224901469.33554047 4188.199146540629 273.67457242370557 3177.9323089355171 1667.7756854748013 0.21318860347816104 19480.07678214648 870770.96284827951 -4.2067433228268909 -1229.0110946175773 415.41175632605558 -2515.2939240378014 119.06594227055103 415.41175632605558 -0.4880901884899036 116.7067937119288 2566.138882130404 1229.0110946175773 0.16497686907309594 +-126858.2046540118 501.92035111621527 -5081.7052397322886 -106.39472975282827 654.44229983432456 -49.65096072564063 -4347854.4682349488 -179357171.24082589 4021.2204603644795 242.17101078119771 3142.7748005432122 1540.7303478579499 0.19644632137620952 17202.487196240469 709634.93933980679 -3.7428378839041594 -1174.9007603343789 401.21508348662041 -2552.4200317832497 116.92086158845045 401.21508348662041 -0.45984374181384691 114.79103341113186 2599.7775295119636 1174.9007603343789 0.15703134383037981 +-111960.66304024243 450.91490900579953 -4535.3241410263763 -54.180026015603872 562.91377585798034 -43.194700247007106 -3565071.1692344151 -132216777.68672456 3777.6701204267802 202.08841953709603 3074.9189637378754 1315.0517701928252 0.17396408526458315 14358.112020975888 532495.20555465564 -3.0558485476601254 -1055.3967898212229 377.12208880144692 -2589.5704655278514 114.03045801391823 377.12208880144692 -0.40717468743102736 112.19983986560219 2631.8211023934382 1055.3967898212229 0.14549463302524959 +-95138.273174034868 391.37910674216459 -3955.1519404418173 -1.56911003488116 463.96782936333216 -36.188008054026199 -2739622.4766124561 -88438756.045980766 3499.7617845154282 162.80202166191097 2990.9364912300584 1027.2231014611943 0.14886995311607629 11270.23816951066 363818.68398375303 -2.2731963358749772 -875.06774538221828 344.22810906754592 -2626.7423353336339 110.4217465450586 344.22810906754592 -0.33285193814462466 108.92981119054951 2662.7189859349519 875.06774538221828 0.13093499774345604 +-77429.281965710266 329.62081878581745 -3450.0673123639394 42.0323194129464 370.0513945623768 -29.043241547528229 -1932388.4582222481 -52025174.715437613 3232.0410726552304 130.72415605694999 2908.3039545685915 710.63642298976401 0.12363871672386251 8226.2866145853968 221474.1019395226 -1.5156538545420983 -641.93864870355139 303.9833696772908 -2663.932222337643 106.12687164415961 303.9833696772908 -0.24078719006134711 104.98149737561734 2692.9963859939212 641.93864870355139 0.11402226919628311 +-44035.803112360532 199.37131914697 -2280.9512670018416 54.454036384652071 212.94244667599571 -22.017901556180266 -890880.98505958891 -18802550.418253027 2229.4311637910923 80.396751384278872 2103.0450518819284 286.42676406669551 0.099685659573485005 4033.4478910508792 85128.214209003228 -0.64722901009000289 -271.65939088268397 190.97049895750982 -1998.6209676727792 74.864372741713154 190.97049895750982 -0.13582969544134199 74.257845233508235 2014.9454192045573 271.65939088268397 0.095485249478754905 +-30958.143403170798 157.72485218599309 -2138.5535010186795 69.905304023678539 162.19951069540656 -15.197910359926754 -454092.6655726952 -6779911.6514513968 2132.4034039549197 72.05826158111222 2084.0899712369137 48.891553032951691 0.077429971618062393 2313.501091569377 34542.141275491922 -0.30017375692685672 -47.892776594533515 154.94954519808539 -2035.7734090836009 71.079508385049436 154.94954519808539 -0.023511426899623718 70.679748642268038 2047.2876019041705 47.892776594533515 0.076067523415849478 +-17705.126105102019 117.44074143153451 -2113.8986978941316 78.419787945582399 118.19213397704813 -8.5367049687087846 -149975.21410185914 -1275350.7352118047 2095.2657294338223 70.748240395111665 2087.9718693802533 -188.67279025240813 0.056625236948666592 994.80795764566381 8459.5915916897084 -0.087430484111452239 187.16610637465718 117.1499302731922 -2072.9313959480924 66.803472992832511 117.1499302731922 0.090244024288648583 66.568969442920803 2080.2337438008722 -187.16610637465718 0.056485019418125455 +-4082.3515924594344 77.65205884342366 -2195.2567963889715 80.138340197591702 77.64023622685184 -1.9338472726003952 -7899.2898126439241 -15281.895698897479 2110.2791396086704 75.604637597048864 2112.9779708265883 -426.69292704197295 0.036784490214790938 150.25558271014555 290.68311172456697 0.0055338684971525222 421.00415995655942 78.926617155967762 -2110.091288444868 62.049174848449042 78.926617155967762 0.19943351963835121 61.933467762400198 2114.0334625730738 -421.00415995655942 0.037388260140202634 +9999.5616493249727 37.636247837114283 -2338.2358327417137 73.418376039329416 37.56584087514802 4.6552894084380689 46739.190673254161 218024.94293180181 2139.3824868755855 84.961474619951488 2143.8919534952711 -658.7016328240137 0.017521530650425644 175.91648769958655 820.60004974397236 -0.016158006327833846 640.72900652479655 41.524400696744067 -2147.2492848417246 56.826802291384148 41.524400696744067 0.29829097138025912 56.784758049162562 2148.8391387416714 -640.72900652479655 0.019331657680048447 +24187.324994957609 -1.6871653169507044 -2477.4572950662532 57.410962344721227 -1.667845296927708 11.069713956502339 273034.28660306754 3052109.151896385 2138.040697560959 95.899214861117173 2163.1323314377742 -867.22814211800892 -0.00077215803979437266 -19.045263533322 -212.89715608075386 0.0017925950376717114 833.77797076792774 6.0120541828884191 -2184.4015002109281 51.144888584556803 6.0120541828884191 0.38159174863520723 51.137910362541362 2184.6995811949037 -833.77797076792774 0.0027515122118482466 +37882.402759527278 -37.923323014498273 -2556.3849299338385 34.5443610339751 -36.584863433412202 17.048786120399317 679885.05355052394 11892723.510968788 2087.0944676669642 104.87195727796554 2165.523100964705 -1031.2341243968394 -0.017067202076731895 -680.61945970524584 -11905.569931547165 0.078658540974413715 988.63176787764792 -26.767026103210402 -2221.5440491460668 45.011942049929196 -26.767026103210402 0.44492878842378392 45.011528564334164 2221.5644567281315 -988.63176787764792 -0.012046366383082988 +50434.980129253483 -68.429234443965754 -2518.1184926091555 10.006712724050384 -63.057693380137266 22.326241757084322 1246849.5881889744 29292954.949282877 1974.3812990399956 107.88210997498113 2148.0167647889775 -1129.7484728061613 -0.0302918257830747 -1691.7021195980863 -39744.131486597944 0.23387778311949015 1095.492519869227 -56.224762382352765 -2258.6731307291434 38.438340566442022 -56.224762382352765 0.48494578126127796 38.427705138480327 2259.2982514664945 -1095.492519869227 -0.024889226375543499 +61312.074570521625 -91.307469350126183 -2333.592782542758 -10.08441456609205 -78.205573993860128 26.703865231063428 1941085.3505299219 56439251.680180885 1800.3161343634397 102.15645323564566 2111.7476744598166 -1149.6175022187388 -0.039768061563643813 -2890.7126759449643 -84050.740070861226 0.45921736130070345 1146.8865652661207 -82.037530666765164 -2295.7851139071504 31.438176026269247 -82.037530666765164 0.49951505455841488 31.411952539312107 2297.7016929837168 -1146.8865652661207 -0.035730631823503993 +70248.145918297319 -106.26309512079989 -2022.3011365735213 -21.717361266057186 -81.924341472037241 30.110649772094863 2719758.4184633037 92862072.831391722 1578.7634300004129 87.663088830359044 2062.2288261699973 -1091.3196545105141 -0.045547833313673333 -4114.1291880201034 -140470.77185316273 0.72408636322556608 1138.1556621794343 -104.13468354041561 -2332.8766213877025 24.030766592265653 -104.13468354041561 0.48785069103276224 23.993111150887287 2336.5379014237583 -1138.1556621794343 -0.044635526592548484 diff --git a/tests/golden/stitch.ppm b/tests/golden/stitch.ppm Binary files differnew file mode 100644 index 0000000..0f0a43e --- /dev/null +++ b/tests/golden/stitch.ppm diff --git a/tests/golden/stitch_cyl_bic.txt b/tests/golden/stitch_cyl_bic.txt new file mode 100644 index 0000000..05cbec1 --- /dev/null +++ b/tests/golden/stitch_cyl_bic.txt @@ -0,0 +1,3072 @@ +0 0 1 8775 13072 10450 +1 0 1 9656 13072 11019 +2 0 1 10409 13072 11526 +3 0 1 11000 13072 11832 +4 0 1 11841 13072 12423 +5 0 1 12675 13072 12813 +6 0 1 13282 13072 13242 +7 0 1 13990 13072 13590 +8 0 1 14866 13072 14039 +9 0 1 15459 13072 14655 +10 0 1 16323 13072 14980 +11 0 1 17185 13072 15539 +12 0 1 17777 13072 15849 +13 0 1 18652 13072 16425 +14 0 1 19359 13072 16891 +15 0 1 19966 13072 17212 +16 0 1 20835 13072 17809 +17 0 1 21442 13072 18170 +18 0 1 22149 13072 18635 +19 0 1 23025 13072 18966 +20 0 1 23618 13072 19457 +21 0 1 24481 13072 20074 +22 0 1 25369 13072 20397 +23 0 1 26176 13072 20932 +24 0 1 26761 13072 21239 +25 0 1 27633 13072 21822 +26 0 1 28299 13072 22247 +27 0 1 28931 13072 22641 +28 0 1 29801 13072 23015 +29 0 1 30392 13072 23425 +30 0 1 31147 13072 24037 +31 0 1 32028 13072 24358 +32 0 1 32640 13072 24893 +33 0 1 33528 13072 25488 +34 0 1 34335 13072 25804 +35 0 1 34921 13072 26309 +36 0 1 35793 13072 26622 +37 0 1 36459 13072 27212 +38 0 1 37091 13072 27603 +39 0 1 37923 13072 28031 +40 0 1 38774 13072 28380 +41 0 1 39359 13072 28828 +42 0 1 40166 13072 29443 +43 0 1 41054 13072 29768 +44 0 1 41666 13072 30330 +45 0 1 42547 13072 30882 +46 0 1 43303 13072 31203 +47 0 1 43893 13072 31675 +48 0 1 44763 13072 32002 +49 0 1 45396 13072 32598 +50 0 1 46061 13072 32960 +51 0 1 46933 13072 33424 +52 0 1 47519 13072 33756 +53 0 1 48325 13072 34245 +54 0 1 49213 13072 34862 +55 0 1 49825 13072 35182 +56 0 1 50700 13072 35751 +57 0 1 51545 13072 36259 +58 0 1 52253 13072 36597 +59 0 1 52860 13072 37034 +60 0 1 53729 13072 37382 +61 0 1 54336 13072 37985 +62 0 1 55042 13072 38324 +63 0 1 55918 13072 38823 +0 1 1 8775 14229 11024 +1 1 1 9656 14229 11552 +2 1 1 10409 14229 11858 +3 1 1 11000 14229 12456 +4 1 1 11841 14229 12859 +5 1 1 12803 14287 13266 +6 1 1 12678 13956 13593 +7 1 1 13025 13779 14013 +8 1 1 13996 13801 14627 +9 1 1 14618 13801 14949 +10 1 1 15524 13801 15524 +11 1 1 16429 13801 15855 +12 1 1 17050 13801 16458 +13 1 1 17969 13801 16950 +14 1 1 18710 13801 17281 +15 1 1 19347 13801 17891 +16 1 1 20259 13801 18271 +17 1 1 20896 13801 18716 +18 1 1 21638 13801 19068 +19 1 1 22557 13801 19554 +20 1 1 23179 13801 20163 +21 1 1 24085 13801 20495 +22 1 1 25017 13801 21070 +23 1 1 25856 13790 21395 +24 1 1 26550 13918 21956 +25 1 1 27677 14292 22271 +26 1 1 28299 14229 22659 +27 1 1 28931 14229 23059 +28 1 1 29801 14229 23452 +29 1 1 30392 14229 24055 +30 1 1 31147 14229 24367 +31 1 1 32028 14229 24882 +32 1 1 32640 14229 25481 +33 1 1 33528 14229 25800 +34 1 1 34335 14229 26340 +35 1 1 34921 14229 26647 +36 1 1 35793 14229 27245 +37 1 1 36459 14229 27649 +38 1 1 37091 14229 28048 +39 1 1 37923 14229 28417 +40 1 1 38774 14229 28846 +41 1 1 39359 14229 29449 +42 1 1 40166 14229 29759 +43 1 1 41054 14229 30318 +44 1 1 41666 14229 30893 +45 1 1 42547 14229 31207 +46 1 1 43303 14229 31716 +47 1 1 43893 14229 32033 +48 1 1 44763 14229 32633 +49 1 1 45396 14229 33004 +50 1 1 46061 14229 33439 +51 1 1 46933 14229 33781 +52 1 1 47519 14229 34251 +53 1 1 48325 14229 34852 +54 1 1 49213 14229 35170 +55 1 1 49825 14229 35756 +56 1 1 50700 14229 36286 +57 1 1 51545 14229 36607 +58 1 1 52253 14229 37081 +59 1 1 52860 14229 37416 +60 1 1 53729 14229 38018 +61 1 1 54336 14229 38362 +62 1 1 55042 14229 38833 +63 1 1 55918 14229 39156 +0 2 1 8775 15406 11572 +1 2 1 9656 15406 11885 +2 2 1 10409 15406 12492 +3 2 1 11000 15406 12913 +4 2 1 11841 15406 13284 +5 2 1 7261 12943 13393 +6 2 1 39002 27001 15520 +7 2 1 55100 34540 16575 +8 2 1 51944 33569 16403 +9 2 1 51292 33569 16387 +10 2 1 50340 33569 16359 +11 2 1 49391 33569 16343 +12 2 1 48739 33569 16312 +13 2 1 47775 33569 16288 +14 2 1 46997 33569 16272 +15 2 1 46328 33569 16242 +16 2 1 45371 33569 16223 +17 2 1 44704 33569 16201 +18 2 1 43924 33569 16184 +19 2 1 42960 33569 16160 +20 2 1 42307 33569 16130 +21 2 1 41356 33569 16114 +22 2 1 40379 33569 16085 +23 2 1 39790 34063 15893 +24 2 1 35756 28604 17776 +25 2 1 25779 12733 23591 +26 2 1 28299 15406 23115 +27 2 1 28931 15406 23493 +28 2 1 29801 15406 24090 +29 2 1 30392 15406 24402 +30 2 1 31147 15406 24896 +31 2 1 32028 15406 25478 +32 2 1 32640 15406 25796 +33 2 1 33528 15406 26361 +34 2 1 34335 15406 26673 +35 2 1 34921 15406 27280 +36 2 1 35793 15406 27702 +37 2 1 36459 15406 28073 +38 2 1 37091 15406 28470 +39 2 1 37923 15406 28882 +40 2 1 38774 15406 29475 +41 2 1 39359 15406 29778 +42 2 1 40166 15406 30316 +43 2 1 41054 15406 30893 +44 2 1 41666 15406 31214 +45 2 1 42547 15406 31753 +46 2 1 43303 15406 32068 +47 2 1 43893 15406 32674 +48 2 1 44763 15406 33060 +49 2 1 45396 15406 33462 +50 2 1 46061 15406 33828 +51 2 1 46933 15406 34278 +52 2 1 47519 15406 34866 +53 2 1 48325 15406 35171 +54 2 1 49213 15406 35752 +55 2 1 49825 15406 36305 +56 2 1 50700 15406 36622 +57 2 1 51545 15406 37128 +58 2 1 52253 15406 37456 +59 2 1 52860 15406 38060 +60 2 1 53729 15406 38416 +61 2 1 54336 15406 38852 +62 2 1 55042 15406 39192 +63 2 1 55918 15406 39684 +0 3 1 8775 16551 11886 +1 3 1 9656 16551 12505 +2 3 1 10409 16551 12945 +3 3 1 11000 16551 13297 +4 3 1 11841 16551 13721 +5 3 1 7521 14369 13925 +6 3 1 37763 26822 15671 +7 3 1 53121 33500 16423 +8 3 1 50159 32640 16320 +9 3 1 49566 32640 16320 +10 3 1 48702 32640 16320 +11 3 1 47840 32640 16320 +12 3 1 47248 32640 16320 +13 3 1 46373 32640 16320 +14 3 1 45666 32640 16320 +15 3 1 45059 32640 16320 +16 3 1 44190 32640 16320 +17 3 1 43583 32640 16320 +18 3 1 42876 32640 16320 +19 3 1 42000 32640 16320 +20 3 1 41407 32640 16320 +21 3 1 40544 32640 16320 +22 3 1 39656 32640 16320 +23 3 1 39134 33078 16137 +24 3 1 35323 28242 18140 +25 3 1 25869 14183 24069 +26 3 1 28299 16551 23523 +27 3 1 28931 16551 24117 +28 3 1 29801 16551 24433 +29 3 1 30392 16551 24909 +30 3 1 31147 16551 25477 +31 3 1 32028 16551 25790 +32 3 1 32640 16551 26345 +33 3 1 33528 16551 26675 +34 3 1 34335 16551 27294 +35 3 1 34921 16551 27734 +36 3 1 35793 16551 28086 +37 3 1 36459 16551 28511 +38 3 1 37091 16551 28910 +39 3 1 37923 16551 29496 +40 3 1 38774 16551 29800 +41 3 1 39359 16551 30316 +42 3 1 40166 16551 30876 +43 3 1 41054 16551 31203 +44 3 1 41666 16551 31757 +45 3 1 42547 16551 32082 +46 3 1 43303 16551 32696 +47 3 1 43893 16551 33100 +48 3 1 44763 16551 33475 +49 3 1 45396 16551 33867 +50 3 1 46061 16551 34301 +51 3 1 46933 16551 34877 +52 3 1 47519 16551 35178 +53 3 1 48325 16551 35736 +54 3 1 49213 16551 36289 +55 3 1 49825 16551 36621 +56 3 1 50700 16551 37148 +57 3 1 51545 16551 37479 +58 3 1 52253 16551 38087 +59 3 1 52860 16551 38460 +60 3 1 53729 16551 38863 +61 3 1 54336 16551 39225 +62 3 1 55042 16551 39698 +63 3 1 55918 16551 40266 +0 4 1 8775 17666 12495 +1 4 1 9656 17666 12950 +2 4 1 10409 17666 13301 +3 4 1 11000 17666 13746 +4 4 1 11841 17666 14139 +5 4 1 7521 15635 14565 +6 4 1 37763 27225 15842 +7 4 1 53121 33441 16396 +8 4 1 50159 32640 16320 +9 4 1 49566 32640 16320 +10 4 1 48702 32640 16320 +11 4 1 47840 32640 16320 +12 4 1 47248 32640 16320 +13 4 1 46373 32640 16320 +14 4 1 45666 32640 16320 +15 4 1 45059 32640 16320 +16 4 1 44190 32640 16320 +17 4 1 43583 32640 16320 +18 4 1 42876 32640 16320 +19 4 1 42000 32640 16320 +20 4 1 41407 32640 16320 +21 4 1 40544 32640 16320 +22 4 1 39656 32640 16320 +23 4 1 39134 33048 16129 +24 4 1 35323 28547 18207 +25 4 1 25869 15462 24531 +26 4 1 28299 17666 24133 +27 4 1 28931 17666 24457 +28 4 1 29801 17666 24920 +29 4 1 30392 17666 25474 +30 4 1 31147 17666 25795 +31 4 1 32028 17666 26309 +32 4 1 32640 17666 26649 +33 4 1 33528 17666 27283 +34 4 1 34335 17666 27739 +35 4 1 34921 17666 28090 +36 4 1 35793 17666 28536 +37 4 1 36459 17666 28928 +38 4 1 37091 17666 29509 +39 4 1 37923 17666 29818 +40 4 1 38774 17666 30318 +41 4 1 39359 17666 30860 +42 4 1 40166 17666 31190 +43 4 1 41054 17666 31725 +44 4 1 41666 17666 32071 +45 4 1 42547 17666 32698 +46 4 1 43303 17666 33117 +47 4 1 43893 17666 33481 +48 4 1 44763 17666 33894 +49 4 1 45396 17666 34317 +50 4 1 46061 17666 34885 +51 4 1 46933 17666 35187 +52 4 1 47519 17666 35725 +53 4 1 48325 17666 36257 +54 4 1 49213 17666 36602 +55 4 1 49825 17666 37137 +56 4 1 50700 17666 37480 +57 4 1 51545 17666 38097 +58 4 1 52253 17666 38485 +59 4 1 52860 17666 38867 +60 4 1 53729 17666 39251 +61 4 1 54336 17666 39709 +62 4 1 55042 17666 40264 +63 4 1 55918 17666 40567 +0 5 1 8775 18758 12928 +1 5 1 9656 18758 13296 +2 5 1 10409 18758 13751 +3 5 1 11000 18758 14146 +4 5 1 11841 18758 14726 +5 5 1 7521 16874 14979 +6 5 1 37763 27620 15949 +7 5 1 53121 33382 16379 +8 5 1 50159 32640 16320 +9 5 1 49566 32640 16320 +10 5 1 48702 32640 16320 +11 5 1 47840 32640 16320 +12 5 1 47248 32640 16320 +13 5 1 46373 32640 16320 +14 5 1 45666 32640 16320 +15 5 1 45059 32640 16320 +16 5 1 44190 32640 16320 +17 5 1 43583 32640 16320 +18 5 1 42876 32640 16320 +19 5 1 42000 32640 16320 +20 5 1 41407 32640 16320 +21 5 1 40544 32640 16320 +22 5 1 39656 32640 16320 +23 5 1 39134 33018 16113 +24 5 1 35323 28845 18378 +25 5 1 25869 16714 25177 +26 5 1 28299 18758 24472 +27 5 1 28931 18758 24926 +28 5 1 29801 18758 25471 +29 5 1 30392 18758 25805 +30 5 1 31147 18758 26276 +31 5 1 32028 18758 26615 +32 5 1 32640 18758 27246 +33 5 1 33528 18758 27716 +34 5 1 34335 18758 28085 +35 5 1 34921 18758 28541 +36 5 1 35793 18758 28935 +37 5 1 36459 18758 29517 +38 5 1 37091 18758 29832 +39 5 1 37923 18758 30319 +40 5 1 38774 18758 30847 +41 5 1 39359 18758 31187 +42 5 1 40166 18758 31679 +43 5 1 41054 18758 32035 +44 5 1 41666 18758 32678 +45 5 1 42547 18758 33110 +46 5 1 43303 18758 33480 +47 5 1 43893 18758 33907 +48 5 1 44763 18758 34325 +49 5 1 45396 18758 34890 +50 5 1 46061 18758 35195 +51 5 1 46933 18758 35717 +52 5 1 47519 18758 36231 +53 5 1 48325 18758 36583 +54 5 1 49213 18758 37094 +55 5 1 49825 18758 37460 +56 5 1 50700 18758 38090 +57 5 1 51545 18758 38490 +58 5 1 52253 18758 38868 +59 5 1 52860 18758 39267 +60 5 1 53729 18758 39715 +61 5 1 54336 18758 40263 +62 5 1 55042 18758 40566 +63 5 1 55918 18758 41124 +0 6 1 8775 19809 13281 +1 6 1 9656 19809 13736 +2 6 1 10409 19809 14141 +3 6 1 11000 19809 14726 +4 6 1 11841 19809 15077 +5 6 1 7521 18068 15442 +6 6 1 37763 28000 16124 +7 6 1 53121 33326 16354 +8 6 1 50159 32640 16320 +9 6 1 49566 32640 16320 +10 6 1 48702 32640 16320 +11 6 1 47840 32640 16320 +12 6 1 47248 32640 16320 +13 6 1 46373 32640 16320 +14 6 1 45666 32640 16320 +15 6 1 45059 32640 16320 +16 6 1 44190 32640 16320 +17 6 1 43583 32640 16320 +18 6 1 42876 32640 16320 +19 6 1 42000 32640 16320 +20 6 1 41407 32640 16320 +21 6 1 40544 32640 16320 +22 6 1 39656 32640 16320 +23 6 1 39134 32989 16101 +24 6 1 35323 29133 18498 +25 6 1 25869 17920 25601 +26 6 1 28299 19809 24929 +27 6 1 28931 19809 25469 +28 6 1 29801 19809 25814 +29 6 1 30392 19809 26249 +30 6 1 31147 19809 26594 +31 6 1 32028 19809 27202 +32 6 1 32640 19809 27666 +33 6 1 33528 19809 28070 +34 6 1 34335 19809 28525 +35 6 1 34921 19809 28930 +36 6 1 35793 19809 29518 +37 6 1 36459 19809 29839 +38 6 1 37091 19809 30319 +39 6 1 37923 19809 30838 +40 6 1 38774 19809 31190 +41 6 1 39359 19809 31640 +42 6 1 40166 19809 31995 +43 6 1 41054 19809 32634 +44 6 1 41666 19809 33078 +45 6 1 42547 19809 33473 +46 6 1 43303 19809 33903 +47 6 1 43893 19809 34326 +48 6 1 44763 19809 34892 +49 6 1 45396 19809 35201 +50 6 1 46061 19809 35712 +51 6 1 46933 19809 36212 +52 6 1 47519 19809 36574 +53 6 1 48325 19809 37040 +54 6 1 49213 19809 37418 +55 6 1 49825 19809 38064 +56 6 1 50700 19809 38473 +57 6 1 51545 19809 38865 +58 6 1 52253 19809 39271 +59 6 1 52860 19809 39717 +60 6 1 53729 19809 40262 +61 6 1 54336 19809 40567 +62 6 1 55042 19809 41110 +63 6 1 55918 19809 41594 +0 7 1 8775 21020 13700 +1 7 1 9656 21020 14124 +2 7 1 10409 21020 14720 +3 7 1 11000 21020 15074 +4 7 1 11841 21020 15481 +5 7 1 7521 19444 15875 +6 7 1 37763 28438 16242 +7 7 1 53121 33261 16336 +8 7 1 50159 32640 16320 +9 7 1 49566 32640 16320 +10 7 1 48702 32640 16320 +11 7 1 47840 32640 16320 +12 7 1 47248 32640 16320 +13 7 1 46373 32640 16320 +14 7 1 45666 32640 16320 +15 7 1 45059 32640 16320 +16 7 1 44190 32640 16320 +17 7 1 43583 32640 16320 +18 7 1 42876 32640 16320 +19 7 1 42000 32640 16320 +20 7 1 41407 32640 16320 +21 7 1 40544 32640 16320 +22 7 1 39656 32640 16320 +23 7 1 39134 32956 16092 +24 7 1 35323 29464 18578 +25 7 1 25869 19309 26108 +26 7 1 28299 21020 25468 +27 7 1 28931 21020 25819 +28 7 1 29801 21020 26231 +29 7 1 30392 21020 26581 +30 7 1 31147 21020 27171 +31 7 1 32028 21020 27611 +32 7 1 32640 21020 28041 +33 7 1 33528 21020 28489 +34 7 1 34335 21020 28912 +35 7 1 34921 21020 29511 +36 7 1 35793 21020 29837 +37 7 1 36459 21020 30318 +38 7 1 37091 21020 30834 +39 7 1 37923 21020 31193 +40 7 1 38774 21020 31611 +41 7 1 39359 21020 31969 +42 7 1 40166 21020 32588 +43 7 1 41054 21020 33022 +44 7 1 41666 21020 33457 +45 7 1 42547 21020 33882 +46 7 1 43303 21020 34317 +47 7 1 43893 21020 34890 +48 7 1 44763 21020 35202 +49 7 1 45396 21020 35709 +50 7 1 46061 21020 36200 +51 7 1 46933 21020 36572 +52 7 1 47519 21020 36999 +53 7 1 48325 21020 37373 +54 7 1 49213 21020 38018 +55 7 1 49825 21020 38434 +56 7 1 50700 21020 38858 +57 7 1 51545 21020 39261 +58 7 1 52253 21020 39714 +59 7 1 52860 21020 40261 +60 7 1 53729 21020 40567 +61 7 1 54336 21020 41101 +62 7 1 55042 21020 41571 +63 7 1 55918 21020 41959 +0 8 1 8775 22245 14093 +1 8 1 9656 22245 14707 +2 8 1 10409 22245 15058 +3 8 1 11000 22245 15484 +4 8 1 11841 22245 15836 +5 8 1 7521 20835 16451 +6 8 1 37763 28881 16479 +7 8 1 53121 33196 16304 +8 8 1 50159 32640 16320 +9 8 1 49566 32640 16320 +10 8 1 48702 32640 16320 +11 8 1 47840 32640 16320 +12 8 1 47248 32640 16320 +13 8 1 46373 32640 16320 +14 8 1 45666 32640 16320 +15 8 1 45059 32640 16320 +16 8 1 44190 32640 16320 +17 8 1 43583 32640 16320 +18 8 1 42876 32640 16320 +19 8 1 42000 32640 16320 +20 8 1 41407 32640 16320 +21 8 1 40544 32640 16320 +22 8 1 39656 32640 16320 +23 8 1 39134 32923 16076 +24 8 1 35323 29798 18756 +25 8 1 25869 20714 26704 +26 8 1 28299 22245 25817 +27 8 1 28931 22245 26225 +28 8 1 29801 22245 26575 +29 8 1 30392 22245 27153 +30 8 1 31147 22245 27574 +31 8 1 32028 22245 28013 +32 8 1 32640 22245 28433 +33 8 1 33528 22245 28881 +34 8 1 34335 22245 29496 +35 8 1 34921 22245 29826 +36 8 1 35793 22245 30314 +37 8 1 36459 22245 30834 +38 8 1 37091 22245 31193 +39 8 1 37923 22245 31597 +40 8 1 38774 22245 31954 +41 8 1 39359 22245 32557 +42 8 1 40166 22245 32966 +43 8 1 41054 22245 33428 +44 8 1 41666 22245 33844 +45 8 1 42547 22245 34298 +46 8 1 43303 22245 34884 +47 8 1 43893 22245 35198 +48 8 1 44763 22245 35706 +49 8 1 45396 22245 36197 +50 8 1 46061 22245 36571 +51 8 1 46933 22245 36973 +52 8 1 47519 22245 37346 +53 8 1 48325 22245 37971 +54 8 1 49213 22245 38376 +55 8 1 49825 22245 38843 +56 8 1 50700 22245 39238 +57 8 1 51545 22245 39705 +58 8 1 52253 22245 40260 +59 8 1 52860 22245 40566 +60 8 1 53729 22245 41097 +61 8 1 54336 22245 41560 +62 8 1 55042 22245 41954 +63 8 1 55918 22245 42357 +0 9 1 8775 23287 14683 +1 9 1 9656 23287 15030 +2 9 1 10409 23287 15490 +3 9 1 11000 23287 15832 +4 9 1 11841 23287 16364 +5 9 1 7521 22019 16973 +6 9 1 37763 29258 16577 +7 9 1 53121 33140 16286 +8 9 1 50159 32640 16320 +9 9 1 49566 32640 16320 +10 9 1 48702 32640 16320 +11 9 1 47840 32640 16320 +12 9 1 47248 32640 16320 +13 9 1 46373 32640 16320 +14 9 1 45666 32640 16320 +15 9 1 45059 32640 16320 +16 9 1 44190 32640 16320 +17 9 1 43583 32640 16320 +18 9 1 42876 32640 16320 +19 9 1 42000 32640 16320 +20 9 1 41407 32640 16320 +21 9 1 40544 32640 16320 +22 9 1 39656 32640 16320 +23 9 1 39134 32895 16066 +24 9 1 35323 30083 18841 +25 9 1 25869 21910 27133 +26 9 1 28299 23287 26231 +27 9 1 28931 23287 26574 +28 9 1 29801 23287 27146 +29 9 1 30392 23287 27555 +30 9 1 31147 23287 27997 +31 9 1 32028 23287 28379 +32 9 1 32640 23287 28836 +33 9 1 33528 23287 29471 +34 9 1 34335 23287 29805 +35 9 1 34921 23287 30307 +36 9 1 35793 23287 30837 +37 9 1 36459 23287 31189 +38 9 1 37091 23287 31597 +39 9 1 37923 23287 31949 +40 9 1 38774 23287 32541 +41 9 1 39359 23287 32932 +42 9 1 40166 23287 33401 +43 9 1 41054 23287 33790 +44 9 1 41666 23287 34269 +45 9 1 42547 23287 34873 +46 9 1 43303 23287 35188 +47 9 1 43893 23287 35705 +48 9 1 44763 23287 36201 +49 9 1 45396 23287 36569 +50 9 1 46061 23287 36964 +51 9 1 46933 23287 37332 +52 9 1 47519 23287 37943 +53 9 1 48325 23287 38323 +54 9 1 49213 23287 38817 +55 9 1 49825 23287 39202 +56 9 1 50700 23287 39688 +57 9 1 51545 23287 40257 +58 9 1 52253 23287 40563 +59 9 1 52860 23287 41098 +60 9 1 53729 23287 41561 +61 9 1 54336 23287 41951 +62 9 1 55042 23287 42337 +63 9 1 55918 23287 42726 +0 10 1 8775 24371 14994 +1 10 1 9656 24371 15497 +2 10 1 10409 24371 15826 +3 10 1 11000 24371 16368 +4 10 1 11841 24371 16842 +5 10 1 7521 23249 17384 +6 10 1 37763 29650 16746 +7 10 1 53121 33082 16262 +8 10 1 50159 32640 16320 +9 10 1 49566 32640 16320 +10 10 1 48702 32640 16320 +11 10 1 47840 32640 16320 +12 10 1 47248 32640 16320 +13 10 1 46373 32640 16320 +14 10 1 45666 32640 16320 +15 10 1 45059 32640 16320 +16 10 1 44190 32640 16320 +17 10 1 43583 32640 16320 +18 10 1 42876 32640 16320 +19 10 1 42000 32640 16320 +20 10 1 41407 32640 16320 +21 10 1 40544 32640 16320 +22 10 1 39656 32640 16320 +23 10 1 39134 32865 16053 +24 10 1 35323 30380 18979 +25 10 1 25869 23154 27620 +26 10 1 28299 24371 26578 +27 10 1 28931 24371 27150 +28 10 1 29801 24371 27555 +29 10 1 30392 24371 27992 +30 10 1 31147 24371 28352 +31 10 1 32028 24371 28796 +32 10 1 32640 24371 29434 +33 10 1 33528 24371 29777 +34 10 1 34335 24371 30296 +35 10 1 34921 24371 30843 +36 10 1 35793 24371 31182 +37 10 1 36459 24371 31612 +38 10 1 37091 24371 31953 +39 10 1 37923 24371 32540 +40 10 1 38774 24371 32921 +41 10 1 39359 24371 33388 +42 10 1 40166 24371 33744 +43 10 1 41054 24371 34229 +44 10 1 41666 24371 34854 +45 10 1 42547 24371 35173 +46 10 1 43303 24371 35703 +47 10 1 43893 24371 36212 +48 10 1 44763 24371 36567 +49 10 1 45396 24371 36974 +50 10 1 46061 24371 37331 +51 10 1 46933 24371 37933 +52 10 1 47519 24371 38297 +53 10 1 48325 24371 38793 +54 10 1 49213 24371 39154 +55 10 1 49825 24371 39664 +56 10 1 50700 24371 40252 +57 10 1 51545 24371 40558 +58 10 1 52253 24371 41102 +59 10 1 52860 24371 41573 +60 10 1 53729 24371 41951 +61 10 1 54336 24371 42338 +62 10 1 55042 24371 42717 +63 10 1 55918 24371 43333 +0 11 1 8775 25476 15499 +1 11 1 9656 25476 15819 +2 11 1 10409 25476 16376 +3 11 1 11000 25476 16862 +4 11 1 11841 25476 17175 +5 11 1 7521 24505 18027 +6 11 1 37763 30050 16972 +7 11 1 53121 33023 16230 +8 11 1 50159 32640 16320 +9 11 1 49566 32640 16320 +10 11 1 48702 32640 16320 +11 11 1 47840 32640 16320 +12 11 1 47248 32640 16320 +13 11 1 46373 32640 16320 +14 11 1 45666 32640 16320 +15 11 1 45059 32640 16320 +16 11 1 44190 32640 16320 +17 11 1 43583 32640 16320 +18 11 1 42876 32640 16320 +19 11 1 42000 32640 16320 +20 11 1 41407 32640 16320 +21 11 1 40544 32640 16320 +22 11 1 39656 32640 16320 +23 11 1 39134 32835 16045 +24 11 1 35323 30682 19058 +25 11 1 25869 24422 28035 +26 11 1 28299 25476 27164 +27 11 1 28931 25476 27574 +28 11 1 29801 25476 27994 +29 11 1 30392 25476 28351 +30 11 1 31147 25476 28782 +31 11 1 32028 25476 29405 +32 11 1 32640 25476 29741 +33 11 1 33528 25476 30281 +34 11 1 34335 25476 30849 +35 11 1 34921 25476 31173 +36 11 1 35793 25476 31639 +37 11 1 36459 25476 31965 +38 11 1 37091 25476 32552 +39 11 1 37923 25476 32932 +40 11 1 38774 25476 33386 +41 11 1 39359 25476 33728 +42 11 1 40166 25476 34198 +43 11 1 41054 25476 34826 +44 11 1 41666 25476 35154 +45 11 1 42547 25476 35701 +46 11 1 43303 25476 36227 +47 11 1 43893 25476 36565 +48 11 1 44763 25476 37000 +49 11 1 45396 25476 37343 +50 11 1 46061 25476 37941 +51 11 1 46933 25476 38297 +52 11 1 47519 25476 38784 +53 11 1 48325 25476 39120 +54 11 1 49213 25476 39632 +55 11 1 49825 25476 40243 +56 11 1 50700 25476 40552 +57 11 1 51545 25476 41109 +58 11 1 52253 25476 41594 +59 11 1 52860 25476 41953 +60 11 1 53729 25476 42359 +61 11 1 54336 25476 42724 +62 11 1 55042 25476 43332 +63 11 1 55918 25476 43672 +0 12 1 8775 26612 15813 +1 12 1 9656 26612 16387 +2 12 1 10409 26612 16889 +3 12 1 11000 26612 17197 +4 12 1 11841 26612 17790 +5 12 1 7521 25794 18513 +6 12 1 37763 30460 17058 +7 12 1 53121 32962 16214 +8 12 1 50159 32640 16320 +9 12 1 49566 32640 16320 +10 12 1 48702 32640 16320 +11 12 1 47840 32640 16320 +12 12 1 47248 32640 16320 +13 12 1 46373 32640 16320 +14 12 1 45666 32640 16320 +15 12 1 45059 32640 16320 +16 12 1 44190 32640 16320 +17 12 1 43583 32640 16320 +18 12 1 42876 32640 16320 +19 12 1 42000 32640 16320 +20 12 1 41407 32640 16320 +21 12 1 40544 32640 16320 +22 12 1 39656 32640 16320 +23 12 1 39134 32804 16031 +24 12 1 35323 30992 19196 +25 12 1 25869 25724 28683 +26 12 1 28299 26612 27611 +27 12 1 28931 26612 28006 +28 12 1 29801 26612 28378 +29 12 1 30392 26612 28792 +30 12 1 31147 26612 29403 +31 12 1 32028 26612 29721 +32 12 1 32640 26612 30261 +33 12 1 33528 26612 30852 +34 12 1 34335 26612 31168 +35 12 1 34921 26612 31673 +36 12 1 35793 26612 31987 +37 12 1 36459 26612 32579 +38 12 1 37091 26612 32966 +39 12 1 37923 26612 33396 +40 12 1 38774 26612 33743 +41 12 1 39359 26612 34195 +42 12 1 40166 26612 34808 +43 12 1 41054 26612 35131 +44 12 1 41666 26612 35698 +45 12 1 42547 26612 36246 +46 12 1 43303 26612 36567 +47 12 1 43893 26612 37039 +48 12 1 44763 26612 37367 +49 12 1 45396 26612 37965 +50 12 1 46061 26612 38323 +51 12 1 46933 26612 38789 +52 12 1 47519 26612 39119 +53 12 1 48325 26612 39613 +54 12 1 49213 26612 40226 +55 12 1 49825 26612 40545 +56 12 1 50700 26612 41119 +57 12 1 51545 26612 41622 +58 12 1 52253 26612 41960 +59 12 1 52860 26612 42398 +60 12 1 53729 26612 42748 +61 12 1 54336 26612 43351 +62 12 1 55042 26612 43687 +63 12 1 55918 26612 44188 +0 13 1 8775 27786 16400 +1 13 1 9656 27786 16922 +2 13 1 10409 27786 17230 +3 13 1 11000 27786 17831 +4 13 1 11841 27786 18229 +5 13 1 7521 27127 19010 +6 13 1 37763 30885 17283 +7 13 1 53121 32900 16184 +8 13 1 50159 32640 16320 +9 13 1 49566 32640 16320 +10 13 1 48702 32640 16320 +11 13 1 47840 32640 16320 +12 13 1 47248 32640 16320 +13 13 1 46373 32640 16320 +14 13 1 45666 32640 16320 +15 13 1 45059 32640 16320 +16 13 1 44190 32640 16320 +17 13 1 43583 32640 16320 +18 13 1 42876 32640 16320 +19 13 1 42000 32640 16320 +20 13 1 41407 32640 16320 +21 13 1 40544 32640 16320 +22 13 1 39656 32640 16320 +23 13 1 39134 32772 16015 +24 13 1 35323 31313 19372 +25 13 1 25869 27071 29238 +26 13 1 28299 27786 28029 +27 13 1 28931 27786 28429 +28 13 1 29801 27786 28827 +29 13 1 30392 27786 29428 +30 13 1 31147 27786 29737 +31 13 1 32028 27786 30258 +32 13 1 32640 27786 30852 +33 13 1 33528 27786 31170 +34 13 1 34335 27786 31711 +35 13 1 34921 27786 32019 +36 13 1 35793 27786 32620 +37 13 1 36459 27786 33019 +38 13 1 37091 27786 33419 +39 13 1 37923 27786 33787 +40 13 1 38774 27786 34221 +41 13 1 39359 27786 34821 +42 13 1 40166 27786 35129 +43 13 1 41054 27786 35695 +44 13 1 41666 27786 36264 +45 13 1 42547 27786 36578 +46 13 1 43303 27786 37087 +47 13 1 43893 27786 37406 +48 13 1 44763 27786 38007 +49 13 1 45396 27786 38374 +50 13 1 46061 27786 38809 +51 13 1 46933 27786 39151 +52 13 1 47519 27786 39627 +53 13 1 48325 27786 40224 +54 13 1 49213 27786 40540 +55 13 1 49825 27786 41132 +56 13 1 50700 27786 41656 +57 13 1 51545 27786 41978 +58 13 1 52253 27786 42451 +59 13 1 52860 27786 42789 +60 13 1 53729 27786 43391 +61 13 1 54336 27786 43732 +62 13 1 55042 27786 44203 +63 13 1 55918 27786 44526 +0 14 1 8775 28965 16941 +1 14 1 9656 28965 17258 +2 14 1 10409 28965 17868 +3 14 1 11000 28965 18283 +4 14 1 11841 28965 18655 +5 14 1 7521 28466 19510 +6 14 1 37763 31311 17377 +7 14 1 53121 32836 16167 +8 14 1 50159 32640 16320 +9 14 1 49566 32640 16320 +10 14 1 48702 32640 16320 +11 14 1 47840 32640 16320 +12 14 1 47248 32640 16320 +13 14 1 46373 32640 16320 +14 14 1 45666 32640 16320 +15 14 1 45059 32640 16320 +16 14 1 44190 32640 16320 +17 14 1 43583 32640 16320 +18 14 1 42876 32640 16320 +19 14 1 42000 32640 16320 +20 14 1 41407 32640 16320 +21 14 1 40544 32640 16320 +22 14 1 39656 32640 16320 +23 14 1 39134 32740 16006 +24 14 1 35323 31635 19445 +25 14 1 25869 28424 29714 +26 14 1 28299 28965 28485 +27 14 1 28931 28965 28869 +28 14 1 29801 28965 29463 +29 14 1 30392 28965 29772 +30 14 1 31147 28965 30273 +31 14 1 32028 28965 30849 +32 14 1 32640 28965 31169 +33 14 1 33528 28965 31731 +34 14 1 34335 28965 32047 +35 14 1 34921 28965 32657 +36 14 1 35793 28965 33072 +37 14 1 36459 28965 33444 +38 14 1 37091 28965 33840 +39 14 1 37923 28965 34259 +40 14 1 38774 28965 34847 +41 14 1 39359 28965 35149 +42 14 1 40166 28965 35693 +43 14 1 41054 28965 36263 +44 14 1 41666 28965 36587 +45 14 1 42547 28965 37123 +46 14 1 43303 28965 37442 +47 14 1 43893 28965 38049 +48 14 1 44763 28965 38431 +49 14 1 45396 28965 38833 +50 14 1 46061 28965 39198 +51 14 1 46933 28965 39655 +52 14 1 47519 28965 40237 +53 14 1 48325 28965 40542 +54 14 1 49213 28965 41130 +55 14 1 49825 28965 41674 +56 14 1 50700 28965 41994 +57 14 1 51545 28965 42498 +58 14 1 52253 28965 42831 +59 14 1 52860 28965 43435 +60 14 1 53729 28965 43787 +61 14 1 54336 28965 44223 +62 14 1 55042 28965 44562 +63 14 1 55918 28965 45061 +0 15 1 8775 30105 17258 +1 15 1 9656 30105 17880 +2 15 1 10409 30105 18312 +3 15 1 11000 30105 18668 +4 15 1 11841 30105 19088 +5 15 1 7521 29761 20028 +6 15 1 37763 31723 17615 +7 15 1 53121 32776 16135 +8 15 1 50159 32640 16320 +9 15 1 49566 32640 16320 +10 15 1 48702 32640 16320 +11 15 1 47840 32640 16320 +12 15 1 47248 32640 16320 +13 15 1 46373 32640 16320 +14 15 1 45666 32640 16320 +15 15 1 45059 32640 16320 +16 15 1 44190 32640 16320 +17 15 1 43583 32640 16320 +18 15 1 42876 32640 16320 +19 15 1 42000 32640 16320 +20 15 1 41407 32640 16320 +21 15 1 40544 32640 16320 +22 15 1 39656 32640 16320 +23 15 1 39134 32709 15991 +24 15 1 35323 31947 19607 +25 15 1 25869 29732 30226 +26 15 1 28299 30105 28898 +27 15 1 28931 30105 29488 +28 15 1 29801 30105 29801 +29 15 1 30392 30105 30286 +30 15 1 31147 30105 30844 +31 15 1 32028 30105 31163 +32 15 1 32640 30105 31711 +33 15 1 33528 30105 32046 +34 15 1 34335 30105 32669 +35 15 1 34921 30105 33101 +36 15 1 35793 30105 33457 +37 15 1 36459 30105 33878 +38 15 1 37091 30105 34286 +39 15 1 37923 30105 34865 +40 15 1 38774 30105 35168 +41 15 1 39359 30105 35693 +42 15 1 40166 30105 36243 +43 15 1 41054 30105 36576 +44 15 1 41666 30105 37123 +45 15 1 42547 30105 37454 +46 15 1 43303 30105 38070 +47 15 1 43893 30105 38468 +48 15 1 44763 30105 38845 +49 15 1 45396 30105 39234 +50 15 1 46061 30105 39677 +51 15 1 46933 30105 40246 +52 15 1 47519 30105 40547 +53 15 1 48325 30105 41113 +54 15 1 49213 30105 41655 +55 15 1 49825 30105 41993 +56 15 1 50700 30105 42515 +57 15 1 51545 30105 42852 +58 15 1 52253 30105 43460 +59 15 1 52860 30105 43828 +60 15 1 53729 30105 44232 +61 15 1 54336 30105 44593 +62 15 1 55042 30105 45074 +63 15 1 55918 30105 45633 +0 16 1 8775 31215 17868 +1 16 1 9656 31215 18313 +2 16 1 10409 31215 18672 +3 16 1 11000 31215 19109 +4 16 1 11841 31215 19513 +5 16 1 7521 31021 20660 +6 16 1 37763 32125 17780 +7 16 1 53121 32716 16109 +8 16 1 50159 32640 16320 +9 16 1 49566 32640 16320 +10 16 1 48702 32640 16320 +11 16 1 47840 32640 16320 +12 16 1 47248 32640 16320 +13 16 1 46373 32640 16320 +14 16 1 45666 32640 16320 +15 16 1 45059 32640 16320 +16 16 1 44190 32640 16320 +17 16 1 43583 32640 16320 +18 16 1 42876 32640 16320 +19 16 1 42000 32640 16320 +20 16 1 41407 32640 16320 +21 16 1 40544 32640 16320 +22 16 1 39656 32640 16320 +23 16 1 39134 32679 15983 +24 16 1 35323 32250 19675 +25 16 1 25869 31005 30694 +26 16 1 28299 31215 29502 +27 16 1 28931 31215 29822 +28 16 1 29801 31215 30295 +29 16 1 30392 31215 30838 +30 16 1 31147 31215 31167 +31 16 1 32028 31215 31672 +32 16 1 32640 31215 32018 +33 16 1 33528 31215 32656 +34 16 1 34335 31215 33102 +35 16 1 34921 31215 33461 +36 16 1 35793 31215 33899 +37 16 1 36459 31215 34302 +38 16 1 37091 31215 34877 +39 16 1 37923 31215 35184 +40 16 1 38774 31215 35693 +41 16 1 39359 31215 36224 +42 16 1 40166 31215 36562 +43 16 1 41054 31215 37088 +44 16 1 41666 31215 37442 +45 16 1 42547 31215 38070 +46 16 1 43303 31215 38481 +47 16 1 43893 31215 38851 +48 16 1 44763 31215 39258 +49 16 1 45396 31215 39692 +50 16 1 46061 31215 40251 +51 16 1 46933 31215 40553 +52 16 1 47519 31215 41100 +53 16 1 48325 31215 41620 +54 16 1 49213 31215 41975 +55 16 1 49825 31215 42499 +56 16 1 50700 31215 42852 +57 16 1 51545 31215 43469 +58 16 1 52253 31215 43851 +59 16 1 52860 31215 44236 +60 16 1 53729 31215 44616 +61 16 1 54336 31215 45084 +62 16 1 55042 31215 45628 +63 16 1 55918 31215 45956 +0 17 1 8775 32301 18287 +1 17 1 9656 32301 18668 +2 17 1 10409 32301 19110 +3 17 1 11000 32301 19519 +4 17 1 11841 32301 20091 +5 17 1 7521 32255 21073 +6 17 1 37763 32517 17893 +7 17 1 53121 32658 16091 +8 17 1 50159 32640 16320 +9 17 1 49566 32640 16320 +10 17 1 48702 32640 16320 +11 17 1 47840 32640 16320 +12 17 1 47248 32640 16320 +13 17 1 46373 32640 16320 +14 17 1 45666 32640 16320 +15 17 1 45059 32640 16320 +16 17 1 44190 32640 16320 +17 17 1 43583 32640 16320 +18 17 1 42876 32640 16320 +19 17 1 42000 32640 16320 +20 17 1 41407 32640 16320 +21 17 1 40544 32640 16320 +22 17 1 39656 32640 16320 +23 17 1 39134 32649 15966 +24 17 1 35323 32547 19847 +25 17 1 25869 32251 31335 +26 17 1 28299 32301 29833 +27 17 1 28931 32301 30301 +28 17 1 29801 32301 30833 +29 17 1 30392 32301 31175 +30 17 1 31147 32301 31636 +31 17 1 32028 32301 31983 +32 17 1 32640 32301 32618 +33 17 1 33528 32301 33075 +34 17 1 34335 32301 33457 +35 17 1 34921 32301 33900 +36 17 1 35793 32301 34308 +37 17 1 36459 32301 34883 +38 17 1 37091 32301 35194 +39 17 1 37923 32301 35693 +40 17 1 38774 32301 36208 +41 17 1 39359 32301 36558 +42 17 1 40166 32301 37038 +43 17 1 41054 32301 37405 +44 17 1 41666 32301 38049 +45 17 1 42547 32301 38470 +46 17 1 43303 32301 38850 +47 17 1 43893 32301 39267 +48 17 1 44763 32301 39699 +49 17 1 45396 32301 40254 +50 17 1 46061 32301 40559 +51 17 1 46933 32301 41091 +52 17 1 47519 32301 41591 +53 17 1 48325 32301 41955 +54 17 1 49213 32301 42453 +55 17 1 49825 32301 42831 +56 17 1 50700 32301 43460 +57 17 1 51545 32301 43852 +58 17 1 52253 32301 44237 +59 17 1 52860 32301 44628 +60 17 1 53729 32301 45090 +61 17 1 54336 32301 45623 +62 17 1 55042 32301 45964 +63 17 1 55918 32301 46425 +0 18 1 8775 33373 18654 +1 18 1 9656 33373 19092 +2 18 1 10409 33373 19514 +3 18 1 11000 33373 20091 +4 18 1 11841 33373 20440 +5 18 1 7521 33473 21534 +6 18 1 37763 32905 18060 +7 18 1 53121 32601 16068 +8 18 1 50159 32640 16320 +9 18 1 49566 32640 16320 +10 18 1 48702 32640 16320 +11 18 1 47840 32640 16320 +12 18 1 47248 32640 16320 +13 18 1 46373 32640 16320 +14 18 1 45666 32640 16320 +15 18 1 45059 32640 16320 +16 18 1 44190 32640 16320 +17 18 1 43583 32640 16320 +18 18 1 42876 32640 16320 +19 18 1 42000 32640 16320 +20 18 1 41407 32640 16320 +21 18 1 40544 32640 16320 +22 18 1 39656 32640 16320 +23 18 1 39134 32620 15955 +24 18 1 35323 32840 19961 +25 18 1 25869 33481 31751 +26 18 1 28299 33373 30303 +27 18 1 28931 33373 30830 +28 18 1 29801 33373 31182 +29 18 1 30392 33373 31608 +30 18 1 31147 33373 31960 +31 18 1 32028 33373 32574 +32 18 1 32640 33373 33022 +33 18 1 33528 33373 33443 +34 18 1 34335 33373 33881 +35 18 1 34921 33373 34303 +36 18 1 35793 33373 34883 +37 18 1 36459 33373 35199 +38 18 1 37091 33373 35693 +39 18 1 37923 33373 36198 +40 18 1 38774 33373 36560 +41 18 1 39359 33373 36998 +42 18 1 40166 33373 37363 +43 18 1 41054 33373 38006 +44 18 1 41666 33373 38434 +45 18 1 42547 33373 38844 +46 18 1 43303 33373 39260 +47 18 1 43893 33373 39699 +48 18 1 44763 33373 40255 +49 18 1 45396 33373 40562 +50 18 1 46061 33373 41086 +51 18 1 46933 33373 41570 +52 18 1 47519 33373 41946 +53 18 1 48325 33373 42397 +54 18 1 49213 33373 42787 +55 18 1 49825 33373 43434 +56 18 1 50700 33373 43831 +57 18 1 51545 33373 44235 +58 18 1 52253 33373 44629 +59 18 1 52860 33373 45091 +60 18 1 53729 33373 45619 +61 18 1 54336 33373 45972 +62 18 1 55042 33373 46397 +63 18 1 55918 33373 46749 +0 19 1 8775 34440 19055 +1 19 1 9656 34440 19498 +2 19 1 10409 34440 20086 +3 19 1 11000 34440 20435 +4 19 1 11841 34440 20848 +5 19 1 6842 34743 22074 +6 19 1 40988 33012 17681 +7 19 1 58275 32084 15155 +8 19 1 54807 32203 15426 +9 19 1 54059 32203 15357 +10 19 1 52967 32203 15279 +11 19 1 51878 32203 15237 +12 19 1 51130 32203 15159 +13 19 1 50023 32203 15096 +14 19 1 49131 32203 15051 +15 19 1 48364 32203 14993 +16 19 1 47266 32203 14945 +17 19 1 46500 32203 14866 +18 19 1 45605 32203 14824 +19 19 1 44499 32203 14757 +20 19 1 43750 32203 14715 +21 19 1 42659 32203 14641 +22 19 1 41538 32203 14566 +23 19 1 40841 32142 14099 +24 19 1 36450 32815 18695 +25 19 1 25636 34769 32543 +26 19 1 28299 34440 30829 +27 19 1 28931 34440 31185 +28 19 1 29801 34440 31593 +29 19 1 30392 34440 31947 +30 19 1 31147 34440 32545 +31 19 1 32028 34440 32966 +32 19 1 32640 34440 33416 +33 19 1 33528 34440 33844 +34 19 1 34335 34440 34286 +35 19 1 34921 34440 34877 +36 19 1 35793 34440 35195 +37 19 1 36459 34440 35693 +38 19 1 37091 34440 36194 +39 19 1 37923 34440 36562 +40 19 1 38774 34440 36970 +41 19 1 39359 34440 37337 +42 19 1 40166 34440 37960 +43 19 1 41054 34440 38377 +44 19 1 41666 34440 38831 +45 19 1 42547 34440 39237 +46 19 1 43303 34440 39692 +47 19 1 43893 34440 40254 +48 19 1 44763 34440 40562 +49 19 1 45396 34440 41084 +50 19 1 46061 34440 41558 +51 19 1 46933 34440 41943 +52 19 1 47519 34440 42356 +53 19 1 48325 34440 42743 +54 19 1 49213 34440 43389 +55 19 1 49825 34440 43790 +56 19 1 50700 34440 44231 +57 19 1 51545 34440 44618 +58 19 1 52253 34440 45089 +59 19 1 52860 34440 45619 +60 19 1 53729 34440 45975 +61 19 1 54336 34440 46382 +62 19 1 55042 34440 46736 +63 19 1 55918 34440 47333 +0 20 1 8775 35509 19470 +1 20 1 9656 35509 20075 +2 20 1 10409 35509 20419 +3 20 1 11000 35509 20855 +4 20 1 11841 35509 21198 +5 20 1 10711 35640 22052 +6 20 1 22613 34892 20739 +7 20 1 28905 34491 20168 +8 20 1 28318 34543 20456 +9 20 1 28459 34543 20769 +10 20 1 28665 34543 21175 +11 20 1 28869 34543 21331 +12 20 1 29010 34543 21711 +13 20 1 29218 34543 22028 +14 20 1 29386 34543 22221 +15 20 1 29531 34543 22504 +16 20 1 29737 34543 22696 +17 20 1 29881 34543 23107 +18 20 1 30049 34543 23282 +19 20 1 30258 34543 23624 +20 20 1 30398 34543 23788 +21 20 1 30604 34543 24136 +22 20 1 30815 34543 24524 +23 20 1 31115 34516 24525 +24 20 1 30025 34807 26523 +25 20 1 26961 35651 31594 +26 20 1 28299 35509 31183 +27 20 1 28931 35509 31591 +28 20 1 29801 35509 31942 +29 20 1 30392 35509 32530 +30 20 1 31147 35509 32930 +31 20 1 32028 35509 33390 +32 20 1 32640 35509 33789 +33 20 1 33528 35509 34258 +34 20 1 34335 35509 34865 +35 20 1 34921 35509 35185 +36 20 1 35793 35509 35692 +37 20 1 36459 35509 36196 +38 20 1 37091 35509 36562 +39 20 1 37923 35509 36960 +40 20 1 38774 35509 37324 +41 20 1 39359 35509 37932 +42 20 1 40166 35509 38322 +43 20 1 41054 35509 38806 +44 20 1 41666 35509 39200 +45 20 1 42547 35509 39676 +46 20 1 43303 35509 40251 +47 20 1 43893 35509 40559 +48 20 1 44763 35509 41085 +49 20 1 45396 35509 41557 +50 20 1 46061 35509 41942 +51 20 1 46933 35509 42334 +52 20 1 47519 35509 42717 +53 20 1 48325 35509 43345 +54 20 1 49213 35509 43733 +55 20 1 49825 35509 44220 +56 20 1 50700 35509 44595 +57 20 1 51545 35509 45083 +58 20 1 52253 35509 45622 +59 20 1 52860 35509 45972 +60 20 1 53729 35509 46381 +61 20 1 54336 35509 46731 +62 20 1 55042 35509 47318 +63 20 1 55918 35509 47724 +0 21 1 8775 36591 20057 +1 21 1 9656 36591 20394 +2 21 1 10409 36591 20867 +3 21 1 11000 36591 21198 +4 21 1 11841 36591 21748 +5 21 1 12984 36572 22143 +6 21 1 11816 36678 22815 +7 21 1 11648 36735 23344 +8 21 1 12753 36728 23762 +9 21 1 13417 36728 24410 +10 21 1 14384 36728 24806 +11 21 1 15349 36728 25341 +12 21 1 16012 36728 25950 +13 21 1 16993 36728 26319 +14 21 1 17784 36728 26796 +15 21 1 18464 36728 27151 +16 21 1 19437 36728 27741 +17 21 1 20116 36728 28178 +18 21 1 20909 36728 28645 +19 21 1 21889 36728 29029 +20 21 1 22553 36728 29499 +21 21 1 23520 36728 30159 +22 21 1 24514 36728 30541 +23 21 1 25400 36732 31106 +24 21 1 26249 36690 31426 +25 21 1 27739 36570 31057 +26 21 1 28299 36591 31604 +27 21 1 28931 36591 31945 +28 21 1 29801 36591 32528 +29 21 1 30392 36591 32917 +30 21 1 31147 36591 33377 +31 21 1 32028 36591 33741 +32 21 1 32640 36591 34218 +33 21 1 33528 36591 34846 +34 21 1 34335 36591 35169 +35 21 1 34921 36591 35691 +36 21 1 35793 36591 36205 +37 21 1 36459 36591 36560 +38 21 1 37091 36591 36967 +39 21 1 37923 36591 37322 +40 21 1 38774 36591 37922 +41 21 1 39359 36591 38293 +42 21 1 40166 36591 38782 +43 21 1 41054 36591 39151 +44 21 1 41666 36591 39652 +45 21 1 42547 36591 40245 +46 21 1 43303 36591 40553 +47 21 1 43893 36591 41089 +48 21 1 44763 36591 41567 +49 21 1 45396 36591 41943 +50 21 1 46061 36591 42332 +51 21 1 46933 36591 42708 +52 21 1 47519 36591 43323 +53 21 1 48325 36591 43684 +54 21 1 49213 36591 44199 +55 21 1 49825 36591 44563 +56 21 1 50700 36591 45072 +57 21 1 51545 36591 45626 +58 21 1 52253 36591 45965 +59 21 1 52860 36591 46394 +60 21 1 53729 36591 46734 +61 21 1 54336 36591 47317 +62 21 1 55042 36591 47712 +63 21 1 55918 36591 48104 +0 22 1 8775 37694 20364 +1 22 1 9656 37694 20883 +2 22 1 10409 37694 21200 +3 22 1 11000 37694 21761 +4 22 1 11841 37694 22211 +5 22 1 12675 37694 22598 +6 22 1 13282 37694 22977 +7 22 1 13990 37694 23370 +8 22 1 14866 37694 23987 +9 22 1 15459 37694 24327 +10 22 1 16323 37694 24833 +11 22 1 17185 37694 25445 +12 22 1 17777 37694 25770 +13 22 1 18652 37694 26261 +14 22 1 19359 37694 26580 +15 22 1 19966 37694 27152 +16 22 1 20835 37694 27568 +17 22 1 21442 37694 27985 +18 22 1 22149 37694 28345 +19 22 1 23025 37694 28770 +20 22 1 23618 37694 29395 +21 22 1 24481 37694 29736 +22 22 1 25369 37694 30269 +23 22 1 26176 37694 30841 +24 22 1 26761 37694 31167 +25 22 1 27633 37694 31629 +26 22 1 28299 37694 31956 +27 22 1 28931 37694 32541 +28 22 1 29801 37694 32926 +29 22 1 30392 37694 33376 +30 22 1 31147 37694 33722 +31 22 1 32028 37694 34186 +32 22 1 32640 37694 34817 +33 22 1 33528 37694 35148 +34 22 1 34335 37694 35689 +35 22 1 34921 37694 36220 +36 22 1 35793 37694 36558 +37 22 1 36459 37694 36991 +38 22 1 37091 37694 37333 +39 22 1 37923 37694 37929 +40 22 1 38774 37694 38291 +41 22 1 39359 37694 38774 +42 22 1 40166 37694 39114 +43 22 1 41054 37694 39620 +44 22 1 41666 37694 40234 +45 22 1 42547 37694 40545 +46 22 1 43303 37694 41096 +47 22 1 43893 37694 41587 +48 22 1 44763 37694 41945 +49 22 1 45396 37694 42351 +50 22 1 46061 37694 42714 +51 22 1 46933 37694 43321 +52 22 1 47519 37694 43666 +53 22 1 48325 37694 44180 +54 22 1 49213 37694 44524 +55 22 1 49825 37694 45057 +56 22 1 50700 37694 45630 +57 22 1 51545 37694 45955 +58 22 1 52253 37694 46419 +59 22 1 52860 37694 46746 +60 22 1 53729 37694 47330 +61 22 1 54336 37694 47721 +62 22 1 55042 37694 48103 +63 22 1 55918 37694 48720 +0 23 1 8775 38828 20895 +1 23 1 9656 38828 21205 +2 23 1 10409 38828 21781 +3 23 1 11000 38828 22245 +4 23 1 11841 38828 22608 +5 23 1 12675 38828 23011 +6 23 1 13282 38828 23389 +7 23 1 13990 38828 23997 +8 23 1 14866 38828 24323 +9 23 1 15459 38828 24814 +10 23 1 16323 38828 25430 +11 23 1 17185 38828 25753 +12 23 1 17777 38828 26287 +13 23 1 18652 38828 26594 +14 23 1 19359 38828 27178 +15 23 1 19966 38828 27603 +16 23 1 20835 38828 27997 +17 23 1 21442 38828 28370 +18 23 1 22149 38828 28781 +19 23 1 23025 38828 29393 +20 23 1 23618 38828 29714 +21 23 1 24481 38828 30250 +22 23 1 25369 38828 30844 +23 23 1 26176 38828 31160 +24 23 1 26761 38828 31664 +25 23 1 27633 38828 31977 +26 23 1 28299 38828 32568 +27 23 1 28931 38828 32958 +28 23 1 29801 38828 33387 +29 23 1 30392 38828 33735 +30 23 1 31147 38828 34184 +31 23 1 32028 38828 34799 +32 23 1 32640 38828 35124 +33 23 1 33528 38828 35687 +34 23 1 34335 38828 36237 +35 23 1 34921 38828 36559 +36 23 1 35793 38828 37030 +37 23 1 36459 38828 37357 +38 23 1 37091 38828 37954 +39 23 1 37923 38828 38315 +40 23 1 38774 38828 38780 +41 23 1 39359 38828 39111 +42 23 1 40166 38828 39601 +43 23 1 41054 38828 40218 +44 23 1 41666 38828 40538 +45 23 1 42547 38828 41107 +46 23 1 43303 38828 41614 +47 23 1 43893 38828 41952 +48 23 1 44763 38828 42389 +49 23 1 45396 38828 42738 +50 23 1 46061 38828 43341 +51 23 1 46933 38828 43679 +52 23 1 47519 38828 44179 +53 23 1 48325 38828 44502 +54 23 1 49213 38828 45037 +55 23 1 49825 38828 45633 +56 23 1 50700 38828 45949 +57 23 1 51545 38828 46454 +58 23 1 52253 38828 46767 +59 23 1 52860 38828 47357 +60 23 1 53729 38828 47751 +61 23 1 54336 38828 48122 +62 23 1 55042 38828 48730 +63 23 1 55918 38828 49054 +0 24 1 8775 40003 21216 +1 24 1 9656 40003 21809 +2 24 1 10409 40003 22290 +3 24 1 11000 40003 22631 +4 24 1 11841 40003 23066 +5 24 1 12675 40003 23429 +6 24 1 13282 40003 24032 +7 24 1 13990 40003 24354 +8 24 1 14866 40003 24828 +9 24 1 15459 40003 25427 +10 24 1 16323 40003 25745 +11 24 1 17185 40003 26312 +12 24 1 17777 40003 26618 +13 24 1 18652 40003 27214 +14 24 1 19359 40003 27654 +15 24 1 19966 40003 28021 +16 24 1 20835 40003 28421 +17 24 1 21442 40003 28816 +18 24 1 22149 40003 29419 +19 24 1 23025 40003 29730 +20 24 1 23618 40003 30247 +21 24 1 24481 40003 30844 +22 24 1 25369 40003 31162 +23 24 1 26176 40003 31703 +24 24 1 26761 40003 32010 +25 24 1 27633 40003 32609 +26 24 1 28299 40003 33011 +27 24 1 28931 40003 33411 +28 24 1 29801 40003 33779 +29 24 1 30392 40003 34211 +30 24 1 31147 40003 34812 +31 24 1 32028 40003 35121 +32 24 1 32640 40003 35684 +33 24 1 33528 40003 36256 +34 24 1 34335 40003 36570 +35 24 1 34921 40003 37079 +36 24 1 35793 40003 37396 +37 24 1 36459 40003 37997 +38 24 1 37091 40003 38366 +39 24 1 37923 40003 38801 +40 24 1 38774 40003 39143 +41 24 1 39359 40003 39616 +42 24 1 40166 40003 40216 +43 24 1 41054 40003 40533 +44 24 1 41666 40003 41121 +45 24 1 42547 40003 41648 +46 24 1 43303 40003 41970 +47 24 1 43893 40003 42443 +48 24 1 44763 40003 42779 +49 24 1 45396 40003 43382 +50 24 1 46061 40003 43724 +51 24 1 46933 40003 44195 +52 24 1 47519 40003 44519 +53 24 1 48325 40003 45035 +54 24 1 49213 40003 45632 +55 24 1 49825 40003 45951 +56 24 1 50700 40003 46492 +57 24 1 51545 40003 46800 +58 24 1 52253 40003 47398 +59 24 1 52860 40003 47802 +60 24 1 53729 40003 48163 +61 24 1 54336 40003 48765 +62 24 1 55042 40003 49088 +63 24 1 55918 40003 49595 +0 25 1 8775 41193 21830 +1 25 1 9656 41193 22329 +2 25 1 10409 41193 22657 +3 25 1 11000 41193 23124 +4 25 1 11841 41193 23478 +5 25 1 12675 41193 24078 +6 25 1 13282 41193 24403 +7 25 1 13990 41193 24861 +8 25 1 14866 41193 25445 +9 25 1 15459 41193 25752 +10 25 1 16323 41193 26316 +11 25 1 17185 41193 26638 +12 25 1 17777 41193 27248 +13 25 1 18652 41193 27704 +14 25 1 19359 41193 28050 +15 25 1 19966 41193 28481 +16 25 1 20835 41193 28864 +17 25 1 21442 41193 29459 +18 25 1 22149 41193 29768 +19 25 1 23025 41193 30268 +20 25 1 23618 41193 30846 +21 25 1 24481 41193 31165 +22 25 1 25369 41193 31728 +23 25 1 26176 41193 32042 +24 25 1 26761 41193 32652 +25 25 1 27633 41193 33069 +26 25 1 28299 41193 33440 +27 25 1 28931 41193 33836 +28 25 1 29801 41193 34254 +29 25 1 30392 41193 34843 +30 25 1 31147 41193 35145 +31 25 1 32028 41193 35688 +32 25 1 32640 41193 36260 +33 25 1 33528 41193 36583 +34 25 1 34335 41193 37119 +35 25 1 34921 41193 37438 +36 25 1 35793 41193 38045 +37 25 1 36459 41193 38427 +38 25 1 37091 41193 38829 +39 25 1 37923 41193 39194 +40 25 1 38774 41193 39650 +41 25 1 39359 41193 40234 +42 25 1 40166 41193 40538 +43 25 1 41054 41193 41125 +44 25 1 41666 41193 41671 +45 25 1 42547 41193 41990 +46 25 1 43303 41193 42495 +47 25 1 43893 41193 42826 +48 25 1 44763 41193 43430 +49 25 1 45396 41193 43783 +50 25 1 46061 41193 44219 +51 25 1 46933 41193 44559 +52 25 1 47519 41193 45056 +53 25 1 48325 41193 45634 +54 25 1 49213 41193 45953 +55 25 1 49825 41193 46517 +56 25 1 50700 41193 46831 +57 25 1 51545 41193 47441 +58 25 1 52253 41193 47856 +59 25 1 52860 41193 48212 +60 25 1 53729 41193 48812 +61 25 1 54336 41193 49142 +62 25 1 55042 41193 49613 +63 25 1 55918 41193 49935 +0 26 1 8775 42386 22333 +1 26 1 9656 42386 22667 +2 26 1 10409 42386 23155 +3 26 1 11000 42386 23509 +4 26 1 11841 42386 24108 +5 26 1 12675 42386 24440 +6 26 1 13282 42386 24887 +7 26 1 13990 42386 25456 +8 26 1 14866 42386 25769 +9 26 1 15459 42386 26294 +10 26 1 16323 42386 26625 +11 26 1 17185 42386 27252 +12 26 1 17777 42386 27724 +13 26 1 18652 42386 28065 +14 26 1 19359 42386 28520 +15 26 1 19966 42386 28897 +16 26 1 20835 42386 29487 +17 26 1 21442 42386 29800 +18 26 1 22149 42386 30285 +19 26 1 23025 42386 30844 +20 26 1 23618 42386 31162 +21 26 1 24481 42386 31711 +22 26 1 25369 42386 32046 +23 26 1 26176 42386 32668 +24 26 1 26761 42386 33101 +25 26 1 27633 42386 33456 +26 26 1 28299 42386 33877 +27 26 1 28931 42386 34285 +28 26 1 29801 42386 34865 +29 26 1 30392 42386 35167 +30 26 1 31147 42386 35692 +31 26 1 32028 42386 36242 +32 26 1 32640 42386 36575 +33 26 1 33528 42386 37123 +34 26 1 34335 42386 37453 +35 26 1 34921 42386 38069 +36 26 1 35793 42386 38467 +37 26 1 36459 42386 38845 +38 26 1 37091 42386 39233 +39 26 1 37923 42386 39676 +40 26 1 38774 42386 40246 +41 26 1 39359 42386 40546 +42 26 1 40166 42386 41112 +43 26 1 41054 42386 41655 +44 26 1 41666 42386 41993 +45 26 1 42547 42386 42514 +46 26 1 43303 42386 42851 +47 26 1 43893 42386 43459 +48 26 1 44763 42386 43827 +49 26 1 45396 42386 44232 +50 26 1 46061 42386 44592 +51 26 1 46933 42386 45073 +52 26 1 47519 42386 45633 +53 26 1 48325 42386 45951 +54 26 1 49213 42386 46499 +55 26 1 49825 42386 46834 +56 26 1 50700 42386 47457 +57 26 1 51545 42386 47885 +58 26 1 52253 42386 48242 +59 26 1 52860 42386 48842 +60 26 1 53729 42386 49186 +61 26 1 54336 42386 49620 +62 26 1 55042 42386 49958 +63 26 1 55918 42386 50480 +0 27 1 8775 43748 22662 +1 27 1 9656 43748 23158 +2 27 1 10409 43748 23521 +3 27 1 11000 43748 24124 +4 27 1 11841 43748 24464 +5 27 1 12675 43748 24904 +6 27 1 13282 43748 25461 +7 27 1 13990 43748 25787 +8 27 1 14866 43748 26268 +9 27 1 15459 43748 26601 +10 27 1 16323 43748 27225 +11 27 1 17185 43748 27711 +12 27 1 17777 43748 28067 +13 27 1 18652 43748 28534 +14 27 1 19359 43748 28914 +15 27 1 19966 43748 29503 +16 27 1 20835 43748 29822 +17 27 1 21442 43748 30297 +18 27 1 22149 43748 30839 +19 27 1 23025 43748 31168 +20 27 1 23618 43748 31673 +21 27 1 24481 43748 32020 +22 27 1 25369 43748 32658 +23 27 1 26176 43748 33103 +24 27 1 26761 43748 33462 +25 27 1 27633 43748 33899 +26 27 1 28299 43748 34304 +27 27 1 28931 43748 34878 +28 27 1 29801 43748 35184 +29 27 1 30392 43748 35695 +30 27 1 31147 43748 36224 +31 27 1 32028 43748 36563 +32 27 1 32640 43748 37089 +33 27 1 33528 43748 37443 +34 27 1 34335 43748 38072 +35 27 1 34921 43748 38482 +36 27 1 35793 43748 38852 +37 27 1 36459 43748 39259 +38 27 1 37091 43748 39693 +39 27 1 37923 43748 40252 +40 27 1 38774 43748 40554 +41 27 1 39359 43748 41102 +42 27 1 40166 43748 41621 +43 27 1 41054 43748 41977 +44 27 1 41666 43748 42500 +45 27 1 42547 43748 42854 +46 27 1 43303 43748 43470 +47 27 1 43893 43748 43851 +48 27 1 44763 43748 44237 +49 27 1 45396 43748 44616 +50 27 1 46061 43748 45085 +51 27 1 46933 43748 45628 +52 27 1 47519 43748 45957 +53 27 1 48325 43748 46461 +54 27 1 49213 43748 46808 +55 27 1 49825 43748 47446 +56 27 1 50700 43748 47887 +57 27 1 51545 43748 48254 +58 27 1 52253 43748 48857 +59 27 1 52860 43748 49215 +60 27 1 53729 43748 49621 +61 27 1 54336 43748 49977 +62 27 1 55042 43748 50483 +63 27 1 55918 43748 51013 +0 28 1 8775 44801 23132 +1 28 1 9656 44801 23514 +2 28 1 10409 44801 24126 +3 28 1 11000 44801 24473 +4 28 1 11841 44801 24914 +5 28 1 12675 44801 25463 +6 28 1 13282 44801 25803 +7 28 1 13990 44801 26245 +8 28 1 14866 44801 26585 +9 28 1 15459 44801 27188 +10 28 1 16323 44801 27666 +11 28 1 17185 44801 28056 +12 28 1 17777 44801 28524 +13 28 1 18652 44801 28916 +14 28 1 19359 44801 29509 +15 28 1 19966 44801 29834 +16 28 1 20835 44801 30304 +17 28 1 21442 44801 30834 +18 28 1 22149 44801 31178 +19 28 1 23025 44801 31637 +20 28 1 23618 44801 31986 +21 28 1 24481 44801 32622 +22 28 1 25369 44801 33076 +23 28 1 26176 44801 33460 +24 28 1 26761 44801 33901 +25 28 1 27633 44801 34311 +26 28 1 28299 44801 34884 +27 28 1 28931 44801 35196 +28 28 1 29801 44801 35697 +29 28 1 30392 44801 36209 +30 28 1 31147 44801 36561 +31 28 1 32028 44801 37039 +32 28 1 32640 44801 37408 +33 28 1 33528 44801 38052 +34 28 1 34335 44801 38471 +35 28 1 34921 44801 38853 +36 28 1 35793 44801 39268 +37 28 1 36459 44801 39703 +38 28 1 37091 44801 40256 +39 28 1 37923 44801 40560 +40 28 1 38774 44801 41095 +41 28 1 39359 44801 41592 +42 28 1 40166 44801 41958 +43 28 1 41054 44801 42454 +44 28 1 41666 44801 42834 +45 28 1 42547 44801 43463 +46 28 1 43303 44801 43853 +47 28 1 43893 44801 44239 +48 28 1 44763 44801 44629 +49 28 1 45396 44801 45093 +50 28 1 46061 44801 45624 +51 28 1 46933 44801 45967 +52 28 1 47519 44801 46426 +53 28 1 48325 44801 46774 +54 28 1 49213 44801 47409 +55 28 1 49825 44801 47862 +56 28 1 50700 44801 48247 +57 28 1 51545 44801 48858 +58 28 1 52253 44801 49226 +59 28 1 52860 44801 49621 +60 28 1 53729 44801 49990 +61 28 1 54336 44801 50486 +62 28 1 55042 44801 50999 +63 28 1 55918 44801 51350 +0 29 1 8775 45874 23489 +1 29 1 9656 45874 24114 +2 29 1 10409 45874 24466 +3 29 1 11000 45874 24915 +4 29 1 11841 45874 25464 +5 29 1 12675 45874 25813 +6 29 1 13282 45874 26228 +7 29 1 13990 45874 26576 +8 29 1 14866 45874 27162 +9 29 1 15459 45874 27612 +10 29 1 16323 45874 28032 +11 29 1 17185 45874 28490 +12 29 1 17777 45874 28903 +13 29 1 18652 45874 29506 +14 29 1 19359 45874 29835 +15 29 1 19966 45874 30307 +16 29 1 20835 45874 30831 +17 29 1 21442 45874 31186 +18 29 1 22149 45874 31610 +19 29 1 23025 45874 31963 +20 29 1 23618 45874 32579 +21 29 1 24481 45874 33023 +22 29 1 25369 45874 33448 +23 29 1 26176 45874 33882 +24 29 1 26761 45874 34308 +25 29 1 27633 45874 34885 +26 29 1 28299 45874 35200 +27 29 1 28931 45874 35698 +28 29 1 29801 45874 36199 +29 29 1 30392 45874 36564 +30 29 1 31147 45874 36998 +31 29 1 32028 45874 37367 +32 29 1 32640 45874 38011 +33 29 1 33528 45874 38435 +34 29 1 34335 45874 38849 +35 29 1 34921 45874 39261 +36 29 1 35793 45874 39704 +37 29 1 36459 45874 40257 +38 29 1 37091 45874 40564 +39 29 1 37923 45874 41091 +40 29 1 38774 45874 41571 +41 29 1 39359 45874 41951 +42 29 1 40166 45874 42398 +43 29 1 41054 45874 42792 +44 29 1 41666 45874 43439 +45 29 1 42547 45874 43832 +46 29 1 43303 45874 44239 +47 29 1 43893 45874 44630 +48 29 1 44763 45874 45096 +49 29 1 45396 45874 45621 +50 29 1 46061 45874 45975 +51 29 1 46933 45874 46399 +52 29 1 47519 45874 46752 +53 29 1 48325 45874 47367 +54 29 1 49213 45874 47811 +55 29 1 49825 45874 48221 +56 29 1 50700 45874 48846 +57 29 1 51545 45874 49217 +58 29 1 52253 45874 49622 +59 29 1 52860 45874 49994 +60 29 1 53729 45874 50487 +61 29 1 54336 45874 50988 +62 29 1 55042 45874 51353 +63 29 1 55918 45874 51787 +0 30 1 8775 46943 24088 +1 30 1 9656 46943 24445 +2 30 1 10409 46943 24910 +3 30 1 11000 46943 25465 +4 30 1 11841 46943 25814 +5 30 1 12675 46943 26222 +6 30 1 13282 46943 26572 +7 30 1 13990 46943 27147 +8 30 1 14866 46943 27574 +9 30 1 15459 46943 28007 +10 30 1 16323 46943 28434 +11 30 1 17185 46943 28876 +12 30 1 17777 46943 29493 +13 30 1 18652 46943 29825 +14 30 1 19359 46943 30307 +15 30 1 19966 46943 30832 +16 30 1 20835 46943 31189 +17 30 1 21442 46943 31595 +18 30 1 22149 46943 31951 +19 30 1 23025 46943 32551 +20 30 1 23618 46943 32967 +21 30 1 24481 46943 33423 +22 30 1 25369 46943 33844 +23 30 1 26176 46943 34293 +24 30 1 26761 46943 34881 +25 30 1 27633 46943 35197 +26 30 1 28299 46943 35700 +27 30 1 28931 46943 36195 +28 30 1 29801 46943 36566 +29 30 1 30392 46943 36972 +30 30 1 31147 46943 37341 +31 30 1 32028 46943 37966 +32 30 1 32640 46943 38378 +33 30 1 33528 46943 38838 +34 30 1 34335 46943 39238 +35 30 1 34921 46943 39698 +36 30 1 35793 46943 40257 +37 30 1 36459 46943 40564 +38 30 1 37091 46943 41091 +39 30 1 37923 46943 41559 +40 30 1 38774 46943 41948 +41 30 1 39359 46943 42357 +42 30 1 40166 46943 42749 +43 30 1 41054 46943 43395 +44 30 1 41666 46943 43791 +45 30 1 42547 46943 44237 +46 30 1 43303 46943 44619 +47 30 1 43893 46943 45096 +48 30 1 44763 46943 45621 +49 30 1 45396 46943 45979 +50 30 1 46061 46943 46384 +51 30 1 46933 46943 46740 +52 30 1 47519 46943 47340 +53 30 1 48325 46943 47758 +54 30 1 49213 46943 48176 +55 30 1 49825 46943 48820 +56 30 1 50700 46943 49191 +57 30 1 51545 46943 49624 +58 30 1 52253 46943 49991 +59 30 1 52860 46943 50489 +60 30 1 53729 46943 50985 +61 30 1 54336 46943 51356 +62 30 1 55042 46943 51761 +63 30 1 55918 46943 52130 +0 31 1 8775 48017 24412 +1 31 1 9656 48017 24897 +2 31 1 10409 48017 25464 +3 31 1 11000 48017 25807 +4 31 1 11841 48017 26229 +5 31 1 12675 48017 26572 +6 31 1 13282 48017 27143 +7 31 1 13990 48017 27554 +8 31 1 14866 48017 27995 +9 31 1 15459 48017 28380 +10 31 1 16323 48017 28834 +11 31 1 17185 48017 29470 +12 31 1 17777 48017 29805 +13 31 1 18652 48017 30304 +14 31 1 19359 48017 30836 +15 31 1 19966 48017 31188 +16 31 1 20835 48017 31595 +17 31 1 21442 48017 31947 +18 31 1 22149 48017 32538 +19 31 1 23025 48017 32932 +20 31 1 23618 48017 33398 +21 31 1 24481 48017 33791 +22 31 1 25369 48017 34267 +23 31 1 26176 48017 34871 +24 31 1 26761 48017 35188 +25 31 1 27633 48017 35702 +26 31 1 28299 48017 36200 +27 31 1 28931 48017 36568 +28 31 1 29801 48017 36963 +29 31 1 30392 48017 37330 +30 31 1 31147 48017 37941 +31 31 1 32028 48017 38324 +32 31 1 32640 48017 38815 +33 31 1 33528 48017 39202 +34 31 1 34335 48017 39685 +35 31 1 34921 48017 40256 +36 31 1 35793 48017 40562 +37 31 1 36459 48017 41094 +38 31 1 37091 48017 41560 +39 31 1 37923 48017 41949 +40 31 1 38774 48017 42336 +41 31 1 39359 48017 42724 +42 31 1 40166 48017 43353 +43 31 1 41054 48017 43735 +44 31 1 41666 48017 44229 +45 31 1 42547 48017 44597 +46 31 1 43303 48017 45092 +47 31 1 43893 48017 45625 +48 31 1 44763 48017 45977 +49 31 1 45396 48017 46385 +50 31 1 46061 48017 46737 +51 31 1 46933 48017 47327 +52 31 1 47519 48017 47726 +53 31 1 48325 48017 48134 +54 31 1 49213 48017 48779 +55 31 1 49825 48017 49149 +56 31 1 50700 48017 49626 +57 31 1 51545 48017 49980 +58 31 1 52253 48017 50490 +59 31 1 52860 48017 50989 +60 31 1 53729 48017 51357 +61 31 1 54336 48017 51753 +62 31 1 55042 48017 52119 +63 31 1 55918 48017 52729 +0 32 1 8775 49106 24878 +1 32 1 9656 49106 25463 +2 32 1 10409 49106 25794 +3 32 1 11000 49106 26248 +4 32 1 11841 49106 26579 +5 32 1 12675 49106 27151 +6 32 1 13282 49106 27555 +7 32 1 13990 49106 27993 +8 32 1 14866 49106 28352 +9 32 1 15459 49106 28797 +10 32 1 16323 49106 29436 +11 32 1 17185 49106 29778 +12 32 1 17777 49106 30297 +13 32 1 18652 49106 30843 +14 32 1 19359 49106 31182 +15 32 1 19966 49106 31612 +16 32 1 20835 49106 31953 +17 32 1 21442 49106 32541 +18 32 1 22149 49106 32921 +19 32 1 23025 49106 33389 +20 32 1 23618 49106 33745 +21 32 1 24481 49106 34231 +22 32 1 25369 49106 34855 +23 32 1 26176 49106 35174 +24 32 1 26761 49106 35704 +25 32 1 27633 49106 36212 +26 32 1 28299 49106 36567 +27 32 1 28931 49106 36974 +28 32 1 29801 49106 37332 +29 32 1 30392 49106 37934 +30 32 1 31147 49106 38297 +31 32 1 32028 49106 38794 +32 32 1 32640 49106 39156 +33 32 1 33528 49106 39666 +34 32 1 34335 49106 40253 +35 32 1 34921 49106 40559 +36 32 1 35793 49106 41102 +37 32 1 36459 49106 41573 +38 32 1 37091 49106 41951 +39 32 1 37923 49106 42338 +40 32 1 38774 49106 42718 +41 32 1 39359 49106 43334 +42 32 1 40166 49106 43689 +43 32 1 41054 49106 44211 +44 32 1 41666 49106 44567 +45 32 1 42547 49106 45085 +46 32 1 43303 49106 45633 +47 32 1 43893 49106 45972 +48 32 1 44763 49106 46402 +49 32 1 45396 49106 46743 +50 32 1 46061 49106 47329 +51 32 1 46933 49106 47716 +52 32 1 47519 49106 48116 +53 32 1 48325 49106 48744 +54 32 1 49213 49106 49099 +55 32 1 49825 49106 49624 +56 32 1 50700 49106 49964 +57 32 1 51545 49106 50493 +58 32 1 52253 49106 51002 +59 32 1 52860 49106 51357 +60 32 1 53729 49106 51764 +61 32 1 54336 49106 52121 +62 32 1 55042 49106 52723 +63 32 1 55918 49106 53091 +0 33 1 8775 50222 25459 +1 33 1 9656 50222 25780 +2 33 1 10409 50222 26276 +3 33 1 11000 50222 26592 +4 33 1 11841 50222 27171 +5 33 1 12675 50222 27578 +6 33 1 13282 50222 27999 +7 33 1 13990 50222 28355 +8 33 1 14866 50222 28788 +9 33 1 15459 50222 29411 +10 33 1 16323 50222 29746 +11 33 1 17185 50222 30289 +12 33 1 17777 50222 30853 +13 33 1 18652 50222 31178 +14 33 1 19359 50222 31643 +15 33 1 19966 50222 31970 +16 33 1 20835 50222 32559 +17 33 1 21442 50222 32935 +18 33 1 22149 50222 33391 +19 33 1 23025 50222 33732 +20 33 1 23618 50222 34205 +21 33 1 24481 50222 34832 +22 33 1 25369 50222 35158 +23 33 1 26176 50222 35709 +24 33 1 26761 50222 36231 +25 33 1 27633 50222 36569 +26 33 1 28299 50222 37004 +27 33 1 28931 50222 37348 +28 33 1 29801 50222 37947 +29 33 1 30392 50222 38300 +30 33 1 31147 50222 38790 +31 33 1 32028 50222 39123 +32 33 1 32640 50222 39640 +33 33 1 33528 50222 40248 +34 33 1 34335 50222 40556 +35 33 1 34921 50222 41116 +36 33 1 35793 50222 41598 +37 33 1 36459 50222 41957 +38 33 1 37091 50222 42363 +39 33 1 37923 50222 42730 +40 33 1 38774 50222 43338 +41 33 1 39359 50222 43675 +42 33 1 40166 50222 44196 +43 33 1 41054 50222 44534 +44 33 1 41666 50222 45076 +45 33 1 42547 50222 45643 +46 33 1 43303 50222 45966 +47 33 1 43893 50222 46433 +48 33 1 44763 50222 46759 +49 33 1 45396 50222 47348 +50 33 1 46061 50222 47730 +51 33 1 46933 50222 48119 +52 33 1 47519 50222 48736 +53 33 1 48325 50222 49066 +54 33 1 49213 50222 49614 +55 33 1 49825 50222 49947 +56 33 1 50700 50222 50497 +57 33 1 51545 50222 51021 +58 33 1 52253 50222 51358 +59 33 1 52860 50222 51794 +60 33 1 53729 50222 52137 +61 33 1 54336 50222 52736 +62 33 1 55042 50222 53094 +63 33 1 55918 50222 53521 +0 34 1 8775 51376 25772 +1 34 1 9656 51376 26309 +2 34 1 10409 51376 26615 +3 34 1 11000 51376 27205 +4 34 1 11841 51376 27620 +5 34 1 12675 51376 28018 +6 34 1 13282 51376 28388 +7 34 1 13990 51376 28807 +8 34 1 14866 51376 29416 +9 34 1 15459 51376 29731 +10 34 1 16323 51376 30278 +11 34 1 17185 51376 30864 +12 34 1 17777 51376 31179 +13 34 1 18652 51376 31685 +14 34 1 19359 51376 31999 +15 34 1 19966 51376 32594 +16 34 1 20835 51376 32976 +17 34 1 21442 51376 33408 +18 34 1 22149 51376 33753 +19 34 1 23025 51376 34211 +20 34 1 23618 51376 34821 +21 34 1 24481 51376 35142 +22 34 1 25369 51376 35715 +23 34 1 26176 51376 36257 +24 34 1 26761 51376 36578 +25 34 1 27633 51376 37050 +26 34 1 28299 51376 37380 +27 34 1 28931 51376 37980 +28 34 1 29801 51376 38333 +29 34 1 30392 51376 38801 +30 34 1 31147 51376 39129 +31 34 1 32028 51376 39630 +32 34 1 32640 51376 40239 +33 34 1 33528 51376 40557 +34 34 1 34335 51376 41135 +35 34 1 34921 51376 41633 +36 34 1 35793 51376 41972 +37 34 1 36459 51376 42409 +38 34 1 37091 51376 42762 +39 34 1 37923 51376 43365 +40 34 1 38774 51376 43697 +41 34 1 39359 51376 44201 +42 34 1 40166 51376 44520 +43 34 1 41054 51376 45066 +44 34 1 41666 51376 45653 +45 34 1 42547 51376 45968 +46 34 1 43303 51376 46475 +47 34 1 43893 51376 46788 +48 34 1 44763 51376 47383 +49 34 1 45396 51376 47768 +50 34 1 46061 51376 48147 +51 34 1 46933 51376 48754 +52 34 1 47519 51376 49072 +53 34 1 48325 51376 49608 +54 34 1 49213 51376 49930 +55 34 1 49825 51376 50503 +56 34 1 50700 51376 51046 +57 34 1 51545 51376 51367 +58 34 1 52253 51376 51840 +59 34 1 52860 51376 52169 +60 34 1 53729 51376 52769 +61 34 1 54336 51376 53125 +62 34 1 55042 51376 53540 +63 34 1 55918 51376 54149 +0 35 1 8775 52576 26341 +1 35 1 9656 52576 26650 +2 35 1 10409 52576 27252 +3 35 1 11000 52576 27681 +4 35 1 11841 52576 28051 +5 35 1 12675 52576 28449 +6 35 1 13282 52576 28853 +7 35 1 13990 52576 29451 +8 35 1 14866 52576 29757 +9 35 1 15459 52576 30287 +10 35 1 16323 52576 30873 +11 35 1 17185 52576 31192 +12 35 1 17777 52576 31732 +13 35 1 18652 52576 32043 +14 35 1 19359 52576 32646 +15 35 1 19966 52576 33039 +16 35 1 20835 52576 33440 +17 35 1 21442 52576 33806 +18 35 1 22149 52576 34249 +19 35 1 23025 52576 34844 +20 35 1 23618 52576 35149 +21 35 1 24481 52576 35724 +22 35 1 25369 52576 36285 +23 35 1 26176 52576 36600 +24 35 1 26761 52576 37107 +25 35 1 27633 52576 37430 +26 35 1 28299 52576 38033 +27 35 1 28931 52576 38394 +28 35 1 29801 52576 38831 +29 35 1 30392 52576 39171 +30 35 1 31147 52576 39655 +31 35 1 32028 52576 40246 +32 35 1 32640 52576 40562 +33 35 1 33528 52576 41161 +34 35 1 34335 52576 41676 +35 35 1 34921 52576 41999 +36 35 1 35793 52576 42471 +37 35 1 36459 52576 42814 +38 35 1 37091 52576 43416 +39 35 1 37923 52576 43752 +40 35 1 38774 52576 44225 +41 35 1 39359 52576 44547 +42 35 1 40166 52576 45074 +43 35 1 41054 52576 45661 +44 35 1 41666 52576 45980 +45 35 1 42547 52576 46521 +46 35 1 43303 52576 46832 +47 35 1 43893 52576 47435 +48 35 1 44763 52576 47828 +49 35 1 45396 52576 48199 +50 35 1 46061 52576 48799 +51 35 1 46933 52576 49116 +52 35 1 47519 52576 49624 +53 35 1 48325 52576 49938 +54 35 1 49213 52576 50511 +55 35 1 49825 52576 51073 +56 35 1 50700 52576 51388 +57 35 1 51545 52576 51897 +58 35 1 52253 52576 52219 +59 35 1 52860 52576 52822 +60 35 1 53729 52576 53183 +61 35 1 54336 52576 53587 +62 35 1 55042 52576 54185 +63 35 1 55918 52576 54492 +0 36 1 8775 53754 26665 +1 36 1 9656 53754 27282 +2 36 1 10409 53754 27726 +3 36 1 11000 53754 28076 +4 36 1 11841 53754 28503 +5 36 1 12675 53754 28897 +6 36 1 13282 53754 29486 +7 36 1 13990 53754 29791 +8 36 1 14866 53754 30303 +9 36 1 15459 53754 30868 +10 36 1 16323 53754 31193 +11 36 1 17185 53754 31750 +12 36 1 17777 53754 32071 +13 36 1 18652 53754 32684 +14 36 1 19359 53754 33091 +15 36 1 19966 53754 33466 +16 36 1 20835 53754 33858 +17 36 1 21442 53754 34288 +18 36 1 22149 53754 34868 +19 36 1 23025 53754 35169 +20 36 1 23618 53754 35724 +21 36 1 24481 53754 36282 +22 36 1 25369 53754 36611 +23 36 1 26176 53754 37141 +24 36 1 26761 53754 37467 +25 36 1 27633 53754 38076 +26 36 1 28299 53754 38450 +27 36 1 28931 53754 38854 +28 36 1 29801 53754 39216 +29 36 1 30392 53754 39685 +30 36 1 31147 53754 40258 +31 36 1 32028 53754 40563 +32 36 1 32640 53754 41160 +33 36 1 33528 53754 41693 +34 36 1 34335 53754 42018 +35 36 1 34921 53754 42516 +36 36 1 35793 53754 42857 +37 36 1 36459 53754 43459 +38 36 1 37091 53754 43807 +39 36 1 37923 53754 44243 +40 36 1 38774 53754 44582 +41 36 1 39359 53754 45091 +42 36 1 40166 53754 45657 +43 36 1 41054 53754 45981 +44 36 1 41666 53754 46538 +45 36 1 42547 53754 46860 +46 36 1 43303 53754 47473 +47 36 1 43893 53754 47877 +48 36 1 44763 53754 48244 +49 36 1 45396 53754 48839 +50 36 1 46061 53754 49167 +51 36 1 46933 53754 49636 +52 36 1 47519 53754 49959 +53 36 1 48325 53754 50511 +54 36 1 49213 53754 51070 +55 36 1 49825 53754 51399 +56 36 1 50700 53754 51930 +57 36 1 51545 53754 52256 +58 36 1 52253 53754 52865 +59 36 1 52860 53754 53234 +60 36 1 53729 53754 53631 +61 36 1 54336 53754 54219 +62 36 1 55042 53754 54533 +63 36 1 55918 53754 55035 +0 37 1 8775 54890 27281 +1 37 1 9656 54890 27738 +2 37 1 10409 54890 28088 +3 37 1 11000 54890 28534 +4 37 1 11841 54890 28926 +5 37 1 12675 54890 29508 +6 37 1 13282 54890 29817 +7 37 1 13990 54890 30315 +8 37 1 14866 54890 30860 +9 37 1 15459 54890 31188 +10 37 1 16323 54890 31726 +11 37 1 17185 54890 32070 +12 37 1 17777 54890 32696 +13 37 1 18652 54890 33116 +14 37 1 19359 54890 33479 +15 37 1 19966 54890 33893 +16 37 1 20835 54890 34314 +17 37 1 21442 54890 34884 +18 37 1 22149 54890 35185 +19 37 1 23025 54890 35723 +20 37 1 23618 54890 36257 +21 37 1 24481 54890 36601 +22 37 1 25369 54890 37137 +23 37 1 26176 54890 37479 +24 37 1 26761 54890 38095 +25 37 1 27633 54890 38484 +26 37 1 28299 54890 38866 +27 37 1 28931 54890 39249 +28 37 1 29801 54890 39706 +29 37 1 30392 54890 40263 +30 37 1 31147 54890 40565 +31 37 1 32028 54890 41143 +32 37 1 32640 54890 41670 +33 37 1 33528 54890 42018 +34 37 1 34335 54890 42528 +35 37 1 34921 54890 42878 +36 37 1 35793 54890 43484 +37 37 1 36459 54890 43846 +38 37 1 37091 54890 44251 +39 37 1 37923 54890 44609 +40 37 1 38774 54890 45104 +41 37 1 39359 54890 45648 +42 37 1 40166 54890 45976 +43 37 1 41054 54890 46514 +44 37 1 41666 54890 46858 +45 37 1 42547 54890 47485 +46 37 1 43303 54890 47899 +47 37 1 43893 54890 48270 +48 37 1 44763 54890 48864 +49 37 1 45396 54890 49206 +50 37 1 46061 54890 49638 +51 37 1 46933 54890 49976 +52 37 1 47519 54890 50511 +53 37 1 48325 54890 51046 +54 37 1 49213 54890 51389 +55 37 1 49825 54890 51926 +56 37 1 50700 54890 52267 +57 37 1 51545 54890 52885 +58 37 1 52253 54890 53264 +59 37 1 52860 54890 53659 +60 37 1 53729 54890 54240 +61 37 1 54336 54890 54568 +62 37 1 55042 54890 55028 +63 37 1 55918 54890 55355 +0 38 1 8775 55993 27719 +1 38 1 9656 55993 28089 +2 38 1 10409 55993 28542 +3 38 1 11000 55993 28939 +4 38 1 11841 55993 29520 +5 38 1 12675 55993 29834 +6 38 1 13282 55993 30324 +7 38 1 13990 55993 30849 +8 38 1 14866 55993 31191 +9 38 1 15459 55993 31681 +10 38 1 16323 55993 32040 +11 38 1 17185 55993 32683 +12 38 1 17777 55993 33112 +13 38 1 18652 55993 33484 +14 38 1 19359 55993 33908 +15 38 1 19966 55993 34330 +16 38 1 20835 55993 34892 +17 38 1 21442 55993 35197 +18 38 1 22149 55993 35722 +19 38 1 23025 55993 36233 +20 38 1 23618 55993 36587 +21 38 1 24481 55993 37097 +22 38 1 25369 55993 37465 +23 38 1 26176 55993 38094 +24 38 1 26761 55993 38492 +25 38 1 27633 55993 38871 +26 38 1 28299 55993 39269 +27 38 1 28931 55993 39720 +28 38 1 29801 55993 40265 +29 38 1 30392 55993 40568 +30 38 1 31147 55993 41129 +31 38 1 32028 55993 41629 +32 38 1 32640 55993 42001 +33 38 1 33528 55993 42508 +34 38 1 34335 55993 42877 +35 38 1 34921 55993 43491 +36 38 1 35793 55993 43863 +37 38 1 36459 55993 44255 +38 38 1 37091 55993 44628 +39 38 1 37923 55993 45112 +40 38 1 38774 55993 45639 +41 38 1 39359 55993 45980 +42 38 1 40166 55993 46470 +43 38 1 41054 55993 46828 +44 38 1 41666 55993 47471 +45 38 1 42547 55993 47895 +46 38 1 43303 55993 48278 +47 38 1 43893 55993 48875 +48 38 1 44763 55993 49229 +49 38 1 45396 55993 49636 +50 38 1 46061 55993 49990 +51 38 1 46933 55993 50511 +52 38 1 47519 55993 51022 +53 38 1 48325 55993 51376 +54 38 1 49213 55993 51885 +55 38 1 49825 55993 52253 +56 38 1 50700 55993 52883 +57 38 1 51545 55993 53271 +58 38 1 52253 55993 53673 +59 38 1 52860 55993 54252 +60 38 1 53729 55993 54593 +61 38 1 54336 55993 55019 +62 38 1 55042 55993 55360 +63 38 1 55918 55993 55917 +0 39 1 8775 57075 28080 +1 39 1 9656 57075 28527 +2 39 1 10409 57075 28939 +3 39 1 11000 57075 29523 +4 39 1 11841 57075 29841 +5 39 1 12675 57075 30329 +6 39 1 13282 57075 30841 +7 39 1 13990 57075 31197 +8 39 1 14866 57075 31642 +9 39 1 15459 57075 32003 +10 39 1 16323 57075 32644 +11 39 1 17185 57075 33080 +12 39 1 17777 57075 33482 +13 39 1 18652 57075 33905 +14 39 1 19359 57075 34335 +15 39 1 19966 57075 34896 +16 39 1 20835 57075 35204 +17 39 1 21442 57075 35722 +18 39 1 22149 57075 36214 +19 39 1 23025 57075 36583 +20 39 1 23618 57075 37043 +21 39 1 24481 57075 37427 +22 39 1 25369 57075 38073 +23 39 1 26176 57075 38476 +24 39 1 26761 57075 38873 +25 39 1 27633 57075 39273 +26 39 1 28299 57075 39727 +27 39 1 28931 57075 40265 +28 39 1 29801 57075 40571 +29 39 1 30392 57075 41120 +30 39 1 31147 57075 41596 +31 39 1 32028 57075 41981 +32 39 1 32640 57075 42457 +33 39 1 33528 57075 42854 +34 39 1 34335 57075 43482 +35 39 1 34921 57075 43859 +36 39 1 35793 57075 44257 +37 39 1 36459 57075 44636 +38 39 1 37091 57075 45118 +39 39 1 37923 57075 45631 +40 39 1 38774 57075 45987 +41 39 1 39359 57075 46431 +42 39 1 40166 57075 46791 +43 39 1 41054 57075 47432 +44 39 1 41666 57075 47865 +45 39 1 42547 57075 48269 +46 39 1 43303 57075 48874 +47 39 1 43893 57075 49235 +48 39 1 44763 57075 49636 +49 39 1 45396 57075 49998 +50 39 1 46061 57075 50511 +51 39 1 46933 57075 51004 +52 39 1 47519 57075 51372 +53 39 1 48325 57075 51831 +54 39 1 49213 57075 52214 +55 39 1 49825 57075 52862 +56 39 1 50700 57075 53256 +57 39 1 51545 57075 53672 +58 39 1 52253 57075 54255 +59 39 1 52860 57075 54605 +60 39 1 53729 57075 55013 +61 39 1 54336 57075 55364 +62 39 1 55042 57075 55909 +63 39 1 55918 57075 56385 +0 40 1 8775 58146 28490 +1 40 1 9656 58146 28927 +2 40 1 10409 58146 29520 +3 40 1 11000 58146 29840 +4 40 1 11841 58146 30333 +5 40 1 12675 58146 30837 +6 40 1 13282 58146 31203 +7 40 1 13990 58146 31615 +8 40 1 14866 58146 31979 +9 40 1 15459 58146 32602 +10 40 1 16323 58146 33024 +11 40 1 17185 58146 33471 +12 40 1 17777 58146 33883 +13 40 1 18652 58146 34332 +14 40 1 19359 58146 34897 +15 40 1 19966 58146 35206 +16 40 1 20835 58146 35724 +17 40 1 21442 58146 36203 +18 40 1 22149 58146 36584 +19 40 1 23025 58146 37002 +20 40 1 23618 58146 37385 +21 40 1 24481 58146 38032 +22 40 1 25369 58146 38436 +23 40 1 26176 58146 38871 +24 40 1 26761 58146 39263 +25 40 1 27633 58146 39729 +26 40 1 28299 58146 40266 +27 40 1 28931 58146 40572 +28 40 1 29801 58146 41116 +29 40 1 30392 58146 41573 +30 40 1 31147 58146 41973 +31 40 1 32028 58146 42399 +32 40 1 32640 58146 42812 +33 40 1 33528 58146 43457 +34 40 1 34335 58146 43835 +35 40 1 34921 58146 44259 +36 40 1 35793 58146 44634 +37 40 1 36459 58146 45121 +38 40 1 37091 58146 45627 +39 40 1 37923 58146 45992 +40 40 1 38774 58146 46404 +41 40 1 39359 58146 46768 +42 40 1 40166 58146 47390 +43 40 1 41054 58146 47812 +44 40 1 41666 58146 48242 +45 40 1 42547 58146 48863 +46 40 1 43303 58146 49222 +47 40 1 43893 58146 49639 +48 40 1 44763 58146 50000 +49 40 1 45396 58146 50512 +50 40 1 46061 58146 50992 +51 40 1 46933 58146 51373 +52 40 1 47519 58146 51790 +53 40 1 48325 58146 52173 +54 40 1 49213 58146 52819 +55 40 1 49825 58146 53221 +56 40 1 50700 58146 53659 +57 40 1 51545 58146 54252 +58 40 1 52253 58146 54602 +59 40 1 52860 58146 55013 +60 40 1 53729 58146 55366 +61 40 1 54336 58146 55905 +62 40 1 55042 58146 56363 +63 40 1 55918 58146 56762 +0 41 1 8775 59220 28902 +1 41 1 9656 59220 29509 +2 41 1 10409 59220 29830 +3 41 1 11000 59220 30335 +4 41 1 11841 59220 30840 +5 41 1 12675 59220 31206 +6 41 1 13282 59220 31604 +7 41 1 13990 59220 31967 +8 41 1 14866 59220 32576 +9 41 1 15459 59220 32968 +10 41 1 16323 59220 33450 +11 41 1 17185 59220 33846 +12 41 1 17777 59220 34319 +13 41 1 18652 59220 34895 +14 41 1 19359 59220 35204 +15 41 1 19966 59220 35728 +16 41 1 20835 59220 36202 +17 41 1 21442 59220 36586 +18 41 1 22149 59220 36978 +19 41 1 23025 59220 37361 +20 41 1 23618 59220 37990 +21 41 1 24481 59220 38380 +22 41 1 25369 59220 38864 +23 41 1 26176 59220 39241 +24 41 1 26761 59220 39726 +25 41 1 27633 59220 40269 +26 41 1 28299 59220 40574 +27 41 1 28931 59220 41118 +28 41 1 29801 59220 41564 +29 41 1 30392 59220 41971 +30 41 1 31147 59220 42361 +31 41 1 32028 59220 42771 +32 41 1 32640 59220 43417 +33 41 1 33528 59220 43794 +34 41 1 34335 59220 44261 +35 41 1 34921 59220 44623 +36 41 1 35793 59220 45124 +37 41 1 36459 59220 45630 +38 41 1 37091 59220 45995 +39 41 1 37923 59220 46394 +40 41 1 38774 59220 46756 +41 41 1 39359 59220 47365 +42 41 1 40166 59220 47760 +43 41 1 41054 59220 48200 +44 41 1 41666 59220 48840 +45 41 1 42547 59220 49195 +46 41 1 43303 59220 49646 +47 41 1 43893 59220 49997 +48 41 1 44763 59220 50517 +49 41 1 45396 59220 50992 +50 41 1 46061 59220 51375 +51 41 1 46933 59220 51768 +52 41 1 47519 59220 52150 +53 41 1 48325 59220 52778 +54 41 1 49213 59220 53168 +55 41 1 49825 59220 53633 +56 41 1 50700 59220 54242 +57 41 1 51545 59220 54586 +58 41 1 52253 59220 55022 +59 41 1 52860 59220 55366 +60 41 1 53729 59220 55907 +61 41 1 54336 59220 56354 +62 41 1 55042 59220 56760 +63 41 1 55918 59220 57150 +0 42 1 8775 60307 29493 +1 42 1 9656 60307 29815 +2 42 1 10409 60307 30338 +3 42 1 11000 60307 30851 +4 42 1 11841 60307 31206 +5 42 1 12675 60307 31613 +6 42 1 13282 60307 31969 +7 42 1 13990 60307 32569 +8 42 1 14866 60307 32939 +9 42 1 15459 60307 33429 +10 42 1 16323 60307 33798 +11 42 1 17185 60307 34300 +12 42 1 17777 60307 34891 +13 42 1 18652 60307 35199 +14 42 1 19359 60307 35736 +15 42 1 19966 60307 36213 +16 42 1 20835 60307 36589 +17 42 1 21442 60307 36977 +18 42 1 22149 60307 37354 +19 42 1 23025 60307 37970 +20 42 1 23618 60307 38331 +21 42 1 24481 60307 38846 +22 42 1 25369 60307 39210 +23 42 1 26176 60307 39719 +24 42 1 26761 60307 40274 +25 42 1 27633 60307 40576 +26 42 1 28299 60307 41128 +27 42 1 28931 60307 41571 +28 42 1 29801 60307 41973 +29 42 1 30392 60307 42348 +30 42 1 31147 60307 42751 +31 42 1 32028 60307 43381 +32 42 1 32640 60307 43742 +33 42 1 33528 60307 44259 +34 42 1 34335 60307 44606 +35 42 1 34921 60307 45126 +36 42 1 35793 60307 45641 +37 42 1 36459 60307 45995 +38 42 1 37091 60307 46402 +39 42 1 37923 60307 46758 +40 42 1 38774 60307 47358 +41 42 1 39359 60307 47733 +42 42 1 40166 60307 48164 +43 42 1 41054 60307 48805 +44 42 1 41666 60307 49158 +45 42 1 42547 60307 49655 +46 42 1 43303 60307 49990 +47 42 1 43893 60307 50525 +48 42 1 44763 60307 51002 +49 42 1 45396 60307 51378 +50 42 1 46061 60307 51767 +51 42 1 46933 60307 52143 +52 42 1 47519 60307 52758 +53 42 1 48325 60307 53122 +54 42 1 49213 60307 53596 +55 42 1 49825 60307 54225 +56 42 1 50700 60307 54562 +57 42 1 51545 60307 55037 +58 42 1 52253 60307 55368 +59 42 1 52860 60307 55917 +60 42 1 53729 60307 56361 +61 42 1 54336 60307 56762 +62 42 1 55042 60307 57137 +63 42 1 55918 60307 57540 +0 43 1 8775 61420 29797 +1 43 1 9656 61420 30342 +2 43 1 10409 61420 30869 +3 43 1 11000 61420 31207 +4 43 1 11841 61420 31641 +5 43 1 12675 61420 31984 +6 43 1 13282 61420 32581 +7 43 1 13990 61420 32939 +8 43 1 14866 61420 33425 +9 43 1 15459 61420 33763 +10 43 1 16323 61420 34274 +11 43 1 17185 61420 34885 +12 43 1 17777 61420 35194 +13 43 1 18652 61420 35749 +14 43 1 19359 61420 36235 +15 43 1 19966 61420 36595 +16 43 1 20835 61420 37000 +17 43 1 21442 61420 37365 +18 43 1 22149 61420 37973 +19 43 1 23025 61420 38315 +20 43 1 23618 61420 38832 +21 43 1 24481 61420 39174 +22 43 1 25369 61420 39710 +23 43 1 26176 61420 40281 +24 43 1 26761 61420 40582 +25 43 1 27633 61420 41146 +26 43 1 28299 61420 41594 +27 43 1 28931 61420 41981 +28 43 1 29801 61420 42361 +29 43 1 30392 61420 42754 +30 43 1 31147 61420 43371 +31 43 1 32028 61420 43706 +32 43 1 32640 61420 44249 +33 43 1 33528 61420 44586 +34 43 1 34335 61420 45130 +35 43 1 34921 61420 45658 +36 43 1 35793 61420 45996 +37 43 1 36459 61420 46430 +38 43 1 37091 61420 46773 +39 43 1 37923 61420 47370 +40 43 1 38774 61420 47733 +41 43 1 39359 61420 48155 +42 43 1 40166 61420 48779 +43 43 1 41054 61420 49117 +44 43 1 41666 61420 49662 +45 43 1 42547 61420 49984 +46 43 1 43303 61420 50537 +47 43 1 43893 61420 51025 +48 43 1 44763 61420 51384 +49 43 1 45396 61420 51790 +50 43 1 46061 61420 52154 +51 43 1 46933 61420 52761 +52 43 1 47519 61420 53108 +53 43 1 48325 61420 53571 +54 43 1 49213 61420 54200 +55 43 1 49825 61420 54534 +56 43 1 50700 61420 55056 +57 43 1 51545 61420 55372 +58 43 1 52253 61420 55935 +59 43 1 52860 61420 56384 +60 43 1 53729 61420 56770 +61 43 1 54336 61420 57150 +62 43 1 55042 61420 57543 +63 43 1 55918 61420 58160 +0 44 1 8775 62571 30348 +1 44 1 9656 62571 30893 +2 44 1 10409 62571 31215 +3 44 1 11000 62571 31686 +4 44 1 11841 62571 32015 +5 44 1 12675 62571 32614 +6 44 1 13282 62571 32970 +7 44 1 13990 62571 33437 +8 44 1 14866 62571 33766 +9 44 1 15459 62571 34263 +10 44 1 16323 62571 34876 +11 44 1 17185 62571 35194 +12 44 1 17777 62571 35768 +13 44 1 18652 62571 36270 +14 44 1 19359 62571 36608 +15 44 1 19966 62571 37045 +16 44 1 20835 62571 37396 +17 44 1 21442 62571 38000 +18 44 1 22149 62571 38334 +19 44 1 23025 62571 38837 +20 44 1 23618 62571 39157 +21 44 1 24481 62571 39700 +22 44 1 25369 62571 40290 +23 44 1 26176 62571 40594 +24 44 1 26761 62571 41175 +25 44 1 27633 62571 41634 +26 44 1 28299 62571 41998 +27 44 1 28931 62571 42401 +28 44 1 29801 62571 42781 +29 44 1 30392 62571 43389 +30 44 1 31147 62571 43709 +31 44 1 32028 62571 44244 +32 44 1 32640 62571 44568 +33 44 1 33528 62571 45136 +34 44 1 34335 62571 45682 +35 44 1 34921 62571 46004 +36 44 1 35793 62571 46476 +37 44 1 36459 62571 46804 +38 44 1 37091 62571 47403 +39 44 1 37923 62571 47762 +40 44 1 38774 62571 48173 +41 44 1 39359 62571 48784 +42 44 1 40166 62571 49100 +43 44 1 41054 62571 49662 +44 44 1 41666 62571 49982 +45 44 1 42547 62571 50557 +46 44 1 43303 62571 51059 +47 44 1 43893 62571 51397 +48 44 1 44763 62571 51835 +49 44 1 45396 62571 52185 +50 44 1 46061 62571 52789 +51 44 1 46933 62571 53126 +52 44 1 47519 62571 53577 +53 44 1 48325 62571 54189 +54 44 1 49213 62571 54512 +55 44 1 49825 62571 55075 +56 44 1 50700 62571 55383 +57 44 1 51545 62571 55963 +58 44 1 52253 62571 56424 +59 44 1 52860 62571 56787 +60 44 1 53729 62571 57191 +61 44 1 54336 62571 57570 +62 44 1 55042 62571 58178 +63 44 1 55918 62571 58501 +0 45 0 0 0 0 +1 45 0 0 0 0 +2 45 0 0 0 0 +3 45 0 0 0 0 +4 45 0 0 0 0 +5 45 0 0 0 0 +6 45 0 0 0 0 +7 45 0 0 0 0 +8 45 0 0 0 0 +9 45 0 0 0 0 +10 45 0 0 0 0 +11 45 0 0 0 0 +12 45 0 0 0 0 +13 45 0 0 0 0 +14 45 0 0 0 0 +15 45 0 0 0 0 +16 45 0 0 0 0 +17 45 0 0 0 0 +18 45 0 0 0 0 +19 45 0 0 0 0 +20 45 0 0 0 0 +21 45 0 0 0 0 +22 45 0 0 0 0 +23 45 0 0 0 0 +24 45 0 0 0 0 +25 45 0 0 0 0 +26 45 0 0 0 0 +27 45 0 0 0 0 +28 45 0 0 0 0 +29 45 0 0 0 0 +30 45 0 0 0 0 +31 45 0 0 0 0 +32 45 0 0 0 0 +33 45 0 0 0 0 +34 45 0 0 0 0 +35 45 0 0 0 0 +36 45 0 0 0 0 +37 45 0 0 0 0 +38 45 0 0 0 0 +39 45 0 0 0 0 +40 45 0 0 0 0 +41 45 0 0 0 0 +42 45 0 0 0 0 +43 45 0 0 0 0 +44 45 0 0 0 0 +45 45 0 0 0 0 +46 45 0 0 0 0 +47 45 0 0 0 0 +48 45 0 0 0 0 +49 45 0 0 0 0 +50 45 0 0 0 0 +51 45 0 0 0 0 +52 45 0 0 0 0 +53 45 0 0 0 0 +54 45 0 0 0 0 +55 45 0 0 0 0 +56 45 0 0 0 0 +57 45 0 0 0 0 +58 45 0 0 0 0 +59 45 0 0 0 0 +60 45 0 0 0 0 +61 45 0 0 0 0 +62 45 0 0 0 0 +63 45 0 0 0 0 +0 46 0 0 0 0 +1 46 0 0 0 0 +2 46 0 0 0 0 +3 46 0 0 0 0 +4 46 0 0 0 0 +5 46 0 0 0 0 +6 46 0 0 0 0 +7 46 0 0 0 0 +8 46 0 0 0 0 +9 46 0 0 0 0 +10 46 0 0 0 0 +11 46 0 0 0 0 +12 46 0 0 0 0 +13 46 0 0 0 0 +14 46 0 0 0 0 +15 46 0 0 0 0 +16 46 0 0 0 0 +17 46 0 0 0 0 +18 46 0 0 0 0 +19 46 0 0 0 0 +20 46 0 0 0 0 +21 46 0 0 0 0 +22 46 0 0 0 0 +23 46 0 0 0 0 +24 46 0 0 0 0 +25 46 0 0 0 0 +26 46 0 0 0 0 +27 46 0 0 0 0 +28 46 0 0 0 0 +29 46 0 0 0 0 +30 46 0 0 0 0 +31 46 0 0 0 0 +32 46 0 0 0 0 +33 46 0 0 0 0 +34 46 0 0 0 0 +35 46 0 0 0 0 +36 46 0 0 0 0 +37 46 0 0 0 0 +38 46 0 0 0 0 +39 46 0 0 0 0 +40 46 0 0 0 0 +41 46 0 0 0 0 +42 46 0 0 0 0 +43 46 0 0 0 0 +44 46 0 0 0 0 +45 46 0 0 0 0 +46 46 0 0 0 0 +47 46 0 0 0 0 +48 46 0 0 0 0 +49 46 0 0 0 0 +50 46 0 0 0 0 +51 46 0 0 0 0 +52 46 0 0 0 0 +53 46 0 0 0 0 +54 46 0 0 0 0 +55 46 0 0 0 0 +56 46 0 0 0 0 +57 46 0 0 0 0 +58 46 0 0 0 0 +59 46 0 0 0 0 +60 46 0 0 0 0 +61 46 0 0 0 0 +62 46 0 0 0 0 +63 46 0 0 0 0 +0 47 0 0 0 0 +1 47 0 0 0 0 +2 47 0 0 0 0 +3 47 0 0 0 0 +4 47 0 0 0 0 +5 47 0 0 0 0 +6 47 0 0 0 0 +7 47 0 0 0 0 +8 47 0 0 0 0 +9 47 0 0 0 0 +10 47 0 0 0 0 +11 47 0 0 0 0 +12 47 0 0 0 0 +13 47 0 0 0 0 +14 47 0 0 0 0 +15 47 0 0 0 0 +16 47 0 0 0 0 +17 47 0 0 0 0 +18 47 0 0 0 0 +19 47 0 0 0 0 +20 47 0 0 0 0 +21 47 0 0 0 0 +22 47 0 0 0 0 +23 47 0 0 0 0 +24 47 0 0 0 0 +25 47 0 0 0 0 +26 47 0 0 0 0 +27 47 0 0 0 0 +28 47 0 0 0 0 +29 47 0 0 0 0 +30 47 0 0 0 0 +31 47 0 0 0 0 +32 47 0 0 0 0 +33 47 0 0 0 0 +34 47 0 0 0 0 +35 47 0 0 0 0 +36 47 0 0 0 0 +37 47 0 0 0 0 +38 47 0 0 0 0 +39 47 0 0 0 0 +40 47 0 0 0 0 +41 47 0 0 0 0 +42 47 0 0 0 0 +43 47 0 0 0 0 +44 47 0 0 0 0 +45 47 0 0 0 0 +46 47 0 0 0 0 +47 47 0 0 0 0 +48 47 0 0 0 0 +49 47 0 0 0 0 +50 47 0 0 0 0 +51 47 0 0 0 0 +52 47 0 0 0 0 +53 47 0 0 0 0 +54 47 0 0 0 0 +55 47 0 0 0 0 +56 47 0 0 0 0 +57 47 0 0 0 0 +58 47 0 0 0 0 +59 47 0 0 0 0 +60 47 0 0 0 0 +61 47 0 0 0 0 +62 47 0 0 0 0 +63 47 0 0 0 0 diff --git a/tests/golden/stitch_dist_bic.txt b/tests/golden/stitch_dist_bic.txt new file mode 100644 index 0000000..619e8de --- /dev/null +++ b/tests/golden/stitch_dist_bic.txt @@ -0,0 +1,3072 @@ +0 0 1 9341 15725 11853 +1 0 1 10134 15687 12397 +2 0 1 10658 15640 12829 +3 0 1 11430 15583 13100 +4 0 1 12216 15513 13577 +5 0 1 12994 15423 13818 +6 0 1 52579 33142 16392 +7 0 1 49018 31796 16229 +8 0 1 46273 30728 16169 +9 0 1 43856 29656 16126 +10 0 1 41397 28579 16183 +11 0 1 39140 27495 16214 +12 0 1 37121 26406 16406 +13 0 1 35168 25309 16609 +14 0 1 33412 24204 16766 +15 0 1 31818 23091 17162 +16 0 1 30405 21968 17501 +17 0 1 29150 20837 17811 +18 0 1 28012 19695 18291 +19 0 1 27165 18543 18683 +20 0 1 26396 17381 19382 +21 0 1 25753 16208 19905 +22 0 1 25458 15025 20507 +23 0 1 25386 13853 21133 +24 0 1 25292 12725 21818 +25 0 1 26520 13410 22122 +26 0 1 27892 14109 22175 +27 0 1 28364 13916 22650 +28 0 1 29100 13871 22985 +29 0 1 29947 13833 23363 +30 0 1 30510 13801 23953 +31 0 1 31325 13772 24334 +32 0 1 32164 13743 24711 +33 0 1 32734 13713 25310 +34 0 1 33580 13679 25702 +35 0 1 34341 13641 26041 +36 0 1 34904 13595 26483 +37 0 1 35754 13542 26805 +38 0 1 36441 13480 27368 +39 0 1 37027 13408 27719 +40 0 1 37848 13324 28055 +41 0 1 38664 13229 28421 +42 0 1 39276 13142 28744 +43 0 1 39918 13068 29297 +44 0 1 40765 13005 29628 +45 0 1 41341 12952 29975 +46 0 1 42030 12907 30539 +47 0 1 42870 12869 30880 +48 0 1 43424 12836 31301 +49 0 1 44156 12808 31653 +50 0 1 44984 12782 32011 +51 0 1 45528 12757 32584 +52 0 1 46294 12733 32969 +53 0 1 47107 12708 33334 +54 0 1 47647 12681 33763 +55 0 1 48438 12650 34075 +56 0 1 49233 12614 34640 +57 0 1 49767 12571 35066 +58 0 1 50566 12520 35341 +59 0 1 51355 12459 35879 +60 0 1 52115 12387 36318 +61 0 1 52644 12301 36559 +62 0 1 53447 12202 37031 +63 0 1 54189 12111 37264 +0 1 1 9373 16761 12443 +1 1 1 10161 16725 12849 +2 1 1 10686 16683 13148 +3 1 1 11466 16630 13613 +4 1 1 12251 16565 13872 +5 1 1 13994 16895 14428 +6 1 1 52989 33278 16399 +7 1 1 50673 32647 16321 +8 1 1 50089 32745 16328 +9 1 1 49907 32941 16340 +10 1 1 49542 33214 16342 +11 1 1 49296 33543 16340 +12 1 1 49281 33905 16310 +13 1 1 48900 34275 16265 +14 1 1 48563 34629 16222 +15 1 1 48316 34941 16135 +16 1 1 47579 35183 16056 +17 1 1 46888 35329 15993 +18 1 1 46167 35352 15925 +19 1 1 44936 35224 15897 +20 1 1 43818 34918 15875 +21 1 1 42583 34409 15939 +22 1 1 40999 33671 16075 +23 1 1 39352 32681 16309 +24 1 1 39452 33974 15778 +25 1 1 30100 19729 20669 +26 1 1 27362 14347 22909 +27 1 1 28386 14958 22997 +28 1 1 29144 14908 23390 +29 1 1 29991 14868 23977 +30 1 1 30562 14834 24332 +31 1 1 31401 14804 24734 +32 1 1 32219 14775 25334 +33 1 1 32785 14746 25707 +34 1 1 33635 14714 26073 +35 1 1 34370 14678 26497 +36 1 1 34940 14637 26847 +37 1 1 35791 14588 27412 +38 1 1 36453 14531 27755 +39 1 1 37056 14464 28107 +40 1 1 37874 14386 28470 +41 1 1 38694 14296 28813 +42 1 1 39287 14204 29359 +43 1 1 39956 14124 29660 +44 1 1 40802 14056 30027 +45 1 1 41365 13998 30591 +46 1 1 42086 13949 30901 +47 1 1 42922 13908 31349 +48 1 1 43471 13873 31676 +49 1 1 44236 13842 32061 +50 1 1 45057 13815 32643 +51 1 1 45602 13789 32998 +52 1 1 46401 13765 33397 +53 1 1 47202 13741 33803 +54 1 1 47742 13715 34146 +55 1 1 48550 13685 34721 +56 1 1 49317 13652 35110 +57 1 1 49851 13612 35415 +58 1 1 50659 13565 35973 +59 1 1 51446 13508 36378 +60 1 1 52178 13441 36647 +61 1 1 52713 13361 37107 +62 1 1 53529 13266 37350 +63 1 1 54242 13169 37861 +0 2 1 9408 17797 12864 +1 2 1 10191 17764 13194 +2 2 1 10718 17725 13638 +3 2 1 11505 17678 13920 +4 2 1 12246 17602 14459 +5 2 1 15035 18329 14960 +6 2 1 53784 33556 16410 +7 2 1 50624 32640 16320 +8 2 1 49865 32640 16320 +9 2 1 49329 32640 16320 +10 2 1 48510 32640 16320 +11 2 1 47774 32640 16320 +12 2 1 47229 32640 16320 +13 2 1 46398 32640 16320 +14 2 1 45692 32640 16320 +15 2 1 45131 32640 16320 +16 2 1 44291 32640 16320 +17 2 1 43624 32640 16320 +18 2 1 43037 32640 16320 +19 2 1 42191 32640 16320 +20 2 1 41565 32640 16320 +21 2 1 40940 32640 16320 +22 2 1 40127 32640 16320 +23 2 1 39303 32640 16320 +24 2 1 40282 35078 15339 +25 2 1 30092 20480 21130 +26 2 1 27437 15498 23229 +27 2 1 28414 16005 23425 +28 2 1 29195 15950 24004 +29 2 1 30042 15906 24332 +30 2 1 30623 15869 24758 +31 2 1 31471 15838 25355 +32 2 1 32264 15809 25709 +33 2 1 32829 15781 26102 +34 2 1 33682 15751 26504 +35 2 1 34390 15717 26885 +36 2 1 34971 15679 27446 +37 2 1 35824 15635 27784 +38 2 1 36462 15583 28149 +39 2 1 37085 15522 28509 +40 2 1 37901 15451 28884 +41 2 1 38726 15369 29429 +42 2 1 39303 15275 29705 +43 2 1 40002 15188 30094 +44 2 1 40846 15114 30648 +45 2 1 41402 15051 30932 +46 2 1 42155 14997 31402 +47 2 1 42986 14951 31705 +48 2 1 43537 14912 32122 +49 2 1 44334 14879 32702 +50 2 1 45147 14850 33031 +51 2 1 45695 14824 33460 +52 2 1 46510 14799 33834 +53 2 1 47281 14775 34211 +54 2 1 47821 14749 34784 +55 2 1 48640 14722 35140 +56 2 1 49376 14690 35482 +57 2 1 49916 14654 36046 +58 2 1 50729 14610 36421 +59 2 1 51518 14558 36721 +60 2 1 52220 14496 37161 +61 2 1 52768 14422 37437 +62 2 1 53592 14334 37969 +63 2 1 54275 14233 38352 +0 3 1 9447 18834 13239 +1 3 1 10224 18803 13655 +2 3 1 10750 18767 13963 +3 3 1 11542 18725 14519 +4 3 1 12189 18621 14931 +5 3 1 16117 19722 15258 +6 3 1 54505 33763 16416 +7 3 1 50591 32640 16320 +8 3 1 49843 32640 16320 +9 3 1 49305 32640 16320 +10 3 1 48482 32640 16320 +11 3 1 47758 32640 16320 +12 3 1 47208 32640 16320 +13 3 1 46375 32640 16320 +14 3 1 45682 32640 16320 +15 3 1 45114 32640 16320 +16 3 1 44272 32640 16320 +17 3 1 43618 32640 16320 +18 3 1 43022 32640 16320 +19 3 1 42175 32640 16320 +20 3 1 41560 32640 16320 +21 3 1 40922 32640 16320 +22 3 1 40110 32640 16320 +23 3 1 39283 32640 16320 +24 3 1 40218 34843 15328 +25 3 1 29610 20587 21607 +26 3 1 27617 16789 23622 +27 3 1 28448 17058 24038 +28 3 1 29254 16997 24338 +29 3 1 30101 16948 24787 +30 3 1 30680 16908 25375 +31 3 1 31531 16874 25708 +32 3 1 32299 16845 26130 +33 3 1 32867 16817 26506 +34 3 1 33721 16788 26918 +35 3 1 34405 16757 27471 +36 3 1 34999 16722 27806 +37 3 1 35853 16682 28183 +38 3 1 36471 16635 28538 +39 3 1 37117 16581 28947 +40 3 1 37931 16517 29487 +41 3 1 38761 16442 29759 +42 3 1 39328 16356 30178 +43 3 1 40058 16263 30715 +44 3 1 40899 16181 30976 +45 3 1 41454 16111 31463 +46 3 1 42241 16051 31743 +47 3 1 43067 16000 32193 +48 3 1 43624 15957 32763 +49 3 1 44444 15920 33066 +50 3 1 45229 15888 33518 +51 3 1 45773 15860 33859 +52 3 1 46597 15834 34272 +53 3 1 47338 15810 34834 +54 3 1 47883 15785 35160 +55 3 1 48710 15759 35543 +56 3 1 49416 15730 36101 +57 3 1 49968 15696 36451 +58 3 1 50782 15656 36784 +59 3 1 51574 15609 37197 +60 3 1 52246 15552 37513 +61 3 1 52811 15484 38060 +62 3 1 53642 15403 38411 +63 3 1 54296 15306 38714 +0 4 1 9486 19841 13667 +1 4 1 10255 19816 14004 +2 4 1 10781 19788 14568 +3 4 1 11578 19755 14957 +4 4 1 12078 19616 15253 +5 4 1 17244 21064 15765 +6 4 1 55148 33902 16387 +7 4 1 50560 32640 16320 +8 4 1 49823 32640 16320 +9 4 1 49282 32640 16320 +10 4 1 48456 32640 16320 +11 4 1 47743 32640 16320 +12 4 1 47189 32640 16320 +13 4 1 46353 32640 16320 +14 4 1 45673 32640 16320 +15 4 1 45098 32640 16320 +16 4 1 44255 32640 16320 +17 4 1 43612 32640 16320 +18 4 1 43006 32640 16320 +19 4 1 42159 32640 16320 +20 4 1 41555 32640 16320 +21 4 1 40903 32640 16320 +22 4 1 40092 32640 16320 +23 4 1 39262 32640 16320 +24 4 1 40121 34578 15307 +25 4 1 29151 20828 22335 +26 4 1 27771 18025 24171 +27 4 1 28490 18121 24358 +28 4 1 29321 18052 24825 +29 4 1 30156 17996 25398 +30 4 1 30729 17951 25710 +31 4 1 31583 17914 26159 +32 4 1 32326 17883 26507 +33 4 1 32899 17854 26951 +34 4 1 33755 17826 27489 +35 4 1 34415 17797 27824 +36 4 1 35026 17766 28209 +37 4 1 35881 17730 28560 +38 4 1 36481 17688 29001 +39 4 1 37153 17640 29529 +40 4 1 37968 17583 29802 +41 4 1 38803 17516 30260 +42 4 1 39364 17439 30787 +43 4 1 40127 17350 31040 +44 4 1 40965 17259 31533 +45 4 1 41525 17180 31795 +46 4 1 42345 17113 32277 +47 4 1 43154 17056 32822 +48 4 1 43706 17007 33104 +49 4 1 44534 16966 33573 +50 4 1 45288 16930 33883 +51 4 1 45835 16899 34334 +52 4 1 46667 16872 34873 +53 4 1 47376 16846 35175 +54 4 1 47932 16821 35600 +55 4 1 48765 16796 36141 +56 4 1 49441 16769 36475 +57 4 1 50010 16738 36835 +58 4 1 50822 16702 37220 +59 4 1 51619 16659 37583 +60 4 1 52263 16607 38129 +61 4 1 52849 16545 38457 +62 4 1 53683 16471 38795 +63 4 1 54309 16383 39165 +0 5 1 9524 21034 14046 +1 5 1 10284 20993 14611 +2 5 1 10811 20949 14972 +3 5 1 11612 20900 15304 +4 5 1 11911 20694 15745 +5 5 1 18416 22420 16019 +6 5 1 55705 33972 16366 +7 5 1 50530 32640 16320 +8 5 1 49805 32640 16320 +9 5 1 49260 32640 16320 +10 5 1 48431 32640 16320 +11 5 1 47731 32640 16320 +12 5 1 47171 32640 16320 +13 5 1 46333 32640 16320 +14 5 1 45665 32640 16320 +15 5 1 45082 32640 16320 +16 5 1 44238 32640 16320 +17 5 1 43608 32640 16320 +18 5 1 42990 32640 16320 +19 5 1 42142 32640 16320 +20 5 1 41549 32640 16320 +21 5 1 40882 32640 16320 +22 5 1 40070 32640 16320 +23 5 1 39237 32640 16320 +24 5 1 39972 34269 15332 +25 5 1 28692 21167 23159 +26 5 1 27901 19221 24438 +27 5 1 28540 19194 24878 +28 5 1 29389 19115 25430 +29 5 1 30202 19051 25720 +30 5 1 30771 19000 26193 +31 5 1 31627 18958 26511 +32 5 1 32347 18924 26984 +33 5 1 32928 18894 27504 +34 5 1 33785 18866 27839 +35 5 1 34424 18838 28230 +36 5 1 35054 18809 28575 +37 5 1 35910 18777 29047 +38 5 1 36495 18740 29557 +39 5 1 37196 18698 29837 +40 5 1 38014 18648 30329 +41 5 1 38853 18589 30841 +42 5 1 39414 18521 31108 +43 5 1 40211 18442 31610 +44 5 1 41046 18350 31866 +45 5 1 41612 18262 32373 +46 5 1 42443 18185 32884 +47 5 1 43221 18120 33151 +48 5 1 43772 18064 33628 +49 5 1 44606 18017 33913 +50 5 1 45329 17977 34397 +51 5 1 45885 17942 34906 +52 5 1 46721 17912 35192 +53 5 1 47401 17885 35656 +54 5 1 47972 17859 36169 +55 5 1 48810 17834 36497 +56 5 1 49457 17808 36876 +57 5 1 50046 17779 37235 +58 5 1 50854 17746 37646 +59 5 1 51657 17708 38178 +60 5 1 52274 17661 38493 +61 5 1 52884 17606 38863 +62 5 1 53719 17539 39211 +63 5 1 54320 17458 39560 +0 6 1 9561 22231 14650 +1 6 1 10311 22207 14984 +2 6 1 10840 22181 15349 +3 6 1 11644 22152 15757 +4 6 1 11688 21910 16096 +5 6 1 19637 23861 16545 +6 6 1 56171 33960 16300 +7 6 1 50502 32640 16320 +8 6 1 49789 32640 16320 +9 6 1 49239 32640 16320 +10 6 1 48407 32640 16320 +11 6 1 47720 32640 16320 +12 6 1 47153 32640 16320 +13 6 1 46314 32640 16320 +14 6 1 45659 32640 16320 +15 6 1 45067 32640 16320 +16 6 1 44221 32640 16320 +17 6 1 43603 32640 16320 +18 6 1 42973 32640 16320 +19 6 1 42126 32640 16320 +20 6 1 41541 32640 16320 +21 6 1 40858 32640 16320 +22 6 1 40046 32640 16320 +23 6 1 39210 32640 16320 +24 6 1 39764 33923 15440 +25 6 1 28232 21611 23745 +26 6 1 28001 20380 24963 +27 6 1 28596 20265 25476 +28 6 1 29450 20164 25745 +29 6 1 30239 20083 26235 +30 6 1 30808 20020 26524 +31 6 1 31666 19970 27023 +32 6 1 32361 19931 27520 +33 6 1 32954 19900 27852 +34 6 1 33812 19873 28249 +35 6 1 34431 19849 28587 +36 6 1 35085 19826 29088 +37 6 1 35941 19801 29573 +38 6 1 36516 19773 29864 +39 6 1 37248 19740 30387 +40 6 1 38072 19700 30877 +41 6 1 38914 19653 31168 +42 6 1 39482 19596 31670 +43 6 1 40312 19529 31944 +44 6 1 41128 19449 32480 +45 6 1 41686 19357 32954 +46 6 1 42523 19270 33212 +47 6 1 43269 19195 33687 +48 6 1 43824 19130 33954 +49 6 1 44664 19076 34467 +50 6 1 45356 19029 34937 +51 6 1 45925 18989 35215 +52 6 1 46766 18955 35714 +53 6 1 47417 18926 36190 +54 6 1 48007 18899 36521 +55 6 1 48847 18873 36909 +56 6 1 49468 18847 37248 +57 6 1 50080 18820 37706 +58 6 1 50884 18790 38210 +59 6 1 51692 18755 38526 +60 6 1 52284 18714 38916 +61 6 1 52920 18664 39246 +62 6 1 53754 18604 39648 +63 6 1 54332 18532 40175 +0 7 1 9597 23241 14996 +1 7 1 10336 23213 15392 +2 7 1 10867 23186 15766 +3 7 1 11674 23157 16129 +4 7 1 11411 22862 16716 +5 7 1 20908 25016 16876 +6 7 1 56540 33929 16237 +7 7 1 50476 32640 16320 +8 7 1 49775 32640 16320 +9 7 1 49219 32640 16320 +10 7 1 48385 32640 16320 +11 7 1 47711 32640 16320 +12 7 1 47136 32640 16320 +13 7 1 46295 32640 16320 +14 7 1 45653 32640 16320 +15 7 1 45051 32640 16320 +16 7 1 44205 32640 16320 +17 7 1 43598 32640 16320 +18 7 1 42955 32640 16320 +19 7 1 42108 32640 16320 +20 7 1 41531 32640 16320 +21 7 1 40831 32640 16320 +22 7 1 40017 32640 16320 +23 7 1 39179 32640 16320 +24 7 1 39491 33534 15582 +25 7 1 27772 22377 24656 +26 7 1 28067 21766 25540 +27 7 1 28647 21633 25795 +28 7 1 29502 21511 26289 +29 7 1 30268 21410 26554 +30 7 1 30840 21325 27072 +31 7 1 31699 21255 27542 +32 7 1 32372 21195 27870 +33 7 1 32980 21144 28270 +34 7 1 33838 21099 28600 +35 7 1 34441 21057 29125 +36 7 1 35120 21017 29581 +37 7 1 35977 20977 29886 +38 7 1 36546 20935 30435 +39 7 1 37311 20890 30898 +40 7 1 38144 20840 31221 +41 7 1 38989 20784 31710 +42 7 1 39565 20721 32008 +43 7 1 40406 20648 32573 +44 7 1 41189 20565 33023 +45 7 1 41746 20471 33291 +46 7 1 42587 20365 33755 +47 7 1 43302 20268 34015 +48 7 1 43867 20185 34546 +49 7 1 44710 20115 34973 +50 7 1 45373 20056 35250 +51 7 1 45960 20007 35776 +52 7 1 46803 19966 36209 +53 7 1 47428 19932 36551 +54 7 1 48040 19904 36936 +55 7 1 48881 19879 37264 +56 7 1 49478 19856 37763 +57 7 1 50115 19833 38231 +58 7 1 50915 19809 38559 +59 7 1 51726 19782 38957 +60 7 1 52298 19749 39275 +61 7 1 52961 19709 39729 +62 7 1 53791 19660 40237 +63 7 1 54350 19598 40501 +0 8 1 9631 24283 15435 +1 8 1 10358 24254 15774 +2 8 1 10893 24227 16174 +3 8 1 11702 24199 16727 +4 8 1 11081 23856 17112 +5 8 1 22230 26143 17078 +6 8 1 56807 33850 16190 +7 8 1 50451 32640 16320 +8 8 1 49763 32640 16320 +9 8 1 49201 32640 16320 +10 8 1 48364 32640 16320 +11 8 1 47703 32640 16320 +12 8 1 47120 32640 16320 +13 8 1 46277 32640 16320 +14 8 1 45648 32640 16320 +15 8 1 45034 32640 16320 +16 8 1 44188 32640 16320 +17 8 1 43592 32640 16320 +18 8 1 42935 32640 16320 +19 8 1 42088 32640 16320 +20 8 1 41519 32640 16320 +21 8 1 40800 32640 16320 +22 8 1 39984 32640 16320 +23 8 1 39143 32640 16320 +24 8 1 39143 33163 15816 +25 8 1 27312 22969 25561 +26 8 1 28117 22859 25862 +27 8 1 28691 22755 26360 +28 8 1 29549 22638 26610 +29 8 1 30291 22538 27140 +30 8 1 30869 22458 27578 +31 8 1 31728 22395 27896 +32 8 1 32380 22346 28299 +33 8 1 33005 22308 28619 +34 8 1 33865 22277 29163 +35 8 1 34454 22250 29586 +36 8 1 35161 22227 29907 +37 8 1 36019 22204 30475 +38 8 1 36588 22180 30906 +39 8 1 37387 22152 31267 +40 8 1 38234 22120 31734 +41 8 1 39070 22082 32061 +42 8 1 39638 22036 32646 +43 8 1 40483 21981 33071 +44 8 1 41233 21915 33364 +45 8 1 41794 21836 33823 +46 8 1 42639 21743 34098 +47 8 1 43324 21636 34639 +48 8 1 43903 21536 35019 +49 8 1 44748 21448 35304 +50 8 1 45385 21372 35845 +51 8 1 45993 21305 36233 +52 8 1 46836 21246 36591 +53 8 1 47437 21194 36962 +54 8 1 48075 21148 37289 +55 8 1 48914 21105 37820 +56 8 1 49491 21065 38244 +57 8 1 50155 21026 38596 +58 8 1 50951 20986 38988 +59 8 1 51764 20944 39305 +60 8 1 52319 20897 39804 +61 8 1 53010 20844 40282 +62 8 1 53834 20784 40548 +63 8 1 54378 20713 41017 +0 9 1 9663 25328 15787 +1 9 1 10378 25297 16222 +2 9 1 10918 25268 16758 +3 9 1 11728 25241 17064 +4 9 1 10702 24859 17554 +5 9 1 23606 27207 17511 +6 9 1 56967 33736 16100 +7 9 1 50427 32640 16320 +8 9 1 49753 32640 16320 +9 9 1 49182 32640 16320 +10 9 1 48344 32640 16320 +11 9 1 47697 32640 16320 +12 9 1 47103 32640 16320 +13 9 1 46260 32640 16320 +14 9 1 45643 32640 16320 +15 9 1 45017 32640 16320 +16 9 1 44171 32640 16320 +17 9 1 43585 32640 16320 +18 9 1 42912 32640 16320 +19 9 1 42066 32640 16320 +20 9 1 41502 32640 16320 +21 9 1 40765 32640 16320 +22 9 1 39945 32640 16320 +23 9 1 39102 32640 16320 +24 9 1 38712 32791 16151 +25 9 1 26886 23694 26272 +26 9 1 28160 23954 26411 +27 9 1 28731 23867 26686 +28 9 1 29589 23762 27233 +29 9 1 30309 23645 27635 +30 9 1 30894 23547 27939 +31 9 1 31755 23467 28341 +32 9 1 32387 23403 28652 +33 9 1 33032 23351 29206 +34 9 1 33893 23309 29594 +35 9 1 34473 23274 29931 +36 9 1 35210 23244 30511 +37 9 1 36069 23217 30907 +38 9 1 36644 23191 31308 +39 9 1 37478 23165 31744 +40 9 1 38330 23137 32105 +41 9 1 39133 23105 32702 +42 9 1 39697 23068 33101 +43 9 1 40544 23024 33425 +44 9 1 41264 22973 33868 +45 9 1 41834 22912 34178 +46 9 1 42681 22840 34735 +47 9 1 43338 22756 35083 +48 9 1 43935 22660 35383 +49 9 1 44781 22572 35927 +50 9 1 45394 22498 36269 +51 9 1 46026 22436 36643 +52 9 1 46868 22385 36993 +53 9 1 47450 22343 37329 +54 9 1 48114 22308 37879 +55 9 1 48950 22278 38257 +56 9 1 49511 22253 38639 +57 9 1 50203 22229 39012 +58 9 1 50995 22206 39339 +59 9 1 51809 22181 39875 +60 9 1 52351 22152 40313 +61 9 1 53070 22117 40595 +62 9 1 53886 22075 41109 +63 9 1 54419 22021 41577 +0 10 1 9693 26378 16278 +1 10 1 10396 26343 16787 +2 10 1 10941 26312 17073 +3 10 1 11752 26284 17510 +4 10 1 10281 25877 18177 +5 10 1 25037 28205 17698 +6 10 1 57018 33594 16037 +7 10 1 50405 32640 16320 +8 10 1 49744 32640 16320 +9 10 1 49164 32640 16320 +10 10 1 48325 32640 16320 +11 10 1 47691 32640 16320 +12 10 1 47086 32640 16320 +13 10 1 46243 32640 16320 +14 10 1 45638 32640 16320 +15 10 1 44997 32640 16320 +16 10 1 44153 32640 16320 +17 10 1 43576 32640 16320 +18 10 1 42886 32640 16320 +19 10 1 42041 32640 16320 +20 10 1 41481 32640 16320 +21 10 1 40725 32640 16320 +22 10 1 39898 32640 16320 +23 10 1 39055 32640 16320 +24 10 1 38203 32447 16579 +25 10 1 26536 24552 27128 +26 10 1 28195 25040 26743 +27 10 1 28765 24968 27314 +28 10 1 29624 24881 27708 +29 10 1 30322 24777 28008 +30 10 1 30918 24661 28405 +31 10 1 31779 24565 28707 +32 10 1 32395 24487 29263 +33 10 1 33063 24424 29614 +34 10 1 33925 24373 29965 +35 10 1 34500 24331 30548 +36 10 1 35270 24297 30907 +37 10 1 36130 24267 31348 +38 10 1 36717 24240 31746 +39 10 1 37571 24214 32144 +40 10 1 38410 24188 32744 +41 10 1 39180 24160 33118 +42 10 1 39745 24129 33475 +43 10 1 40595 24092 33895 +44 10 1 41284 24049 34245 +45 10 1 41868 23998 34806 +46 10 1 42716 23937 35135 +47 10 1 43348 23864 35477 +48 10 1 43967 23779 36023 +49 10 1 44813 23681 36326 +50 10 1 45405 23591 36712 +51 10 1 46062 23514 37035 +52 10 1 46903 23448 37387 +53 10 1 47468 23393 37944 +54 10 1 48160 23346 38275 +55 10 1 48992 23307 38691 +56 10 1 49540 23273 39035 +57 10 1 50260 23243 39383 +58 10 1 51052 23215 39942 +59 10 1 51863 23188 40337 +60 10 1 52397 23160 40646 +61 10 1 53143 23128 41194 +62 10 1 53949 23091 41623 +63 10 1 54477 23047 41916 +0 11 1 9721 27433 16819 +1 11 1 10410 27393 17091 +2 11 1 10963 27358 17561 +3 11 1 11774 27328 18066 +4 11 1 9826 26915 18579 +5 11 1 26524 29133 17869 +6 11 1 56956 33433 15987 +7 11 1 50383 32640 16320 +8 11 1 49737 32640 16320 +9 11 1 49147 32640 16320 +10 11 1 48306 32640 16320 +11 11 1 47686 32640 16320 +12 11 1 47068 32640 16320 +13 11 1 46225 32640 16320 +14 11 1 45632 32640 16320 +15 11 1 44976 32640 16320 +16 11 1 44132 32640 16320 +17 11 1 43564 32640 16320 +18 11 1 42856 32640 16320 +19 11 1 42013 32640 16320 +20 11 1 41455 32640 16320 +21 11 1 40677 32640 16320 +22 11 1 39844 32640 16320 +23 11 1 39002 32641 16319 +24 11 1 37678 32185 17051 +25 11 1 26261 25519 27788 +26 11 1 28224 26117 27370 +27 11 1 28795 26060 27755 +28 11 1 29655 25989 28075 +29 11 1 30331 25902 28483 +30 11 1 30941 25799 28796 +31 11 1 31803 25686 29342 +32 11 1 32405 25590 29655 +33 11 1 33098 25513 30018 +34 11 1 33962 25450 30594 +35 11 1 34537 25399 30916 +36 11 1 35341 25357 31389 +37 11 1 36203 25322 31746 +38 11 1 36792 25292 32183 +39 11 1 37645 25265 32777 +40 11 1 38475 25239 33127 +41 11 1 39214 25213 33517 +42 11 1 39784 25185 33909 +43 11 1 40636 25154 34302 +44 11 1 41298 25118 34856 +45 11 1 41898 25075 35168 +46 11 1 42748 25024 35557 +47 11 1 43357 24963 36101 +48 11 1 44000 24890 36395 +49 11 1 44845 24804 36798 +50 11 1 45419 24704 37097 +51 11 1 46104 24612 37471 +52 11 1 46941 24533 38019 +53 11 1 47495 24466 38308 +54 11 1 48215 24410 38754 +55 11 1 49041 24363 39063 +56 11 1 49582 24323 39441 +57 11 1 50330 24289 40009 +58 11 1 51122 24259 40359 +59 11 1 51928 24231 40706 +60 11 1 52460 24204 41274 +61 11 1 53231 24174 41662 +62 11 1 54025 24141 41995 +63 11 1 54553 24102 42439 +0 12 1 9747 28495 17121 +1 12 1 10423 28448 17620 +2 12 1 10984 28409 18090 +3 12 1 11795 28375 18405 +4 12 1 9347 27979 19064 +5 12 1 28069 29989 18017 +6 12 1 56783 33262 15937 +7 12 1 50363 32640 16320 +8 12 1 49731 32640 16320 +9 12 1 49129 32640 16320 +10 12 1 48288 32640 16320 +11 12 1 47680 32640 16320 +12 12 1 47049 32640 16320 +13 12 1 46206 32640 16320 +14 12 1 45624 32640 16320 +15 12 1 44952 32640 16320 +16 12 1 44109 32640 16320 +17 12 1 43547 32640 16320 +18 12 1 42821 32640 16320 +19 12 1 41979 32640 16320 +20 12 1 41421 32640 16320 +21 12 1 40622 32640 16320 +22 12 1 39780 32640 16320 +23 12 1 38982 32656 16286 +24 12 1 37143 32013 17594 +25 12 1 26056 26572 28660 +26 12 1 28247 27187 27777 +27 12 1 28823 27140 28124 +28 12 1 29683 27084 28532 +29 12 1 30339 27014 28882 +30 12 1 30964 26931 29435 +31 12 1 31827 26831 29728 +32 12 1 32418 26719 30099 +33 12 1 33139 26624 30656 +34 12 1 34005 26545 30943 +35 12 1 34586 26482 31437 +36 12 1 35425 26429 31755 +37 12 1 36276 26386 32228 +38 12 1 36855 26350 32807 +39 12 1 37705 26319 33134 +40 12 1 38528 26291 33556 +41 12 1 39237 26265 33915 +42 12 1 39818 26239 34353 +43 12 1 40671 26211 34888 +44 12 1 41307 26180 35190 +45 12 1 41928 26144 35624 +46 12 1 42778 26101 36152 +47 12 1 43368 26050 36451 +48 12 1 44037 25989 36869 +49 12 1 44880 25916 37168 +50 12 1 45442 25829 37584 +51 12 1 46154 25727 38109 +52 12 1 46987 25633 38364 +53 12 1 47533 25552 38829 +54 12 1 48282 25484 39104 +55 12 1 49101 25427 39517 +56 12 1 49639 25380 40079 +57 12 1 50413 25340 40388 +58 12 1 51209 25306 40778 +59 12 1 52006 25276 41351 +60 12 1 52540 25247 41698 +61 12 1 53333 25219 42075 +62 12 1 54110 25189 42485 +63 12 1 54637 25154 42803 +0 13 1 9772 29565 17689 +1 13 1 10433 29511 18119 +2 13 1 11004 29466 18434 +3 13 1 11814 29427 18837 +4 13 1 8861 29073 19511 +5 13 1 29673 30766 18119 +6 13 1 56501 33090 15906 +7 13 1 50343 32640 16320 +8 13 1 49726 32640 16320 +9 13 1 49110 32640 16320 +10 13 1 48269 32640 16320 +11 13 1 47674 32640 16320 +12 13 1 47027 32640 16320 +13 13 1 46185 32640 16320 +14 13 1 45613 32640 16320 +15 13 1 44924 32640 16320 +16 13 1 44083 32640 16320 +17 13 1 43526 32640 16320 +18 13 1 42780 32640 16320 +19 13 1 41940 32640 16320 +20 13 1 41379 32640 16320 +21 13 1 40558 32640 16320 +22 13 1 39714 32640 16320 +23 13 1 39006 32681 16212 +24 13 1 36598 31933 18164 +25 13 1 25918 27692 29271 +26 13 1 28265 28249 28163 +27 13 1 28847 28211 28555 +28 13 1 29708 28167 28946 +29 13 1 30346 28112 29497 +30 13 1 30989 28046 29791 +31 13 1 31853 27966 30200 +32 13 1 32437 27871 30743 +33 13 1 33188 27763 31004 +34 13 1 34057 27665 31499 +35 13 1 34649 27585 31784 +36 13 1 35516 27518 32287 +37 13 1 36333 27464 32844 +38 13 1 36907 27418 33148 +39 13 1 37752 27380 33597 +40 13 1 38572 27347 33924 +41 13 1 39252 27318 34402 +42 13 1 39848 27291 34909 +43 13 1 40701 27265 35205 +44 13 1 41316 27237 35681 +45 13 1 41960 27206 36181 +46 13 1 42809 27170 36497 +47 13 1 43384 27128 36918 +48 13 1 44080 27077 37221 +49 13 1 44920 27015 37687 +50 13 1 45473 26942 38199 +51 13 1 46214 26854 38448 +52 13 1 47042 26750 38922 +53 13 1 47585 26653 39168 +54 13 1 48361 26571 39616 +55 13 1 49172 26502 40156 +56 13 1 49713 26445 40431 +57 13 1 50511 26397 40865 +58 13 1 51308 26357 41423 +59 13 1 52086 26322 41733 +60 13 1 52618 26292 42147 +61 13 1 53422 26263 42516 +62 13 1 54175 26234 42878 +63 13 1 54705 26203 43441 +0 14 1 9795 30645 18158 +1 14 1 10441 30583 18477 +2 14 1 11023 30530 18874 +3 14 1 11831 30485 19177 +4 14 1 8385 30201 20133 +5 14 1 31336 31462 18321 +6 14 1 56114 32926 15860 +7 14 1 50324 32640 16320 +8 14 1 49720 32640 16320 +9 14 1 49089 32640 16320 +10 14 1 48249 32640 16320 +11 14 1 47666 32640 16320 +12 14 1 47002 32640 16320 +13 14 1 46162 32640 16320 +14 14 1 45599 32640 16320 +15 14 1 44891 32640 16320 +16 14 1 44052 32640 16320 +17 14 1 43498 32640 16320 +18 14 1 42731 32640 16320 +19 14 1 41894 32640 16320 +20 14 1 41327 32640 16320 +21 14 1 40490 32640 16320 +22 14 1 39654 32640 16320 +23 14 1 39072 32703 16092 +24 14 1 36039 31949 18794 +25 14 1 25846 28860 29839 +26 14 1 28279 29307 28563 +27 14 1 28871 29275 28996 +28 14 1 29731 29239 29532 +29 14 1 30353 29196 29829 +30 14 1 31016 29145 30276 +31 14 1 31882 29082 30806 +32 14 1 32463 29007 31084 +33 14 1 33246 28919 31575 +34 14 1 34119 28817 31846 +35 14 1 34723 28715 32369 +36 14 1 35592 28631 32895 +37 14 1 36376 28560 33179 +38 14 1 36949 28501 33646 +39 14 1 37790 28452 33944 +40 14 1 38609 28411 34455 +41 14 1 39263 28376 34927 +42 14 1 39877 28346 35221 +43 14 1 40730 28318 35733 +44 14 1 41326 28291 36196 +45 14 1 41995 28263 36537 +46 14 1 42843 28232 36948 +47 14 1 43407 28196 37261 +48 14 1 44130 28154 37771 +49 14 1 44967 28103 38257 +50 14 1 45516 28041 38529 +51 14 1 46286 27967 39012 +52 14 1 47107 27878 39261 +53 14 1 47653 27773 39742 +54 14 1 48455 27673 40246 +55 14 1 49252 27589 40495 +56 14 1 49792 27519 40964 +57 14 1 50600 27461 41491 +58 14 1 51393 27413 41771 +59 14 1 52146 27373 42214 +60 14 1 52680 27339 42540 +61 14 1 53495 27308 42950 +62 14 1 54223 27279 43500 +63 14 1 54759 27249 43836 +0 15 1 9817 31738 18537 +1 15 1 10448 31665 18920 +2 15 1 11043 31603 19219 +3 15 1 11849 31550 19752 +4 15 1 7942 31365 20702 +5 15 1 33059 32072 18273 +6 15 1 55631 32779 15865 +7 15 1 50304 32640 16320 +8 15 1 49713 32640 16320 +9 15 1 49066 32640 16320 +10 15 1 48227 32640 16320 +11 15 1 47656 32640 16320 +12 15 1 46974 32640 16320 +13 15 1 46136 32640 16320 +14 15 1 45580 32640 16320 +15 15 1 44852 32640 16320 +16 15 1 44016 32640 16320 +17 15 1 43463 32640 16320 +18 15 1 42673 32640 16320 +19 15 1 41839 32640 16320 +20 15 1 41268 32640 16320 +21 15 1 40428 32640 16320 +22 15 1 39600 32640 16320 +23 15 1 39175 32709 15931 +24 15 1 35466 32064 19461 +25 15 1 25836 30064 30311 +26 15 1 28289 30365 29041 +27 15 1 28893 30335 29552 +28 15 1 29754 30304 29849 +29 15 1 30361 30269 30333 +30 15 1 31047 30229 30837 +31 15 1 31915 30181 31145 +32 15 1 32497 30123 31621 +33 15 1 33315 30055 31914 +34 15 1 34192 29974 32473 +35 15 1 34788 29879 32971 +36 15 1 35655 29774 33239 +37 15 1 36407 29682 33712 +38 15 1 36985 29606 33987 +39 15 1 37820 29541 34522 +40 15 1 38640 29488 34951 +41 15 1 39271 29444 35247 +42 15 1 39906 29406 35786 +43 15 1 40758 29374 36206 +44 15 1 41340 29345 36575 +45 15 1 42036 29317 36966 +46 15 1 42881 29289 37294 +47 15 1 43438 29257 37839 +48 15 1 44190 29222 38290 +49 15 1 45023 29179 38599 +50 15 1 45573 29128 39073 +51 15 1 46369 29066 39346 +52 15 1 47183 28991 39877 +53 15 1 47731 28900 40348 +54 15 1 48545 28793 40584 +55 15 1 49317 28692 41079 +56 15 1 49856 28607 41561 +57 15 1 50669 28536 41821 +58 15 1 51461 28478 42280 +59 15 1 52190 28429 42568 +60 15 1 52730 28389 43023 +61 15 1 53553 28355 43545 +62 15 1 54257 28324 43860 +63 15 1 54803 28294 44245 +0 16 1 9838 32843 18979 +1 16 1 10454 32759 19285 +2 16 1 11063 32687 19825 +3 16 1 11866 32625 20182 +4 16 1 7560 32563 21185 +5 16 1 34842 32591 18309 +6 16 1 55065 32655 15874 +7 16 1 50283 32640 16320 +8 16 1 49705 32640 16320 +9 16 1 49040 32640 16320 +10 16 1 48203 32640 16320 +11 16 1 47642 32640 16320 +12 16 1 46941 32640 16320 +13 16 1 46105 32640 16320 +14 16 1 45554 32640 16320 +15 16 1 44805 32640 16320 +16 16 1 43972 32640 16320 +17 16 1 43417 32640 16320 +18 16 1 42605 32640 16320 +19 16 1 41779 32640 16320 +20 16 1 41214 32640 16320 +21 16 1 40374 32640 16320 +22 16 1 39552 32640 16320 +23 16 1 39309 32691 15726 +24 16 1 34878 32286 20199 +25 16 1 25885 31290 30865 +26 16 1 28297 31426 29569 +27 16 1 28916 31394 29864 +28 16 1 29778 31365 30379 +29 16 1 30372 31335 30847 +30 16 1 31084 31302 31191 +31 16 1 31954 31265 31636 +32 16 1 32542 31220 31957 +33 16 1 33396 31168 32545 +34 16 1 34255 31106 33030 +35 16 1 34841 31032 33322 +36 16 1 35707 30943 33800 +37 16 1 36429 30837 34068 +38 16 1 37015 30737 34612 +39 16 1 37846 30653 34995 +40 16 1 38668 30583 35295 +41 16 1 39279 30524 35848 +42 16 1 39937 30476 36221 +43 16 1 40789 30436 36616 +44 16 1 41360 30401 36980 +45 16 1 42083 30371 37328 +46 16 1 42926 30342 37896 +47 16 1 43481 30313 38306 +48 16 1 44261 30282 38661 +49 16 1 45088 30246 39110 +50 16 1 45644 30204 39417 +51 16 1 46461 30152 39981 +52 16 1 47253 30089 40421 +53 16 1 47797 30013 40684 +54 16 1 48618 29921 41211 +55 16 1 49366 29812 41644 +56 16 1 49908 29709 41893 +57 16 1 50724 29623 42350 +58 16 1 51517 29552 42611 +59 16 1 52221 29493 43102 +60 16 1 52771 29445 43584 +61 16 1 53599 29405 43888 +62 16 1 54280 29370 44292 +63 16 1 54839 29339 44628 +0 17 1 9859 33961 19376 +1 17 1 10460 33868 19912 +2 17 1 11085 33784 20227 +3 17 1 11885 33711 20605 +4 17 1 7272 33793 21690 +5 17 1 36686 33013 18194 +6 17 1 54433 32562 15915 +7 17 1 50259 32640 16320 +8 17 1 49693 32640 16320 +9 17 1 49010 32640 16320 +10 17 1 48176 32640 16320 +11 17 1 47622 32640 16320 +12 17 1 46901 32640 16320 +13 17 1 46069 32640 16320 +14 17 1 45520 32640 16320 +15 17 1 44749 32640 16320 +16 17 1 43920 32640 16320 +17 17 1 43360 32640 16320 +18 17 1 42530 32640 16320 +19 17 1 41727 32640 16320 +20 17 1 41166 32640 16320 +21 17 1 40327 32640 16320 +22 17 1 39509 32640 16320 +23 17 1 39467 32639 15475 +24 17 1 34272 32624 21091 +25 17 1 25990 32528 31350 +26 17 1 28305 32497 29888 +27 17 1 28941 32459 30427 +28 17 1 29803 32426 30852 +29 17 1 30388 32397 31228 +30 17 1 31127 32367 31635 +31 17 1 32000 32337 31983 +32 17 1 32599 32302 32587 +33 17 1 33480 32262 33048 +34 17 1 34305 32215 33385 +35 17 1 34885 32157 33858 +36 17 1 35750 32087 34164 +37 17 1 36444 32003 34725 +38 17 1 37042 31902 35071 +39 17 1 37869 31793 35377 +40 17 1 38694 31700 35928 +41 17 1 39290 31624 36255 +42 17 1 39972 31560 36668 +43 17 1 40823 31508 37000 +44 17 1 41387 31465 37370 +45 17 1 42138 31428 37949 +46 17 1 42978 31396 38314 +47 17 1 43535 31367 38718 +48 17 1 44342 31337 39129 +49 17 1 45162 31306 39477 +50 17 1 45716 31270 40060 +51 17 1 46540 31227 40462 +52 17 1 47306 31175 40764 +53 17 1 47851 31111 41320 +54 17 1 48678 31034 41724 +55 17 1 49401 30940 41989 +56 17 1 49950 30828 42431 +57 17 1 50767 30724 42680 +58 17 1 51561 30637 43194 +59 17 1 52242 30566 43626 +60 17 1 52805 30507 43927 +61 17 1 53638 30459 44337 +62 17 1 54295 30420 44646 +63 17 1 54870 30385 45120 +0 18 1 9881 35069 20002 +1 18 1 10467 34987 20292 +2 18 1 11109 34895 20680 +3 18 1 11906 34811 20995 +4 18 1 6612 35091 22279 +5 18 1 41475 33083 17678 +6 18 1 58979 32038 15063 +7 18 1 55273 32169 15358 +8 18 1 54739 32151 15259 +9 18 1 53834 32151 15173 +10 18 1 52624 32165 15165 +11 18 1 51675 32192 15168 +12 18 1 50408 32229 15194 +13 18 1 49003 32275 15288 +14 18 1 47931 32326 15384 +15 18 1 46582 32381 15526 +16 18 1 45247 32437 15664 +17 18 1 44263 32491 15815 +18 18 1 43055 32541 15976 +19 18 1 41994 32584 16116 +20 18 1 41246 32616 16232 +21 18 1 40306 32636 16304 +22 18 1 39471 32640 16320 +23 18 1 39642 32546 15213 +24 18 1 33650 33087 21908 +25 18 1 26147 33768 31628 +26 18 1 28312 33582 30485 +27 18 1 28969 33535 30866 +28 18 1 29831 33495 31266 +29 18 1 30411 33461 31635 +30 18 1 31180 33431 32006 +31 18 1 32055 33402 32615 +32 18 1 32668 33373 33038 +33 18 1 33550 33340 33431 +34 18 1 34342 33303 33879 +35 18 1 34921 33258 34231 +36 18 1 35785 33204 34805 +37 18 1 36455 33138 35141 +38 18 1 37068 33057 35487 +39 18 1 37891 32960 36034 +40 18 1 38721 32847 36322 +41 18 1 39304 32746 36738 +42 18 1 40011 32663 37039 +43 18 1 40862 32595 37430 +44 18 1 41423 32539 38004 +45 18 1 42201 32492 38325 +46 18 1 43038 32454 38775 +47 18 1 43602 32421 39141 +48 18 1 44429 32390 39532 +49 18 1 45224 32360 40118 +50 18 1 45774 32328 40483 +51 18 1 46604 32292 40829 +52 18 1 47345 32249 41398 +53 18 1 47894 32196 41775 +54 18 1 48727 32131 42075 +55 18 1 49426 32052 42508 +56 18 1 49985 31955 42780 +57 18 1 50801 31841 43305 +58 18 1 51598 31737 43681 +59 18 1 52256 31650 43984 +60 18 1 52835 31579 44385 +61 18 1 53670 31521 44678 +62 18 1 54306 31474 45187 +63 18 1 54898 31434 45632 +0 19 1 9904 36164 20353 +1 19 1 10476 36096 20761 +2 19 1 11137 36016 21063 +3 19 1 11931 35925 21436 +4 19 1 12397 35862 22028 +5 19 1 16266 35581 21803 +6 19 1 20087 35305 21704 +7 19 1 22017 35149 21661 +8 19 1 23975 34979 21654 +9 19 1 25907 34814 21743 +10 19 1 27740 34654 21523 +11 19 1 29361 34497 21417 +12 19 1 30909 34344 21353 +13 19 1 32316 34192 21021 +14 19 1 33592 34043 20839 +15 19 1 34717 33894 20444 +16 19 1 35690 33746 20145 +17 19 1 36603 33598 19792 +18 19 1 37276 33448 19294 +19 19 1 37834 33297 18833 +20 19 1 38377 33142 18264 +21 19 1 38596 32984 17703 +22 19 1 38666 32822 17065 +23 19 1 39766 32424 14974 +24 19 1 33278 33613 22585 +25 19 1 26238 35032 32229 +26 19 1 28321 34688 30900 +27 19 1 29002 34627 31313 +28 19 1 29865 34577 31649 +29 19 1 30442 34534 32039 +30 19 1 31243 34498 32643 +31 19 1 32122 34466 33019 +32 19 1 32731 34437 33468 +33 19 1 33608 34407 33876 +34 19 1 34369 34376 34279 +35 19 1 34952 34340 34851 +36 19 1 35815 34297 35176 +37 19 1 36462 34246 35570 +38 19 1 37093 34182 36117 +39 19 1 37915 34105 36410 +40 19 1 38749 34010 36828 +41 19 1 39323 33897 37111 +42 19 1 40057 33789 37520 +43 19 1 40906 33700 38071 +44 19 1 41468 33627 38353 +45 19 1 42274 33568 38837 +46 19 1 43107 33519 39157 +47 19 1 43670 33479 39590 +48 19 1 44502 33444 40165 +49 19 1 45272 33413 40493 +50 19 1 45822 33382 40887 +51 19 1 46656 33350 41452 +52 19 1 47373 33313 41804 +53 19 1 47930 33269 42141 +54 19 1 48767 33215 42554 +55 19 1 49443 33149 42872 +56 19 1 50015 33068 43416 +57 19 1 50829 32969 43757 +58 19 1 51629 32852 44066 +59 19 1 52266 32748 44443 +60 19 1 52861 32662 44734 +61 19 1 53698 32591 45263 +62 19 1 54314 32534 45655 +63 19 1 54924 32487 46002 +0 20 1 9929 37248 20822 +1 20 1 10489 37191 21120 +2 20 1 11169 37125 21540 +3 20 1 11960 37047 22065 +4 20 1 12774 36957 22321 +5 20 1 13239 36877 22779 +6 20 1 13681 36820 23098 +7 20 1 14202 36776 23563 +8 20 1 14358 36749 24206 +9 20 1 14699 36734 24619 +10 20 1 15125 36728 25158 +11 20 1 15262 36730 25877 +12 20 1 15721 36736 26383 +13 20 1 16281 36744 27002 +14 20 1 16599 36750 27545 +15 20 1 17337 36753 28108 +16 20 1 18120 36748 28825 +17 20 1 18707 36733 29340 +18 20 1 19763 36705 29779 +19 20 1 20767 36661 30215 +20 20 1 21654 36596 30513 +21 20 1 22950 36508 30928 +22 20 1 24279 36393 31040 +23 20 1 25358 36264 31103 +24 20 1 26747 36034 30826 +25 20 1 27715 35913 30998 +26 20 1 28333 35818 31378 +27 20 1 29041 35741 31688 +28 20 1 29905 35676 32093 +29 20 1 30484 35621 32682 +30 20 1 31319 35575 33011 +31 20 1 32190 35536 33505 +32 20 1 32784 35501 33867 +33 20 1 33657 35469 34317 +34 20 1 34388 35439 34878 +35 20 1 34978 35408 35191 +36 20 1 35841 35373 35630 +37 20 1 36469 35332 36161 +38 20 1 37120 35283 36472 +39 20 1 37941 35222 36891 +40 20 1 38780 35146 37191 +41 20 1 39349 35054 37640 +42 20 1 40109 34943 38162 +43 20 1 40957 34828 38411 +44 20 1 41524 34734 38910 +45 20 1 42355 34657 39188 +46 20 1 43168 34594 39658 +47 20 1 43726 34544 40209 +48 20 1 44562 34502 40504 +49 20 1 45308 34466 40944 +50 20 1 45862 34434 41491 +51 20 1 46700 34403 41823 +52 20 1 47394 34370 42193 +53 20 1 47961 34333 42578 +54 20 1 48800 34288 42947 +55 20 1 49455 34232 43493 +56 20 1 50041 34164 43820 +57 20 1 50852 34081 44158 +58 20 1 51657 33979 44520 +59 20 1 52274 33860 44821 +60 20 1 52887 33757 45352 +61 20 1 53724 33672 45690 +62 20 1 54322 33602 46054 +63 20 1 54951 33546 46409 +0 21 1 9959 38322 21164 +1 21 1 10508 38275 21629 +2 21 1 11207 38219 22136 +3 21 1 11997 38155 22396 +4 21 1 12810 38080 22854 +5 21 1 13350 37993 23110 +6 21 1 14084 37906 23538 +7 21 1 14903 37831 24073 +8 21 1 15442 37766 24338 +9 21 1 16210 37710 24768 +10 21 1 17025 37662 25329 +11 21 1 17572 37621 25625 +12 21 1 18382 37586 26091 +13 21 1 19183 37555 26428 +14 21 1 19734 37528 26846 +15 21 1 20564 37503 27421 +16 21 1 21322 37479 27763 +17 21 1 21878 37454 28149 +18 21 1 22722 37428 28546 +19 21 1 23425 37398 28927 +20 21 1 24002 37364 29490 +21 21 1 24829 37323 29814 +22 21 1 25646 37275 30195 +23 21 1 26269 37218 30747 +24 21 1 26914 37151 31048 +25 21 1 27772 37070 31463 +26 21 1 28352 36976 31760 +27 21 1 29088 36880 32177 +28 21 1 29953 36798 32740 +29 21 1 30540 36727 33030 +30 21 1 31406 36667 33551 +31 21 1 32244 36615 33869 +32 21 1 32829 36572 34358 +33 21 1 33699 36534 34899 +34 21 1 34402 36500 35198 +35 21 1 35003 36468 35677 +36 21 1 35865 36437 36181 +37 21 1 36477 36403 36513 +38 21 1 37149 36363 36924 +39 21 1 37971 36315 37240 +40 21 1 38814 36256 37733 +41 21 1 39381 36182 38240 +42 21 1 40168 36092 38501 +43 21 1 41014 35981 39000 +44 21 1 41589 35863 39251 +45 21 1 42428 35764 39747 +46 21 1 43217 35684 40261 +47 21 1 43772 35619 40529 +48 21 1 44611 35566 41007 +49 21 1 45334 35523 41523 +50 21 1 45895 35487 41839 +51 21 1 46735 35455 42236 +52 21 1 47408 35423 42590 +53 21 1 47987 35389 43009 +54 21 1 48828 35351 43540 +55 21 1 49464 35305 43863 +56 21 1 50066 35248 44226 +57 21 1 50874 35178 44580 +58 21 1 51681 35091 44931 +59 21 1 52281 34986 45460 +60 21 1 52913 34866 45748 +61 21 1 53749 34764 46124 +62 21 1 54330 34680 46450 +63 21 1 54980 34612 46781 +0 22 1 9993 39387 21706 +1 22 1 10533 39348 22184 +2 22 1 11253 39302 22461 +3 22 1 12042 39248 22924 +4 22 1 12853 39186 23185 +5 22 1 13389 39114 23656 +6 22 1 14144 39030 24163 +7 22 1 14957 38943 24404 +8 22 1 15496 38866 24866 +9 22 1 16286 38800 25400 +10 22 1 17094 38743 25668 +11 22 1 17642 38694 26157 +12 22 1 18461 38652 26453 +13 22 1 19238 38616 26907 +14 22 1 19787 38584 27460 +15 22 1 20622 38557 27776 +16 22 1 21354 38531 28192 +17 22 1 21916 38507 28552 +18 22 1 22762 38483 28977 +19 22 1 23440 38458 29523 +20 22 1 24030 38430 29833 +21 22 1 24853 38399 30257 +22 22 1 25673 38362 30795 +23 22 1 26278 38318 31105 +24 22 1 26947 38266 31524 +25 22 1 27804 38204 31832 +26 22 1 28378 38130 32286 +27 22 1 29145 38043 32823 +28 22 1 30011 37943 33086 +29 22 1 30610 37854 33611 +30 22 1 31484 37777 33896 +31 22 1 32287 37710 34414 +32 22 1 32867 37653 34927 +33 22 1 33734 37605 35212 +34 22 1 34413 37564 35724 +35 22 1 35026 37528 36194 +36 22 1 35889 37496 36543 +37 22 1 36487 37464 36939 +38 22 1 37181 37430 37270 +39 22 1 38006 37391 37798 +40 22 1 38853 37344 38276 +41 22 1 39422 37286 38574 +42 22 1 40235 37213 39069 +43 22 1 41077 37123 39340 +44 22 1 41648 37013 39866 +45 22 1 42489 36892 40332 +46 22 1 43254 36790 40579 +47 22 1 43811 36707 41085 +48 22 1 44653 36641 41561 +49 22 1 45353 36587 41863 +50 22 1 45923 36543 42277 +51 22 1 46766 36507 42601 +52 22 1 47418 36474 43066 +53 22 1 48011 36441 43568 +54 22 1 48854 36407 43894 +55 22 1 49471 36368 44274 +56 22 1 50090 36320 44617 +57 22 1 50895 36261 45021 +58 22 1 51705 36189 45546 +59 22 1 52290 36099 45827 +60 22 1 52940 35989 46216 +61 22 1 53774 35869 46510 +62 22 1 54342 35769 46857 +63 22 1 55011 35687 47397 +0 23 1 10035 40445 22217 +1 23 1 10568 40412 22520 +2 23 1 11310 40374 22975 +3 23 1 12098 40330 23248 +4 23 1 12905 40278 23761 +5 23 1 13439 40219 24245 +6 23 1 14217 40149 24492 +7 23 1 15022 40069 24988 +8 23 1 15565 39982 25486 +9 23 1 16372 39904 25734 +10 23 1 17160 39836 26230 +11 23 1 17704 39778 26495 +12 23 1 18529 39727 26981 +13 23 1 19282 39684 27502 +14 23 1 19833 39647 27798 +15 23 1 20672 39614 28236 +16 23 1 21378 39586 28562 +17 23 1 21949 39560 29027 +18 23 1 22797 39536 29546 +19 23 1 23450 39513 29845 +20 23 1 24057 39489 30309 +21 23 1 24876 39462 30821 +22 23 1 25701 39433 31145 +23 23 1 26290 39399 31560 +24 23 1 26985 39358 31877 +25 23 1 27843 39310 32369 +26 23 1 28414 39253 32887 +27 23 1 29214 39184 33164 +28 23 1 30081 39102 33686 +29 23 1 30679 39004 33959 +30 23 1 31549 38907 34495 +31 23 1 32318 38823 34974 +32 23 1 32899 38750 35245 +33 23 1 33765 38688 35779 +34 23 1 34420 38636 36214 +35 23 1 35050 38592 36571 +36 23 1 35912 38555 36953 +37 23 1 36500 38521 37291 +38 23 1 37218 38489 37844 +39 23 1 38047 38455 38284 +40 23 1 38896 38416 38629 +41 23 1 39471 38369 39098 +42 23 1 40309 38311 39405 +43 23 1 41132 38239 39970 +44 23 1 41697 38149 40416 +45 23 1 42541 38039 40669 +46 23 1 43282 37915 41187 +47 23 1 43843 37811 41615 +48 23 1 44687 37727 41903 +49 23 1 45366 37660 42324 +50 23 1 45947 37606 42622 +51 23 1 46792 37562 43124 +52 23 1 47425 37525 43586 +53 23 1 48034 37491 43922 +54 23 1 48877 37459 44306 +55 23 1 49478 37424 44640 +56 23 1 50115 37383 45094 +57 23 1 50917 37334 45599 +58 23 1 51730 37273 45889 +59 23 1 52301 37197 46298 +60 23 1 52971 37103 46588 +61 23 1 53802 36989 46968 +62 23 1 54357 36870 47487 +63 23 1 55046 36772 47758 +0 24 1 10085 41474 22579 +1 24 1 10615 41450 23014 +2 24 1 11378 41422 23307 +3 24 1 12168 41389 23852 +4 24 1 12969 41350 24305 +5 24 1 13505 41304 24572 +6 24 1 14304 41249 25107 +7 24 1 15093 41184 25575 +8 24 1 15633 41109 25824 +9 24 1 16449 41023 26313 +10 24 1 17213 40943 26559 +11 24 1 17757 40874 27070 +12 24 1 18588 40814 27551 +13 24 1 19315 40762 27834 +14 24 1 19872 40717 28283 +15 24 1 20715 40678 28582 +16 24 1 21396 40645 29081 +17 24 1 21979 40616 29566 +18 24 1 22828 40590 29858 +19 24 1 23459 40565 30358 +20 24 1 24085 40542 30835 +21 24 1 24902 40519 31177 +22 24 1 25730 40494 31581 +23 24 1 26308 40466 31907 +24 24 1 27032 40434 32431 +25 24 1 27889 40396 32919 +26 24 1 28463 40351 33226 +27 24 1 29296 40298 33735 +28 24 1 30152 40233 34031 +29 24 1 30737 40154 34596 +30 24 1 31604 40059 35045 +31 24 1 32342 39955 35309 +32 24 1 32928 39863 35854 +33 24 1 33792 39785 36252 +34 24 1 34427 39719 36606 +35 24 1 35075 39663 36976 +36 24 1 35937 39618 37318 +37 24 1 36517 39579 37884 +38 24 1 37259 39544 38281 +39 24 1 38094 39512 38672 +40 24 1 38946 39478 39102 +41 24 1 39529 39439 39447 +42 24 1 40377 39392 40039 +43 24 1 41176 39334 40461 +44 24 1 41738 39261 40753 +45 24 1 42584 39170 41304 +46 24 1 43302 39058 41696 +47 24 1 43871 38933 41972 +48 24 1 44717 38828 42385 +49 24 1 45376 38744 42665 +50 24 1 45970 38676 43190 +51 24 1 46815 38622 43606 +52 24 1 47432 38578 43952 +53 24 1 48057 38541 44332 +54 24 1 48899 38508 44659 +55 24 1 49486 38475 45155 +56 24 1 50141 38439 45626 +57 24 1 50940 38397 45938 +58 24 1 51755 38345 46351 +59 24 1 52315 38281 46647 +60 24 1 53004 38202 47081 +61 24 1 53831 38104 47589 +62 24 1 54378 37984 47828 +63 24 1 55086 37868 48218 +0 25 1 10147 42652 23046 +1 25 1 10676 42617 23367 +2 25 1 11461 42579 23934 +3 25 1 12255 42538 24351 +4 25 1 13042 42492 24645 +5 25 1 13578 42440 25204 +6 25 1 14386 42382 25638 +7 25 1 15153 42316 25909 +8 25 1 15693 42242 26393 +9 25 1 16516 42158 26648 +10 25 1 17254 42064 27177 +11 25 1 17803 41973 27613 +12 25 1 18639 41894 27890 +13 25 1 19339 41825 28337 +14 25 1 19907 41767 28619 +15 25 1 20752 41717 29145 +16 25 1 21408 41674 29589 +17 25 1 22007 41639 29880 +18 25 1 22857 41608 30408 +19 25 1 23467 41582 30846 +20 25 1 24116 41560 31207 +21 25 1 24931 41539 31596 +22 25 1 25764 41518 31931 +23 25 1 26334 41498 32481 +24 25 1 27088 41475 32932 +25 25 1 27945 41448 33275 +26 25 1 28527 41417 33757 +27 25 1 29387 41378 34081 +28 25 1 30208 41330 34671 +29 25 1 30785 41270 35104 +30 25 1 31650 41196 35396 +31 25 1 32358 41103 35953 +32 25 1 32954 40995 36316 +33 25 1 33817 40897 36659 +34 25 1 34435 40815 37018 +35 25 1 35102 40745 37359 +36 25 1 35965 40688 37928 +37 25 1 36540 40640 38281 +38 25 1 37307 40601 38711 +39 25 1 38149 40566 39094 +40 25 1 39002 40533 39476 +41 25 1 39586 40498 40082 +42 25 1 40435 40459 40476 +43 25 1 41210 40411 40813 +44 25 1 41772 40352 41384 +45 25 1 42620 40278 41763 +46 25 1 43318 40186 42061 +47 25 1 43895 40071 42468 +48 25 1 44742 39945 42741 +49 25 1 45384 39841 43273 +50 25 1 45991 39757 43639 +51 25 1 46837 39690 43993 +52 25 1 47438 39637 44359 +53 25 1 48081 39594 44684 +54 25 1 48922 39557 45211 +55 25 1 49496 39523 45640 +56 25 1 50170 39489 45981 +57 25 1 50967 39452 46384 +58 25 1 51783 39408 46689 +59 25 1 52334 39355 47172 +60 25 1 53042 39287 47660 +61 25 1 53864 39204 47906 +62 25 1 54403 39101 48343 +63 25 1 55130 38977 48842 +0 26 1 10220 43879 23434 +1 26 1 10751 43859 24008 +2 26 1 11548 43836 24384 +3 26 1 12340 43811 24711 +4 26 1 13105 43781 25282 +5 26 1 13640 43746 25681 +6 26 1 14457 43704 25982 +7 26 1 15200 43656 26446 +8 26 1 15743 43598 26728 +9 26 1 16572 43532 27281 +10 26 1 17285 43454 27684 +11 26 1 17842 43365 27967 +12 26 1 18682 43271 28401 +13 26 1 19357 43187 28678 +14 26 1 19938 43113 29219 +15 26 1 20784 43046 29621 +16 26 1 21417 42987 29917 +17 26 1 22036 42935 30464 +18 26 1 22885 42888 30861 +19 26 1 23478 42845 31241 +20 26 1 24152 42806 31611 +21 26 1 24967 42770 31957 +22 26 1 25804 42734 32524 +23 26 1 26370 42700 32936 +24 26 1 27156 42664 33318 +25 26 1 28012 42626 33764 +26 26 1 28603 42585 34118 +27 26 1 29464 42538 34723 +28 26 1 30251 42484 35133 +29 26 1 30826 42421 35460 +30 26 1 31689 42345 36034 +31 26 1 32370 42255 36392 +32 26 1 32979 42147 36734 +33 26 1 33841 42021 37085 +34 26 1 34444 41907 37426 +35 26 1 35134 41812 37986 +36 26 1 35995 41735 38297 +37 26 1 36569 41672 38753 +38 26 1 37361 41623 39091 +39 26 1 38212 41584 39505 +40 26 1 39060 41551 40111 +41 26 1 39634 41522 40475 +42 26 1 40485 41492 40857 +43 26 1 41237 41457 41435 +44 26 1 41802 41414 41799 +45 26 1 42651 41359 42128 +46 26 1 43329 41287 42535 +47 26 1 43916 41195 42845 +48 26 1 44764 41079 43378 +49 26 1 45390 40953 43696 +50 26 1 46012 40850 44051 +51 26 1 46857 40768 44397 +52 26 1 47446 40702 44724 +53 26 1 48106 40650 45268 +54 26 1 48945 40607 45649 +55 26 1 49509 40571 46023 +56 26 1 50202 40537 46403 +57 26 1 50997 40503 46723 +58 26 1 51814 40464 47245 +59 26 1 52357 40418 47701 +60 26 1 53084 40362 47966 +61 26 1 53901 40291 48454 +62 26 1 54435 40202 48936 +63 26 1 55180 40094 49166 +0 27 1 10287 44889 24071 +1 27 1 10817 44868 24409 +2 27 1 11623 44846 24774 +3 27 1 12412 44823 25345 +4 27 1 13154 44797 25711 +5 27 1 13693 44769 26044 +6 27 1 14517 44736 26479 +7 27 1 15235 44699 26797 +8 27 1 15786 44655 27361 +9 27 1 16620 44605 27737 +10 27 1 17307 44548 28045 +11 27 1 17877 44481 28468 +12 27 1 18719 44405 28762 +13 27 1 19369 44322 29308 +14 27 1 19967 44248 29666 +15 27 1 20814 44184 29974 +16 27 1 21426 44129 30529 +17 27 1 22067 44082 30885 +18 27 1 22915 44042 31283 +19 27 1 23493 44008 31631 +20 27 1 24195 43979 31990 +21 27 1 25012 43954 32568 +22 27 1 25853 43932 32940 +23 27 1 26420 43911 33359 +24 27 1 27239 43891 33766 +25 27 1 28089 43869 34151 +26 27 1 28670 43846 34760 +27 27 1 29529 43818 35144 +28 27 1 30284 43784 35507 +29 27 1 30861 43741 36086 +30 27 1 31722 43688 36441 +31 27 1 32379 43620 36795 +32 27 1 33004 43535 37160 +33 27 1 33865 43429 37521 +34 27 1 34456 43300 38064 +35 27 1 35169 43180 38341 +36 27 1 36031 43077 38804 +37 27 1 36605 42988 39103 +38 27 1 37422 42913 39542 +39 27 1 38282 42848 40138 +40 27 1 39107 42791 40470 +41 27 1 39676 42739 40895 +42 27 1 40527 42689 41467 +43 27 1 41258 42636 41815 +44 27 1 41828 42578 42176 +45 27 1 42678 42511 42571 +46 27 1 43338 42431 42925 +47 27 1 43937 42335 43470 +48 27 1 44785 42220 43778 +49 27 1 45396 42080 44133 +50 27 1 46034 41941 44456 +51 27 1 46878 41830 44787 +52 27 1 47456 41743 45333 +53 27 1 48133 41676 45665 +54 27 1 48970 41624 46068 +55 27 1 49526 41583 46418 +56 27 1 50237 41550 46757 +57 27 1 51032 41520 47305 +58 27 1 51847 41490 47722 +59 27 1 52386 41455 48013 +60 27 1 53131 41412 48541 +61 27 1 53942 41357 48996 +62 27 1 54474 41286 49253 +63 27 1 55236 41196 49725 +0 28 1 10340 45933 24430 +1 28 1 10874 45912 24835 +2 28 1 11684 45891 25394 +3 28 1 12474 45869 25733 +4 28 1 13191 45847 26098 +5 28 1 13738 45822 26500 +6 28 1 14568 45794 26857 +7 28 1 15260 45763 27419 +8 28 1 15823 45727 27774 +9 28 1 16661 45685 28108 +10 28 1 17322 45637 28514 +11 28 1 17908 45582 28844 +12 28 1 18751 45518 29399 +13 28 1 19378 45445 29726 +14 28 1 19997 45364 30054 +15 28 1 20843 45287 30606 +16 28 1 21436 45219 30924 +17 28 1 22102 45160 31336 +18 28 1 22949 45108 31661 +19 28 1 23516 45063 32037 +20 28 1 24248 45024 32615 +21 28 1 25068 44990 32949 +22 28 1 25912 44960 33402 +23 28 1 26486 44933 33769 +24 28 1 27334 44908 34186 +25 28 1 28153 44884 34790 +26 28 1 28725 44860 35146 +27 28 1 29584 44834 35545 +28 28 1 30307 44805 36120 +29 28 1 30891 44771 36469 +30 28 1 31752 44730 36837 +31 28 1 32387 44679 37205 +32 28 1 33030 44615 37603 +33 28 1 33891 44536 38146 +34 28 1 34473 44439 38415 +35 28 1 35210 44322 38870 +36 28 1 36071 44218 39141 +37 28 1 36649 44133 39597 +38 28 1 37492 44065 40170 +39 28 1 38346 44011 40473 +40 28 1 39146 43969 40935 +41 28 1 39712 43934 41492 +42 28 1 40564 43904 41821 +43 28 1 41273 43874 42213 +44 28 1 41851 43840 42586 +45 28 1 42701 43798 42986 +46 28 1 43344 43745 43525 +47 28 1 43957 43675 43839 +48 28 1 44805 43585 44211 +49 28 1 45402 43470 44536 +50 28 1 46057 43327 44883 +51 28 1 46900 43190 45413 +52 28 1 47468 43076 45700 +53 28 1 48163 42982 46123 +54 28 1 48997 42904 46438 +55 28 1 49547 42839 46798 +56 28 1 50275 42781 47359 +57 28 1 51071 42729 47733 +58 28 1 51884 42678 48056 +59 28 1 52421 42625 48611 +60 28 1 53182 42566 49030 +61 28 1 53988 42497 49327 +62 28 1 54519 42416 49791 +63 28 1 55297 42319 50042 +0 29 1 10380 46978 24898 +1 29 1 10921 46955 25434 +2 29 1 11734 46934 25755 +3 29 1 12526 46913 26144 +4 29 1 13218 46892 26513 +5 29 1 13776 46870 26912 +6 29 1 14611 46846 27461 +7 29 1 15278 46819 27800 +8 29 1 15855 46789 28158 +9 29 1 16696 46754 28543 +10 29 1 17333 46714 28914 +11 29 1 17937 46668 29466 +12 29 1 18782 46614 29775 +13 29 1 19387 46553 30144 +14 29 1 20029 46483 30692 +15 29 1 20874 46403 30982 +16 29 1 21451 46325 31403 +17 29 1 22144 46255 31705 +18 29 1 22989 46194 32101 +19 29 1 23549 46140 32670 +20 29 1 24312 46094 32971 +21 29 1 25140 46053 33451 +22 29 1 25984 46018 33781 +23 29 1 26565 45986 34228 +24 29 1 27416 45958 34819 +25 29 1 28202 45933 35147 +26 29 1 28771 45908 35582 +27 29 1 29629 45883 36143 +28 29 1 30323 45857 36485 +29 29 1 30919 45827 36867 +30 29 1 31778 45793 37230 +31 29 1 32394 45750 37662 +32 29 1 33059 45698 38194 +33 29 1 33920 45633 38481 +34 29 1 34495 45551 38937 +35 29 1 35258 45450 39208 +36 29 1 36118 45330 39676 +37 29 1 36703 45225 40217 +38 29 1 37558 45138 40494 +39 29 1 38401 45067 40984 +40 29 1 39177 45009 41519 +41 29 1 39743 44962 41828 +42 29 1 40596 44923 42247 +43 29 1 41285 44888 42592 +44 29 1 41871 44853 43034 +45 29 1 42722 44816 43556 +46 29 1 43350 44771 43878 +47 29 1 43976 44717 44261 +48 29 1 44824 44648 44593 +49 29 1 45410 44560 44987 +50 29 1 46081 44451 45510 +51 29 1 46923 44321 45764 +52 29 1 47483 44207 46191 +53 29 1 48196 44119 46473 +54 29 1 49027 44050 46854 +55 29 1 49572 43998 47413 +56 29 1 50318 43957 47743 +57 29 1 51115 43924 48101 +58 29 1 51926 43894 48668 +59 29 1 52461 43863 49048 +60 29 1 53239 43827 49392 +61 29 1 54038 43783 49832 +62 29 1 54571 43725 50115 +63 29 1 55363 43651 50650 +0 30 1 10409 48025 25467 +1 30 1 10962 48000 25779 +2 30 1 11775 47977 26184 +3 30 1 12570 47956 26525 +4 30 1 13237 47935 26966 +5 30 1 13810 47915 27491 +6 30 1 14648 47893 27821 +7 30 1 15290 47869 28198 +8 30 1 15886 47843 28561 +9 30 1 16728 47814 28974 +10 30 1 17342 47780 29512 +11 30 1 17968 47741 29808 +12 30 1 18812 47697 30219 +13 30 1 19398 47645 30760 +14 30 1 20065 47586 31045 +15 30 1 20909 47518 31479 +16 30 1 21473 47440 31766 +17 30 1 22196 47360 32185 +18 30 1 23038 47288 32736 +19 30 1 23596 47226 33009 +20 30 1 24390 47170 33508 +21 30 1 25229 47122 33806 +22 30 1 26063 47080 34280 +23 30 1 26634 47043 34852 +24 30 1 27485 47010 35155 +25 30 1 28238 46982 35621 +26 30 1 28809 46955 36162 +27 30 1 29667 46930 36496 +28 30 1 30334 46904 36891 +29 30 1 30945 46877 37244 +30 30 1 31804 46847 37707 +31 30 1 32404 46811 38220 +32 30 1 33092 46767 38528 +33 30 1 33952 46713 38976 +34 30 1 34524 46644 39265 +35 30 1 35313 46559 39767 +36 30 1 36172 46453 40283 +37 30 1 36762 46332 40541 +38 30 1 37616 46229 41051 +39 30 1 38449 46144 41556 +40 30 1 39202 46075 41844 +41 30 1 39771 46019 42285 +42 30 1 40623 45974 42600 +43 30 1 41294 45935 43078 +44 30 1 41891 45900 43574 +45 30 1 42742 45865 43905 +46 30 1 43355 45826 44293 +47 30 1 43997 45780 44627 +48 30 1 44844 45721 45065 +49 30 1 45420 45648 45580 +50 30 1 46108 45555 45845 +51 30 1 46948 45439 46277 +52 30 1 47502 45308 46532 +53 30 1 48232 45198 46931 +54 30 1 49060 45111 47474 +55 30 1 49602 45042 47762 +56 30 1 50365 44988 48153 +57 30 1 51164 44944 48720 +58 30 1 51971 44906 49060 +59 30 1 52508 44872 49451 +60 30 1 53301 44837 49855 +61 30 1 54090 44797 50177 +62 30 1 54622 44749 50735 +63 30 1 55422 44690 51144 +0 31 1 10429 49074 25810 +1 31 1 10997 49046 26220 +2 31 1 11809 49021 26540 +3 31 1 12607 48999 27019 +4 31 1 13249 48978 27511 +5 31 1 13841 48957 27843 +6 31 1 14681 48937 28229 +7 31 1 15300 48915 28574 +8 31 1 15917 48892 29028 +9 31 1 16758 48867 29543 +10 31 1 17352 48838 29832 +11 31 1 18002 48805 30284 +12 31 1 18844 48767 30805 +13 31 1 19415 48724 31096 +14 31 1 20110 48674 31539 +15 31 1 20950 48616 31827 +16 31 1 21506 48549 32283 +17 31 1 22259 48473 32813 +18 31 1 23097 48392 33068 +19 31 1 23659 48318 33574 +20 31 1 24485 48254 33849 +21 31 1 25325 48197 34346 +22 31 1 26126 48147 34890 +23 31 1 26690 48104 35173 +24 31 1 27543 48066 35666 +25 31 1 28264 48032 36183 +26 31 1 28843 48002 36508 +27 31 1 29700 47975 36915 +28 31 1 30342 47949 37254 +29 31 1 30972 47923 37746 +30 31 1 31830 47896 38232 +31 31 1 32417 47864 38564 +32 31 1 33130 47826 38995 +33 31 1 33989 47780 39303 +34 31 1 34562 47723 39833 +35 31 1 35376 47650 40334 +36 31 1 36232 47560 40608 +37 31 1 36814 47448 41140 +38 31 1 37664 47328 41610 +39 31 1 38491 47228 41878 +40 31 1 39221 47146 42333 +41 31 1 39795 47080 42619 +42 31 1 40647 47027 43125 +43 31 1 41301 46983 43590 +44 31 1 41910 46946 43927 +45 31 1 42761 46911 44314 +46 31 1 43362 46875 44647 +47 31 1 44018 46834 45124 +48 31 1 44864 46785 45618 +49 31 1 45432 46722 45906 +50 31 1 46138 46643 46343 +51 31 1 46975 46544 46610 +52 31 1 47524 46420 47037 +53 31 1 48272 46292 47548 +54 31 1 49095 46190 47800 +55 31 1 49638 46108 48220 +56 31 1 50417 46044 48773 +57 31 1 51219 45993 49075 +58 31 1 52020 45951 49508 +59 31 1 52559 45915 49869 +60 31 1 53360 45881 50231 +61 31 1 54135 45844 50798 +62 31 1 54666 45802 51176 +63 31 1 55472 45751 51482 +0 32 1 10443 50127 26253 +1 32 1 11029 50095 26563 +2 32 1 11838 50067 27074 +3 32 1 12640 50042 27527 +4 32 1 13259 50020 27868 +5 32 1 13872 49999 28255 +6 32 1 14712 49979 28588 +7 32 1 15309 49958 29080 +8 32 1 15950 49937 29563 +9 32 1 16790 49914 29853 +10 32 1 17365 49889 30342 +11 32 1 18042 49861 30836 +12 32 1 18882 49829 31140 +13 32 1 19440 49792 31584 +14 32 1 20164 49749 31875 +15 32 1 21000 49700 32369 +16 32 1 21553 49643 32881 +17 32 1 22337 49577 33142 +18 32 1 23171 49501 33651 +19 32 1 23740 49419 33911 +20 32 1 24578 49344 34427 +21 32 1 25405 49278 34938 +22 32 1 26173 49220 35205 +23 32 1 26737 49169 35721 +24 32 1 27590 49124 36208 +25 32 1 28281 49085 36527 +26 32 1 28872 49051 36940 +27 32 1 29729 49021 37266 +28 32 1 30350 48993 37782 +29 32 1 31001 48967 38239 +30 32 1 31859 48940 38592 +31 32 1 32436 48911 39004 +32 32 1 33174 48878 39328 +33 32 1 34033 48838 39880 +34 32 1 34609 48789 40358 +35 32 1 35449 48728 40655 +36 32 1 36286 48651 41221 +37 32 1 36859 48554 41678 +38 32 1 37706 48435 41937 +39 32 1 38527 48319 42393 +40 32 1 39236 48223 42657 +41 32 1 39817 48146 43181 +42 32 1 40669 48083 43611 +43 32 1 41307 48033 43950 +44 32 1 41929 47992 44334 +45 32 1 42779 47955 44663 +46 32 1 43369 47920 45171 +47 32 1 44042 47883 45635 +48 32 1 44886 47840 45953 +49 32 1 45448 47786 46382 +50 32 1 46170 47719 46667 +51 32 1 47004 47633 47139 +52 32 1 47551 47526 47636 +53 32 1 48315 47394 47865 +54 32 1 49135 47275 48307 +55 32 1 49678 47180 48833 +56 32 1 50473 47105 49101 +57 32 1 51278 47045 49566 +58 32 1 52067 46998 49883 +59 32 1 52604 46958 50283 +60 32 1 53412 46923 50847 +61 32 1 54171 46888 51193 +62 32 1 54704 46850 51541 +63 32 1 55516 46805 51965 +0 33 1 10453 51183 26597 +1 33 1 11060 51147 27133 +2 33 1 11865 51115 27542 +3 33 1 12672 51087 27900 +4 33 1 13268 51063 28278 +5 33 1 13905 51040 28607 +6 33 1 14744 51019 29130 +7 33 1 15321 50999 29577 +8 33 1 15988 50979 29874 +9 33 1 16825 50958 30396 +10 33 1 17386 50936 30856 +11 33 1 18091 50911 31180 +12 33 1 18926 50883 31618 +13 33 1 19477 50851 31916 +14 33 1 20231 50814 32444 +15 33 1 21062 50772 32931 +16 33 1 21618 50722 33210 +17 33 1 22432 50665 33718 +18 33 1 23254 50600 33987 +19 33 1 23815 50524 34523 +20 33 1 24652 50440 34997 +21 33 1 25472 50365 35255 +22 33 1 26207 50297 35787 +23 33 1 26776 50238 36241 +24 33 1 27629 50187 36555 +25 33 1 28293 50142 36970 +26 33 1 28900 50102 37284 +27 33 1 29756 50068 37820 +28 33 1 30359 50037 38245 +29 33 1 31033 50009 38618 +30 33 1 31890 49982 39009 +31 33 1 32461 49954 39348 +32 33 1 33226 49924 39916 +33 33 1 34083 49889 40366 +34 33 1 34666 49847 40687 +35 33 1 35520 49794 41276 +36 33 1 36330 49727 41721 +37 33 1 36898 49644 42003 +38 33 1 37741 49540 42465 +39 33 1 38559 49416 42720 +40 33 1 39247 49306 43251 +41 33 1 39837 49216 43645 +42 33 1 40690 49144 43980 +43 33 1 41313 49085 44360 +44 33 1 41948 49038 44682 +45 33 1 42798 48999 45213 +46 33 1 43378 48963 45642 +47 33 1 44067 48928 45990 +48 33 1 44910 48889 46400 +49 33 1 45466 48842 46705 +50 33 1 46205 48784 47215 +51 33 1 47036 48710 47695 +52 33 1 47583 48617 47942 +53 33 1 48363 48500 48417 +54 33 1 49178 48368 48906 +55 33 1 49724 48258 49147 +56 33 1 50530 48170 49629 +57 33 1 51332 48101 49905 +58 33 1 52107 48046 50341 +59 33 1 52643 48002 50888 +60 33 1 53456 47965 51204 +61 33 1 54201 47930 51592 +62 33 1 54737 47894 51980 +63 33 1 55554 47854 52324 +0 34 1 10462 52242 27195 +1 34 1 11093 52201 27561 +2 34 1 11893 52165 27941 +3 34 1 12704 52134 28300 +4 34 1 13281 52107 28635 +5 34 1 13943 52082 29181 +6 34 1 14779 52060 29588 +7 34 1 15340 52039 29901 +8 34 1 16035 52019 30448 +9 34 1 16868 51999 30871 +10 34 1 17418 51978 31222 +11 34 1 18152 51956 31644 +12 34 1 18982 51931 31956 +13 34 1 19529 51903 32511 +14 34 1 20314 51871 32968 +15 34 1 21139 51834 33272 +16 34 1 21701 51791 33770 +17 34 1 22531 51742 34052 +18 34 1 23320 51684 34611 +19 34 1 23877 51618 35059 +20 34 1 24712 51540 35324 +21 34 1 25526 51456 35865 +22 34 1 26231 51379 36285 +23 34 1 26810 51312 36595 +24 34 1 27663 51252 37006 +25 34 1 28302 51200 37314 +26 34 1 28929 51155 37863 +27 34 1 29784 51116 38256 +28 34 1 30372 51081 38645 +29 34 1 31071 51051 39016 +30 34 1 31927 51022 39367 +31 34 1 32495 50995 39946 +32 34 1 33287 50966 40365 +33 34 1 34142 50934 40711 +34 34 1 34729 50897 41313 +35 34 1 35582 50851 41741 +36 34 1 36365 50793 42053 +37 34 1 36932 50721 42515 +38 34 1 37770 50630 42798 +39 34 1 38587 50517 43339 +40 34 1 39256 50394 43698 +41 34 1 39856 50291 44024 +42 34 1 40709 50207 44396 +43 34 1 41318 50140 44712 +44 34 1 41969 50086 45256 +45 34 1 42817 50042 45647 +46 34 1 43389 50005 46023 +47 34 1 44095 49970 46409 +48 34 1 44936 49933 46733 +49 34 1 45489 49892 47271 +50 34 1 46244 49841 47726 +51 34 1 47072 49777 47997 +52 34 1 47619 49696 48513 +53 34 1 48414 49594 48987 +54 34 1 49224 49466 49219 +55 34 1 49770 49341 49701 +56 34 1 50580 49240 49944 +57 34 1 51379 49160 50408 +58 34 1 52140 49097 50929 +59 34 1 52677 49047 51219 +60 34 1 53495 49006 51639 +61 34 1 54226 48970 51988 +62 34 1 54765 48936 52375 +63 34 1 55586 48898 52927 +0 35 1 10474 53305 27586 +1 35 1 11130 53258 27991 +2 35 1 11926 53218 28326 +3 35 1 12740 53183 28675 +4 35 1 13299 53152 29234 +5 35 1 13990 53125 29601 +6 35 1 14820 53101 29936 +7 35 1 15369 53079 30500 +8 35 1 16092 53058 30885 +9 35 1 16919 53038 31266 +10 35 1 17464 53018 31667 +11 35 1 18228 52997 31998 +12 35 1 19050 52975 32573 +13 35 1 19601 52950 32999 +14 35 1 20415 52921 33332 +15 35 1 21223 52889 33807 +16 35 1 21777 52851 34110 +17 35 1 22612 52808 34683 +18 35 1 23369 52757 35104 +19 35 1 23927 52698 35391 +20 35 1 24759 52630 35949 +21 35 1 25571 52550 36339 +22 35 1 26247 52465 36648 +23 35 1 26840 52388 37050 +24 35 1 27694 52321 37357 +25 35 1 28311 52262 37913 +26 35 1 28959 52210 38274 +27 35 1 29813 52165 38676 +28 35 1 30390 52127 39027 +29 35 1 31116 52093 39390 +30 35 1 31970 52062 39975 +31 35 1 32539 52034 40362 +32 35 1 33358 52006 40732 +33 35 1 34206 51976 41341 +34 35 1 34783 51942 41747 +35 35 1 35636 51901 42092 +36 35 1 36393 51851 42542 +37 35 1 36961 51787 42857 +38 35 1 37796 51708 43415 +39 35 1 38611 51608 43766 +40 35 1 39263 51485 44084 +41 35 1 39875 51369 44447 +42 35 1 40727 51274 44758 +43 35 1 41325 51198 45306 +44 35 1 41991 51136 45659 +45 35 1 42838 51087 46056 +46 35 1 43404 51046 46416 +47 35 1 44126 51010 46757 +48 35 1 44964 50975 47314 +49 35 1 45515 50937 47738 +50 35 1 46286 50891 48037 +51 35 1 47110 50835 48583 +52 35 1 47660 50765 49038 +53 35 1 48469 50675 49298 +54 35 1 49267 50562 49782 +55 35 1 49810 50429 50008 +56 35 1 50623 50314 50491 +57 35 1 51420 50223 50977 +58 35 1 52167 50151 51243 +59 35 1 52706 50094 51687 +60 35 1 53528 50049 51997 +61 35 1 54245 50010 52425 +62 35 1 54790 49975 52963 +63 35 1 55615 49940 53276 +0 36 1 10492 54371 28051 +1 36 1 11175 54319 28358 +2 36 1 11967 54273 28730 +3 36 1 12782 54234 29292 +4 36 1 13328 54199 29620 +5 36 1 14047 54169 29983 +6 36 1 14871 54142 30554 +7 36 1 15412 54118 30902 +8 36 1 16164 54097 31315 +9 36 1 16984 54076 31690 +10 36 1 17528 54056 32047 +11 36 1 18321 54036 32633 +12 36 1 19135 54015 33028 +13 36 1 19688 53992 33388 +14 36 1 20512 53966 33835 +15 36 1 21288 53938 34161 +16 36 1 21840 53904 34742 +17 36 1 22679 53866 35134 +18 36 1 23404 53821 35448 +19 36 1 23969 53768 36016 +20 36 1 24797 53707 36389 +21 36 1 25609 53636 36711 +22 36 1 26258 53552 37103 +23 36 1 26870 53467 37415 +24 36 1 27722 53391 37970 +25 36 1 28321 53325 38302 +26 36 1 28993 53267 38713 +27 36 1 29846 53216 39044 +28 36 1 30415 53173 39421 +29 36 1 31168 53135 40005 +30 36 1 32020 53102 40362 +31 36 1 32594 53072 40754 +32 36 1 33438 53043 41364 +33 36 1 34261 53014 41746 +34 36 1 34830 52983 42124 +35 36 1 35682 52946 42553 +36 36 1 36414 52901 42900 +37 36 1 36987 52845 43467 +38 36 1 37818 52775 43816 +39 36 1 38634 52686 44149 +40 36 1 39269 52576 44514 +41 36 1 39894 52450 44825 +42 36 1 40746 52344 45366 +43 36 1 41333 52257 45684 +44 36 1 42015 52188 46093 +45 36 1 42861 52132 46427 +46 36 1 43421 52087 46783 +47 36 1 44160 52049 47350 +48 36 1 44995 52014 47740 +49 36 1 45546 51978 48067 +50 36 1 46333 51937 48634 +51 36 1 47153 51887 49064 +52 36 1 47705 51825 49360 +53 36 1 48520 51746 49840 +54 36 1 49303 51646 50088 +55 36 1 49845 51521 50593 +56 36 1 50661 51393 51036 +57 36 1 51456 51289 51284 +58 36 1 52189 51207 51738 +59 36 1 52732 51143 52015 +60 36 1 53557 51092 52477 +61 36 1 54261 51050 52991 +62 36 1 54812 51014 53284 +63 36 1 55640 50979 53700 +0 37 1 10520 55439 28400 +1 37 1 11231 55381 28801 +2 37 1 12021 55330 29355 +3 37 1 12833 55286 29648 +4 37 1 13372 55248 30043 +5 37 1 14119 55214 30612 +6 37 1 14934 55184 30928 +7 37 1 15473 55158 31370 +8 37 1 16254 55135 31718 +9 37 1 17065 55113 32104 +10 37 1 17613 55093 32693 +11 37 1 18427 55073 33054 +12 37 1 19214 55052 33440 +13 37 1 19761 55031 33855 +14 37 1 20591 55008 34210 +15 37 1 21336 54981 34790 +16 37 1 21891 54952 35155 +17 37 1 22733 54917 35498 +18 37 1 23428 54877 36067 +19 37 1 24005 54830 36425 +20 37 1 24828 54775 36765 +21 37 1 25642 54710 37151 +22 37 1 26267 54634 37487 +23 37 1 26900 54547 38037 +24 37 1 27752 54463 38341 +25 37 1 28335 54389 38758 +26 37 1 29033 54324 39071 +27 37 1 29885 54268 39460 +28 37 1 30449 54219 40039 +29 37 1 31230 54177 40367 +30 37 1 32080 54141 40782 +31 37 1 32661 54109 41386 +32 37 1 33510 54079 41743 +33 37 1 34304 54051 42153 +34 37 1 34870 54021 42555 +35 37 1 35722 53987 42935 +36 37 1 36430 53946 43501 +37 37 1 37011 53896 43847 +38 37 1 37837 53833 44195 +39 37 1 38655 53754 44568 +40 37 1 39274 53656 44908 +41 37 1 39915 53533 45441 +42 37 1 40765 53415 45728 +43 37 1 41343 53319 46138 +44 37 1 42042 53241 46448 +45 37 1 42886 53179 46817 +46 37 1 43442 53129 47385 +47 37 1 44197 53088 47740 +48 37 1 45029 53051 48095 +49 37 1 45581 53016 48673 +50 37 1 46383 52978 49073 +51 37 1 47197 52933 49410 +52 37 1 47747 52878 49871 +53 37 1 48565 52808 50151 +54 37 1 49334 52719 50688 +55 37 1 49876 52607 51109 +56 37 1 50693 52474 51346 +57 37 1 51488 52359 51798 +58 37 1 52207 52267 52049 +59 37 1 52755 52194 52537 +60 37 1 53583 52136 53019 +61 37 1 54274 52091 53295 +62 37 1 54831 52052 53748 +63 37 1 55661 52017 54263 +0 38 1 10562 56509 28890 +1 38 1 11301 56445 29426 +2 38 1 12091 56389 29691 +3 38 1 12898 56340 30119 +4 38 1 13433 56297 30676 +5 38 1 14208 56260 30965 +6 38 1 15014 56227 31432 +7 38 1 15556 56199 31753 +8 38 1 16362 56173 32171 +9 38 1 17151 56150 32748 +10 38 1 17694 56129 33080 +11 38 1 18517 56108 33488 +12 38 1 19272 56088 33872 +13 38 1 19821 56068 34257 +14 38 1 20656 56046 34829 +15 38 1 21370 56021 35169 +16 38 1 21934 55994 35544 +17 38 1 22778 55963 36107 +18 38 1 23444 55926 36451 +19 38 1 24037 55884 36808 +20 38 1 24855 55834 37185 +21 38 1 25672 55775 37551 +22 38 1 26276 55706 38102 +23 38 1 26933 55625 38392 +24 38 1 27784 55535 38812 +25 38 1 28355 55453 39108 +26 38 1 29081 55382 39511 +27 38 1 29930 55320 40080 +28 38 1 30495 55266 40381 +29 38 1 31304 55220 40817 +30 38 1 32149 55180 41410 +31 38 1 32724 55146 41743 +32 38 1 33572 55115 42182 +33 38 1 34339 55085 42554 +34 38 1 34904 55056 42964 +35 38 1 35756 55024 43525 +36 38 1 36443 54987 43867 +37 38 1 37032 54942 44229 +38 38 1 37856 54885 44602 +39 38 1 38674 54814 44974 +40 38 1 39280 54725 45512 +41 38 1 39937 54613 45790 +42 38 1 40786 54487 46194 +43 38 1 41356 54381 46484 +44 38 1 42071 54295 46863 +45 38 1 42913 54226 47424 +46 38 1 43467 54171 47745 +47 38 1 44238 54126 48124 +48 38 1 45067 54088 48705 +49 38 1 45622 54053 49074 +50 38 1 46437 54016 49451 +51 38 1 47237 53975 49885 +52 38 1 47783 53926 50198 +53 38 1 48605 53863 50756 +54 38 1 49360 53783 51162 +55 38 1 49903 53683 51422 +56 38 1 50721 53558 51870 +57 38 1 51516 53430 52105 +58 38 1 52222 53328 52607 +59 38 1 52775 53246 53052 +60 38 1 53606 53182 53316 +61 38 1 54284 53131 53798 +62 38 1 54848 53090 54284 +63 38 1 55681 53054 54604 +0 39 1 10623 57581 29507 +1 39 1 11390 57511 29752 +2 39 1 12181 57449 30213 +3 39 1 12980 57395 30748 +4 39 1 13517 57348 31017 +5 39 1 14317 57306 31501 +6 39 1 15103 57270 31795 +7 39 1 15642 57239 32242 +8 39 1 16457 57211 32797 +9 39 1 17217 57186 33107 +10 39 1 17760 57164 33533 +11 39 1 18589 57143 33887 +12 39 1 19314 57122 34304 +13 39 1 19869 57102 34860 +14 39 1 20709 57081 35179 +15 39 1 21392 57058 35586 +16 39 1 21970 57033 36137 +17 39 1 22816 57004 36472 +18 39 1 23455 56971 36844 +19 39 1 24067 56932 37209 +20 39 1 24882 56886 37606 +21 39 1 25703 56832 38151 +22 39 1 26288 56769 38440 +23 39 1 26972 56694 38870 +24 39 1 27821 56606 39156 +25 39 1 28384 56517 39574 +26 39 1 29139 56439 40127 +27 39 1 29984 56371 40406 +28 39 1 30553 56313 40861 +29 39 1 31389 56262 41440 +30 39 1 32210 56219 41750 +31 39 1 32776 56181 42212 +32 39 1 33625 56149 42555 +33 39 1 34365 56119 42993 +34 39 1 34934 56089 43542 +35 39 1 35786 56059 43879 +36 39 1 36452 56024 44254 +37 39 1 37053 55982 44621 +38 39 1 37873 55931 45024 +39 39 1 38694 55867 45559 +40 39 1 39288 55785 45847 +41 39 1 39960 55684 46257 +42 39 1 40808 55559 46537 +43 39 1 41372 55444 46924 +44 39 1 42104 55349 47470 +45 39 1 42943 55274 47759 +46 39 1 43497 55213 48160 +47 39 1 44284 55164 48735 +48 39 1 45108 55124 49073 +49 39 1 45666 55088 49488 +50 39 1 46486 55052 49888 +51 39 1 47271 55014 50235 +52 39 1 47816 54969 50805 +53 39 1 48641 54912 51193 +54 39 1 49381 54840 51482 +55 39 1 49927 54750 51931 +56 39 1 50746 54636 52183 +57 39 1 51541 54504 52692 +58 39 1 52234 54391 53096 +59 39 1 52793 54300 53351 +60 39 1 53626 54229 53854 +61 39 1 54292 54173 54306 +62 39 1 54864 54128 54622 +63 39 1 55698 54090 55013 +0 40 1 10706 58654 29834 +1 40 1 11498 58577 30324 +2 40 1 12291 58510 30827 +3 40 1 13069 58451 31080 +4 40 1 13604 58399 31569 +5 40 1 14415 58353 31840 +6 40 1 15172 58314 32314 +7 40 1 15711 58279 32842 +8 40 1 16535 58249 33137 +9 40 1 17264 58222 33575 +10 40 1 17814 58198 33903 +11 40 1 18648 58176 34351 +12 40 1 19343 58155 34886 +13 40 1 19910 58135 35189 +14 40 1 20752 58114 35628 +15 40 1 21407 58092 36159 +16 40 1 22002 58069 36491 +17 40 1 22849 58042 36873 +18 40 1 23464 58011 37226 +19 40 1 24100 57975 37654 +20 40 1 24910 57933 38187 +21 40 1 25735 57883 38479 +22 40 1 26307 57824 38916 +23 40 1 27018 57754 39205 +24 40 1 27864 57672 39648 +25 40 1 28423 57580 40182 +26 40 1 29207 57495 40442 +27 40 1 30049 57422 40915 +28 40 1 30624 57358 41475 +29 40 1 31467 57304 41765 +30 40 1 32259 57257 42244 +31 40 1 32821 57217 42561 +32 40 1 33670 57182 43023 +33 40 1 34385 57151 43557 +34 40 1 34961 57121 43887 +35 40 1 35813 57091 44275 +36 40 1 36459 57058 44633 +37 40 1 37074 57020 45064 +38 40 1 37891 56972 45589 +39 40 1 38714 56913 45889 +40 40 1 39297 56839 46302 +41 40 1 39987 56746 46595 +42 40 1 40833 56629 47001 +43 40 1 41392 56506 47526 +44 40 1 42141 56403 47787 +45 40 1 42977 56321 48205 +46 40 1 43531 56255 48768 +47 40 1 44333 56202 49076 +48 40 1 45153 56159 49523 +49 40 1 45707 56121 49887 +50 40 1 46530 56086 50268 +51 40 1 47299 56050 50841 +52 40 1 47845 56008 51209 +53 40 1 48673 55956 51531 +54 40 1 49399 55891 51968 +55 40 1 49949 55809 52252 +56 40 1 50767 55706 52785 +57 40 1 51563 55578 53155 +58 40 1 52244 55454 53405 +59 40 1 52809 55355 53919 +60 40 1 53644 55277 54333 +61 40 1 54298 55215 54646 +62 40 1 54879 55166 55042 +63 40 1 55714 55126 55359 +0 41 1 10795 59722 30440 +1 41 1 11598 59643 30904 +2 41 1 12388 59570 31150 +3 41 1 13137 59506 31633 +4 41 1 13674 59450 31890 +5 41 1 14494 59400 32389 +6 41 1 15221 59357 32883 +7 41 1 15767 59320 33172 +8 41 1 16598 59287 33613 +9 41 1 17296 59258 33922 +10 41 1 17858 59232 34399 +11 41 1 18696 59209 34907 +12 41 1 19361 59187 35199 +13 41 1 19945 59167 35668 +14 41 1 20789 59146 36176 +15 41 1 21418 59125 36508 +16 41 1 22034 59102 36897 +17 41 1 22881 59076 37240 +18 41 1 23474 59047 37698 +19 41 1 24136 59014 38213 +20 41 1 24944 58974 38512 +21 41 1 25772 58928 38953 +22 41 1 26334 58873 39244 +23 41 1 27074 58807 39717 +24 41 1 27916 58730 40240 +25 41 1 28476 58639 40492 +26 41 1 29289 58550 40980 +27 41 1 30123 58471 41517 +28 41 1 30690 58403 41790 +29 41 1 31534 58344 42280 +30 41 1 32297 58294 42573 +31 41 1 32859 58251 43057 +32 41 1 33709 58214 43572 +33 41 1 34399 58182 43896 +34 41 1 34985 58152 44295 +35 41 1 35837 58122 44640 +36 41 1 36465 58090 45098 +37 41 1 37095 58054 45608 +38 41 1 37909 58010 45920 +39 41 1 38735 57955 46332 +40 41 1 39309 57886 46636 +41 41 1 40016 57800 47074 +42 41 1 40860 57692 47591 +43 41 1 41416 57566 47831 +44 41 1 42182 57456 48262 +45 41 1 43014 57368 48806 +46 41 1 43571 57296 49087 +47 41 1 44387 57239 49560 +48 41 1 45193 57193 49888 +49 41 1 45743 57154 50299 +50 41 1 46569 57119 50868 +51 41 1 47324 57084 51216 +52 41 1 47870 57044 51570 +53 41 1 48701 56997 51988 +54 41 1 49414 56937 52306 +55 41 1 49968 56862 52854 +56 41 1 50786 56767 53212 +57 41 1 51583 56649 53480 +58 41 1 52251 56518 53996 +59 41 1 52825 56411 54371 +60 41 1 53661 56325 54681 +61 41 1 54304 56258 55074 +62 41 1 54892 56204 55376 +63 41 1 55729 56161 55873 +0 42 1 10867 60783 30972 +1 42 1 11676 60708 31227 +2 42 1 12465 60630 31694 +3 42 1 13186 60561 31947 +4 42 1 13730 60500 32466 +5 42 1 14558 60447 32921 +6 42 1 15255 60400 33212 +7 42 1 15813 60359 33649 +8 42 1 16649 60324 33945 +9 42 1 17317 60293 34449 +10 42 1 17895 60265 34924 +11 42 1 18736 60241 35213 +12 42 1 19373 60218 35709 +13 42 1 19978 60197 36188 +14 42 1 20822 60176 36527 +15 42 1 21427 60155 36918 +16 42 1 22068 60133 37252 +17 42 1 22913 60109 37740 +18 42 1 23489 60081 38232 +19 42 1 24178 60049 38542 +20 42 1 24986 60012 38983 +21 42 1 25816 59968 39277 +22 42 1 26372 59916 39778 +23 42 1 27142 59855 40286 +24 42 1 27979 59781 40545 +25 42 1 28545 59695 41055 +26 42 1 29379 59601 41566 +27 42 1 30185 59517 41826 +28 42 1 30745 59445 42319 +29 42 1 31590 59383 42594 +30 42 1 32325 59329 43096 +31 42 1 32892 59284 43590 +32 42 1 33742 59245 43907 +33 42 1 34410 59211 44314 +34 42 1 35008 59181 44647 +35 42 1 35859 59151 45128 +36 42 1 36472 59120 45619 +37 42 1 37118 59086 45945 +38 42 1 37930 59044 46352 +39 42 1 38758 58993 46664 +40 42 1 39324 58929 47130 +41 42 1 40049 58848 47640 +42 42 1 40890 58747 47887 +43 42 1 41445 58624 48334 +44 42 1 42227 58508 48853 +45 42 1 43056 58413 49110 +46 42 1 43615 58337 49599 +47 42 1 44438 58276 49896 +48 42 1 45228 58227 50333 +49 42 1 45775 58186 50892 +50 42 1 46604 58150 51219 +51 42 1 47344 58115 51603 +52 42 1 47893 58078 51996 +53 42 1 48726 58033 52349 +54 42 1 49426 57979 52903 +55 42 1 49986 57910 53249 +56 42 1 50802 57822 53551 +57 42 1 51601 57713 54082 +58 42 1 52258 57582 54424 +59 42 1 52839 57466 54728 +60 42 1 53676 57374 55112 +61 42 1 54308 57300 55404 +62 42 1 54906 57242 55920 +63 42 1 55742 57196 56346 +0 43 1 10924 61837 31301 +1 43 1 11737 61767 31752 +2 43 1 12528 61688 32012 +3 43 1 13218 61614 32544 +4 43 1 13776 61549 32959 +5 43 1 14609 61492 33259 +6 43 1 15277 61442 33682 +7 43 1 15852 61398 33975 +8 43 1 16690 61360 34499 +9 43 1 17331 61327 34940 +10 43 1 17929 61298 35231 +11 43 1 18771 61272 35751 +12 43 1 19383 61248 36198 +13 43 1 20012 61226 36548 +14 43 1 20855 61205 36935 +15 43 1 21439 61184 37266 +16 43 1 22107 61162 37779 +17 43 1 22950 61139 38246 +18 43 1 23512 61112 38571 +19 43 1 24230 61082 39007 +20 43 1 25038 61046 39307 +21 43 1 25869 61005 39832 +22 43 1 26424 60955 40323 +23 43 1 27223 60896 40593 +24 43 1 28054 60826 41128 +25 43 1 28620 60744 41619 +26 43 1 29457 60649 41873 +27 43 1 30233 60561 42363 +28 43 1 30792 60485 42624 +29 43 1 31637 60419 43140 +30 43 1 32346 60363 43610 +31 43 1 32921 60316 43923 +32 43 1 33771 60275 44334 +33 43 1 34418 60240 44656 +34 43 1 35030 60209 45156 +35 43 1 35881 60179 45626 +36 43 1 36479 60148 45965 +37 43 1 37143 60115 46365 +38 43 1 37953 60076 46685 +39 43 1 38783 60027 47175 +40 43 1 39344 59967 47672 +41 43 1 40086 59891 47930 +42 43 1 40924 59796 48407 +43 43 1 41479 59678 48909 +44 43 1 42277 59556 49148 +45 43 1 43101 59457 49643 +46 43 1 43658 59376 49913 +47 43 1 44483 59311 50372 +48 43 1 45257 59259 50915 +49 43 1 45805 59217 51223 +50 43 1 46635 59180 51634 +51 43 1 47362 59146 51999 +52 43 1 47914 59109 52385 +53 43 1 48749 59068 52938 +54 43 1 49436 59016 53270 +55 43 1 50002 58952 53607 +56 43 1 50817 58871 54149 +57 43 1 51617 58770 54483 +58 43 1 52263 58644 54789 +59 43 1 52852 58521 55160 +60 43 1 53690 58422 55446 +61 43 1 54312 58343 55972 +62 43 1 54919 58280 56363 +63 43 1 55755 58231 56709 +0 44 1 10971 62885 31795 +1 44 1 11783 62819 32082 +2 44 1 12578 62744 32624 +3 44 1 13239 62665 32997 +4 44 1 13815 62596 33311 +5 44 1 14652 62535 33715 +6 44 1 15291 62482 34012 +7 44 1 15887 62436 34552 +8 44 1 16727 62395 34956 +9 44 1 17341 62360 35256 +10 44 1 17963 62329 35794 +11 44 1 18804 62301 36207 +12 44 1 19394 62277 36573 +13 44 1 20049 62254 36951 +14 44 1 20890 62233 37284 +15 44 1 21458 62211 37818 +16 44 1 22154 62190 38257 +17 44 1 22992 62167 38601 +18 44 1 23546 62141 39029 +19 44 1 24293 62112 39337 +20 44 1 25105 62078 39883 +21 44 1 25934 62038 40354 +22 44 1 26493 61990 40637 +23 44 1 27318 61933 41192 +24 44 1 28126 61866 41665 +25 44 1 28683 61786 41927 +26 44 1 29523 61692 42411 +27 44 1 30269 61601 42664 +28 44 1 30831 61522 43190 +29 44 1 31677 61453 43635 +30 44 1 32361 61395 43944 +31 44 1 32947 61346 44357 +32 44 1 33797 61303 44668 +33 44 1 34425 61267 45186 +34 44 1 35053 61235 45632 +35 44 1 35903 61205 45982 +36 44 1 36488 61175 46375 +37 44 1 37170 61142 46700 +38 44 1 37980 61105 47211 +39 44 1 38811 61058 47691 +40 44 1 39368 61001 47962 +41 44 1 40128 60929 48465 +42 44 1 40962 60839 48959 +43 44 1 41518 60727 49201 +44 44 1 42333 60602 49693 +45 44 1 43144 60498 49942 +46 44 1 43697 60413 50419 +47 44 1 44524 60345 50942 +48 44 1 45282 60291 51230 +49 44 1 45831 60247 51664 +50 44 1 46663 60209 52001 +51 44 1 47376 60174 52418 +52 44 1 47933 60139 52962 +53 44 1 48769 60099 53282 +54 44 1 49444 60051 53652 +55 44 1 50017 59991 54196 +56 44 1 50831 59916 54527 +57 44 1 51632 59821 54851 +58 44 1 52267 59702 55219 +59 44 1 52865 59574 55504 +60 44 1 53703 59468 56031 +61 44 1 54316 59384 56387 +62 44 1 54931 59318 56739 +63 44 1 55768 59265 57105 +0 45 0 0 0 0 +1 45 0 0 0 0 +2 45 0 0 0 0 +3 45 1 13253 63710 33370 +4 45 1 13850 63628 33748 +5 45 1 14689 63556 34057 +6 45 1 15301 63494 34606 +7 45 1 15921 63440 34973 +8 45 1 16761 63394 35288 +9 45 1 17352 63355 35838 +10 45 1 18000 63321 36218 +11 45 1 18839 63292 36603 +12 45 1 19410 63267 36967 +13 45 1 20094 63245 37306 +14 45 1 20931 63225 37858 +15 45 1 21486 63206 38269 +16 45 1 22212 63187 38632 +17 45 1 23045 63168 39048 +18 45 1 23594 63146 39368 +19 45 1 24371 63121 39932 +20 45 1 25188 63092 40380 +21 45 1 26012 63057 40680 +22 45 1 26573 63014 41250 +23 45 1 27404 62962 41700 +24 45 1 28181 62899 41975 +25 45 1 28736 62823 42456 +26 45 1 29578 62731 42713 +27 45 1 30296 62637 43245 +28 45 1 30864 62555 43665 +29 45 1 31712 62485 43971 +30 45 1 32372 62425 44382 +31 45 1 32971 62373 44686 +32 45 1 33821 62330 45216 +33 45 1 34432 62293 45640 +34 45 1 35077 62260 46000 +35 45 1 35926 62230 46383 +36 45 1 36500 62200 46713 +37 45 1 37201 62168 47241 +38 45 1 38011 62131 47702 +39 45 1 38843 62087 47986 +40 45 1 39397 62032 48511 +41 45 1 40175 61963 48992 +42 45 1 41005 61876 49251 +43 45 1 41564 61769 49746 +44 45 1 42387 61644 49984 +45 45 1 43182 61536 50473 +46 45 1 43731 61448 50973 +47 45 1 44561 61378 51245 +48 45 1 45304 61321 51695 +49 45 1 45854 61275 52006 +50 45 1 46689 61237 52449 +51 45 1 47388 61202 52981 +52 45 1 47950 61167 53288 +53 45 1 48788 61129 53689 +54 45 1 49451 61083 54229 +55 45 1 50031 61027 54557 +56 45 1 50843 60955 54898 +57 45 1 51646 60866 55268 +58 45 1 52271 60754 55578 +59 45 1 52877 60624 56099 +60 45 1 53715 60514 56421 +61 45 1 54319 60425 56777 +62 45 1 54944 60354 57129 +63 45 1 55780 60299 57447 +0 46 0 0 0 0 +1 46 0 0 0 0 +2 46 0 0 0 0 +3 46 0 0 0 0 +4 46 0 0 0 0 +5 46 0 0 0 0 +6 46 0 0 0 0 +7 46 0 0 0 0 +8 46 0 0 0 0 +9 46 0 0 0 0 +10 46 0 0 0 0 +11 46 0 0 0 0 +12 46 0 0 0 0 +13 46 0 0 0 0 +14 46 0 0 0 0 +15 46 0 0 0 0 +16 46 0 0 0 0 +17 46 0 0 0 0 +18 46 0 0 0 0 +19 46 0 0 0 0 +20 46 0 0 0 0 +21 46 0 0 0 0 +22 46 0 0 0 0 +23 46 0 0 0 0 +24 46 0 0 0 0 +25 46 0 0 0 0 +26 46 0 0 0 0 +27 46 1 30315 63659 43698 +28 46 1 30893 63566 44004 +29 46 1 31741 63486 44409 +30 46 1 32380 63419 44709 +31 46 1 32995 63363 45249 +32 46 1 33844 63317 45649 +33 46 1 34440 63279 46019 +34 46 1 35104 63248 46392 +35 46 1 35950 63220 46726 +36 46 1 36516 63194 47268 +37 46 1 37237 63167 47708 +38 46 1 38047 63136 48006 +39 46 1 38878 63098 48548 +40 46 1 39432 63050 49012 +41 46 1 40228 62987 49293 +42 46 1 41052 62907 49787 +43 46 1 41611 62805 50037 +44 46 1 42436 62681 50537 +45 46 1 43214 62571 51011 +46 46 1 43762 62481 51268 +47 46 1 44593 62408 51729 +48 46 1 45322 62350 52016 +49 46 1 45875 62302 52482 +50 46 1 46711 62263 52997 +51 46 1 47398 62228 53292 +52 46 1 47966 62194 53722 +53 46 1 48805 62156 54251 +54 46 1 49456 62113 54579 +55 46 1 50044 62059 54934 +56 46 1 50855 61991 55302 +57 46 1 51659 61906 55643 +58 46 1 52275 61801 56172 +59 46 1 52890 61673 56469 +60 46 1 53727 61557 56823 +61 46 1 54323 61464 57161 +62 46 1 54957 61390 57479 +63 46 1 55792 61331 58015 +0 47 0 0 0 0 +1 47 0 0 0 0 +2 47 0 0 0 0 +3 47 0 0 0 0 +4 47 0 0 0 0 +5 47 0 0 0 0 +6 47 0 0 0 0 +7 47 0 0 0 0 +8 47 0 0 0 0 +9 47 0 0 0 0 +10 47 0 0 0 0 +11 47 0 0 0 0 +12 47 0 0 0 0 +13 47 0 0 0 0 +14 47 0 0 0 0 +15 47 0 0 0 0 +16 47 0 0 0 0 +17 47 0 0 0 0 +18 47 0 0 0 0 +19 47 0 0 0 0 +20 47 0 0 0 0 +21 47 0 0 0 0 +22 47 0 0 0 0 +23 47 0 0 0 0 +24 47 0 0 0 0 +25 47 0 0 0 0 +26 47 0 0 0 0 +27 47 0 0 0 0 +28 47 0 0 0 0 +29 47 0 0 0 0 +30 47 0 0 0 0 +31 47 0 0 0 0 +32 47 0 0 0 0 +33 47 0 0 0 0 +34 47 0 0 0 0 +35 47 0 0 0 0 +36 47 0 0 0 0 +37 47 0 0 0 0 +38 47 0 0 0 0 +39 47 0 0 0 0 +40 47 0 0 0 0 +41 47 0 0 0 0 +42 47 0 0 0 0 +43 47 0 0 0 0 +44 47 1 42480 63710 51056 +45 47 1 43241 63584 51301 +46 47 1 43789 63483 51766 +47 47 1 44623 63402 52033 +48 47 1 45336 63339 52518 +49 47 1 45895 63290 53014 +50 47 1 46732 63252 53298 +51 47 1 47406 63219 53752 +52 47 1 47981 63190 54267 +53 47 1 48820 63159 54594 +54 47 1 49461 63122 54961 +55 47 1 50057 63076 55323 +56 47 1 50866 63016 55696 +57 47 1 51672 62939 56227 +58 47 1 52278 62841 56523 +59 47 1 52902 62717 56879 +60 47 1 53739 62598 57201 +61 47 1 54327 62501 57522 +62 47 1 54970 62424 58060 +63 47 1 55804 62363 58414 diff --git a/tests/golden/stitch_rect_bic.txt b/tests/golden/stitch_rect_bic.txt new file mode 100644 index 0000000..ca546ea --- /dev/null +++ b/tests/golden/stitch_rect_bic.txt @@ -0,0 +1,3072 @@ +0 0 1 8544 12745 10252 +1 0 1 9428 12758 10679 +2 0 1 10316 12771 11349 +3 0 1 10922 12783 11709 +4 0 1 11790 12795 12208 +5 0 1 12632 12807 12734 +6 0 1 13268 12820 13109 +7 0 1 13950 12832 13510 +8 0 1 14834 12845 13898 +9 0 1 15431 12858 14536 +10 0 1 16293 12871 14913 +11 0 1 17176 12884 15445 +12 0 1 17777 12897 15796 +13 0 1 18658 12911 16338 +14 0 1 19362 12924 16845 +15 0 1 19975 12937 17162 +16 0 1 20846 12950 17755 +17 0 1 21448 12963 18139 +18 0 1 22172 12975 18608 +19 0 1 23051 12987 18952 +20 0 1 23650 12999 19445 +21 0 1 24529 13009 20074 +22 0 1 25406 13020 20400 +23 0 1 26192 13029 20923 +24 0 1 26778 13038 21236 +25 0 1 27647 13046 21819 +26 0 1 28302 13052 22241 +27 0 1 28941 13058 22643 +28 0 1 29809 13063 23013 +29 0 1 30397 13067 23428 +30 0 1 31155 13070 24041 +31 0 1 32032 13072 24360 +32 0 1 32640 13072 24893 +33 0 1 33523 13072 25486 +34 0 1 34330 13070 25799 +35 0 1 34913 13067 26304 +36 0 1 35783 13063 26614 +37 0 1 36456 13058 27197 +38 0 1 37080 13052 27596 +39 0 1 37911 13046 28010 +40 0 1 38757 13038 28364 +41 0 1 39343 13029 28788 +42 0 1 40129 13020 29401 +43 0 1 41014 13009 29726 +44 0 1 41632 12999 30262 +45 0 1 42516 12987 30838 +46 0 1 43290 12975 31157 +47 0 1 43879 12963 31628 +48 0 1 44751 12950 31947 +49 0 1 45393 12937 32521 +50 0 1 46053 12924 32916 +51 0 1 46929 12911 33350 +52 0 1 47519 12897 33706 +53 0 1 48336 12884 34150 +54 0 1 49237 12871 34793 +55 0 1 49856 12858 35140 +56 0 1 50736 12845 35645 +57 0 1 51585 12832 36188 +58 0 1 52267 12820 36536 +59 0 1 52903 12807 36944 +60 0 1 53783 12795 37295 +61 0 1 54381 12783 37887 +62 0 1 55187 12771 38311 +63 0 1 56106 12758 38770 +0 1 1 8539 13817 10607 +1 1 1 9418 13831 11278 +2 1 1 10311 13845 11668 +3 1 1 10918 13859 12178 +4 1 1 11787 13874 12735 +5 1 1 13244 14170 13128 +6 1 1 10179 12509 13337 +7 1 1 8248 11269 13613 +8 1 1 9921 11531 14313 +9 1 1 10826 11619 14751 +10 1 1 12068 11718 15340 +11 1 1 13345 11826 15773 +12 1 1 14264 11941 16395 +13 1 1 15523 12063 16961 +14 1 1 16555 12190 17332 +15 1 1 17461 12320 17957 +16 1 1 18662 12452 18379 +17 1 1 19525 12584 18839 +18 1 1 20511 12715 19206 +19 1 1 21654 12844 19703 +20 1 1 22454 12969 20319 +21 1 1 23549 13089 20659 +22 1 1 24616 13203 21210 +23 1 1 25542 13279 21533 +24 1 1 26407 13641 22029 +25 1 1 27717 14297 22247 +26 1 1 28302 14204 22658 +27 1 1 28941 14212 23053 +28 1 1 29809 14218 23453 +29 1 1 30397 14223 24058 +30 1 1 31154 14226 24369 +31 1 1 32032 14228 24885 +32 1 1 32640 14229 25481 +33 1 1 33523 14228 25797 +34 1 1 34330 14226 26337 +35 1 1 34914 14223 26640 +36 1 1 35784 14218 27233 +37 1 1 36456 14212 27643 +38 1 1 37081 14204 28030 +39 1 1 37912 14196 28404 +40 1 1 38758 14186 28812 +41 1 1 39343 14175 29413 +42 1 1 40131 14163 29722 +43 1 1 41016 14150 30248 +44 1 1 41635 14136 30842 +45 1 1 42519 14122 31153 +46 1 1 43292 14106 31664 +47 1 1 43881 14091 31969 +48 1 1 44753 14074 32549 +49 1 1 45394 14057 32946 +50 1 1 46055 14040 33360 +51 1 1 46931 14023 33712 +52 1 1 47521 14006 34141 +53 1 1 48341 13989 34762 +54 1 1 49243 13971 35104 +55 1 1 49860 13954 35636 +56 1 1 50740 13938 36196 +57 1 1 51588 13921 36529 +58 1 1 52268 13905 36962 +59 1 1 52905 13889 37299 +60 1 1 53786 13874 37882 +61 1 1 54384 13859 38277 +62 1 1 55194 13845 38743 +63 1 1 56114 13831 39152 +0 2 1 8534 14900 11212 +1 2 1 9408 14918 11600 +2 2 1 10306 14936 12132 +3 2 1 10914 14955 12730 +4 2 1 11784 14974 13076 +5 2 1 9348 13483 13345 +6 2 1 30466 22856 14891 +7 2 1 47408 30732 16120 +8 2 1 44937 29929 16083 +9 2 1 45095 30235 16146 +10 2 1 44989 30528 16235 +11 2 1 44780 30808 16276 +12 2 1 44731 31075 16336 +13 2 1 44387 31330 16367 +14 2 1 44128 31571 16376 +15 2 1 43901 31799 16392 +16 2 1 43385 32014 16386 +17 2 1 43067 32217 16375 +18 2 1 42593 32406 16354 +19 2 1 41919 32582 16330 +20 2 1 41481 32744 16299 +21 2 1 40718 32889 16264 +22 2 1 39915 33016 16225 +23 2 1 39543 33746 15969 +24 2 1 35291 27886 18003 +25 2 1 25840 12769 23565 +26 2 1 28302 15384 23107 +27 2 1 28940 15391 23492 +28 2 1 29809 15397 24091 +29 2 1 30396 15401 24402 +30 2 1 31154 15404 24899 +31 2 1 32032 15406 25480 +32 2 1 32640 15406 25796 +33 2 1 33523 15406 26359 +34 2 1 34330 15404 26669 +35 2 1 34914 15401 27272 +36 2 1 35784 15397 27698 +37 2 1 36456 15391 28060 +38 2 1 37081 15384 28462 +39 2 1 37913 15376 28858 +40 2 1 38759 15366 29450 +41 2 1 39344 15355 29752 +42 2 1 40134 15342 30261 +43 2 1 41019 15328 30844 +44 2 1 41637 15312 31159 +45 2 1 42522 15294 31703 +46 2 1 43293 15274 32003 +47 2 1 43882 15254 32593 +48 2 1 44755 15234 32999 +49 2 1 45394 15213 33384 +50 2 1 46057 15191 33751 +51 2 1 46933 15169 34162 +52 2 1 47524 15147 34765 +53 2 1 48347 15124 35080 +54 2 1 49248 15102 35625 +55 2 1 49864 15080 36205 +56 2 1 50743 15058 36523 +57 2 1 51591 15036 36997 +58 2 1 52268 15015 37316 +59 2 1 52908 14994 37898 +60 2 1 53789 14974 38277 +61 2 1 54387 14955 38736 +62 2 1 55202 14936 39095 +63 2 1 56122 14918 39591 +0 3 1 8529 16007 11550 +1 3 1 9399 16030 12064 +2 3 1 10300 16054 12710 +3 3 1 10910 16079 13051 +4 3 1 11781 16105 13555 +5 3 1 8528 14350 13718 +6 3 1 34309 25204 15418 +7 3 1 54355 34034 16498 +8 3 1 50307 32695 16325 +9 3 1 49657 32668 16322 +10 3 1 48771 32651 16320 +11 3 1 47864 32642 16320 +12 3 1 47258 32640 16320 +13 3 1 46375 32640 16320 +14 3 1 45665 32640 16320 +15 3 1 45055 32640 16320 +16 3 1 44183 32640 16320 +17 3 1 43579 32640 16320 +18 3 1 42860 32640 16320 +19 3 1 41981 32640 16320 +20 3 1 41383 32640 16320 +21 3 1 40505 32640 16320 +22 3 1 39626 32640 16320 +23 3 1 39193 33192 16091 +24 3 1 35047 27848 18296 +25 3 1 25901 14185 24050 +26 3 1 28302 16535 23522 +27 3 1 28940 16540 24116 +28 3 1 29808 16544 24432 +29 3 1 30396 16547 24913 +30 3 1 31153 16550 25479 +31 3 1 32032 16551 25792 +32 3 1 32640 16551 26345 +33 3 1 33524 16551 26672 +34 3 1 34331 16550 27289 +35 3 1 34914 16547 27731 +36 3 1 35785 16544 28078 +37 3 1 36457 16540 28508 +38 3 1 37082 16535 28895 +39 3 1 37913 16529 29481 +40 3 1 38760 16522 29786 +41 3 1 39345 16514 30280 +42 3 1 40136 16505 30846 +43 3 1 41022 16494 31159 +44 3 1 41640 16482 31721 +45 3 1 42524 16469 32033 +46 3 1 43294 16454 32638 +47 3 1 43884 16438 33060 +48 3 1 44756 16420 33419 +49 3 1 45394 16400 33816 +50 3 1 46059 16379 34212 +51 3 1 46936 16355 34802 +52 3 1 47526 16328 35102 +53 3 1 48352 16300 35624 +54 3 1 49254 16272 36215 +55 3 1 49868 16243 36528 +56 3 1 50747 16215 37045 +57 3 1 51594 16187 37349 +58 3 1 52269 16159 37936 +59 3 1 52911 16132 38313 +60 3 1 53792 16105 38747 +61 3 1 54390 16079 39087 +62 3 1 55209 16054 39558 +63 3 1 56130 16030 40196 +0 4 1 8525 17149 12016 +1 4 1 9389 17180 12665 +2 4 1 10294 17212 13020 +3 4 1 10906 17245 13580 +4 4 1 11778 17279 13909 +5 4 1 8633 15705 14396 +6 4 1 33944 25588 15674 +7 4 1 54227 33868 16442 +8 4 1 50204 32640 16320 +9 4 1 49608 32640 16320 +10 4 1 48758 32640 16320 +11 4 1 47866 32640 16320 +12 4 1 47261 32640 16320 +13 4 1 46378 32640 16320 +14 4 1 45666 32640 16320 +15 4 1 45056 32640 16320 +16 4 1 44185 32640 16320 +17 4 1 43580 32640 16320 +18 4 1 42862 32640 16320 +19 4 1 41983 32640 16320 +20 4 1 41385 32640 16320 +21 4 1 40508 32640 16320 +22 4 1 39628 32640 16320 +23 4 1 39189 33145 16084 +24 4 1 35066 28206 18357 +25 4 1 25899 15467 24519 +26 4 1 28302 17655 24133 +27 4 1 28939 17658 24456 +28 4 1 29808 17661 24923 +29 4 1 30396 17663 25476 +30 4 1 31153 17665 25797 +31 4 1 32031 17666 26311 +32 4 1 32640 17666 26649 +33 4 1 33524 17666 27280 +34 4 1 34331 17665 27737 +35 4 1 34915 17663 28084 +36 4 1 35785 17661 28533 +37 4 1 36457 17658 28918 +38 4 1 37083 17655 29501 +39 4 1 37914 17650 29812 +40 4 1 38761 17645 30294 +41 4 1 39346 17639 30843 +42 4 1 40138 17633 31161 +43 4 1 41024 17625 31697 +44 4 1 41643 17617 32033 +45 4 1 42527 17607 32654 +46 4 1 43295 17597 33093 +47 4 1 43886 17585 33440 +48 4 1 44758 17573 33867 +49 4 1 45395 17558 34256 +50 4 1 46061 17543 34839 +51 4 1 46938 17526 35140 +52 4 1 47529 17507 35647 +53 4 1 48358 17486 36218 +54 4 1 49259 17463 36540 +55 4 1 49872 17438 37091 +56 4 1 50750 17411 37397 +57 4 1 51597 17381 37994 +58 4 1 52270 17348 38380 +59 4 1 52914 17313 38777 +60 4 1 53795 17279 39127 +61 4 1 54393 17245 39572 +62 4 1 55217 17212 40180 +63 4 1 56137 17180 40510 +0 5 1 8520 18340 12644 +1 5 1 9380 18380 12986 +2 5 1 10289 18417 13588 +3 5 1 10901 18450 13924 +4 5 1 11775 18481 14555 +5 5 1 8697 17050 14896 +6 5 1 33734 26058 15788 +7 5 1 54298 33798 16418 +8 5 1 50208 32640 16320 +9 5 1 49611 32640 16320 +10 5 1 48765 32640 16320 +11 5 1 47870 32640 16320 +12 5 1 47264 32640 16320 +13 5 1 46381 32640 16320 +14 5 1 45667 32640 16320 +15 5 1 45058 32640 16320 +16 5 1 44186 32640 16320 +17 5 1 43581 32640 16320 +18 5 1 42864 32640 16320 +19 5 1 41985 32640 16320 +20 5 1 41388 32640 16320 +21 5 1 40512 32640 16320 +22 5 1 39631 32640 16320 +23 5 1 39184 33101 16068 +24 5 1 35085 28554 18529 +25 5 1 25897 16721 25166 +26 5 1 28301 18749 24471 +27 5 1 28939 18752 24929 +28 5 1 29807 18754 25473 +29 5 1 30396 18756 25806 +30 5 1 31153 18757 26278 +31 5 1 32031 18757 26617 +32 5 1 32640 18758 27246 +33 5 1 33524 18757 27715 +34 5 1 34331 18757 28081 +35 5 1 34915 18756 28539 +36 5 1 35786 18754 28928 +37 5 1 36457 18752 29511 +38 5 1 37083 18749 29829 +39 5 1 37915 18746 30303 +40 5 1 38762 18743 30838 +41 5 1 39347 18739 31169 +42 5 1 40141 18734 31661 +43 5 1 41027 18729 32004 +44 5 1 41645 18723 32641 +45 5 1 42530 18716 33095 +46 5 1 43296 18709 33448 +47 5 1 43887 18700 33893 +48 5 1 44759 18691 34283 +49 5 1 45395 18681 34862 +50 5 1 46063 18671 35169 +51 5 1 46940 18659 35662 +52 5 1 47532 18645 36212 +53 5 1 48363 18631 36535 +54 5 1 49264 18615 37091 +55 5 1 49876 18597 37420 +56 5 1 50753 18578 38035 +57 5 1 51600 18557 38443 +58 5 1 52271 18534 38813 +59 5 1 52916 18509 39195 +60 5 1 53798 18481 39623 +61 5 1 54396 18450 40209 +62 5 1 55224 18417 40514 +63 5 1 56145 18380 41102 +0 6 1 8516 19540 12979 +1 6 1 9371 19567 13550 +2 6 1 10283 19592 13907 +3 6 1 10897 19614 14553 +4 6 1 11771 19634 14998 +5 6 1 8761 18334 15335 +6 6 1 33524 26517 16062 +7 6 1 54368 33728 16376 +8 6 1 50211 32640 16320 +9 6 1 49615 32640 16320 +10 6 1 48771 32640 16320 +11 6 1 47874 32640 16320 +12 6 1 47268 32640 16320 +13 6 1 46384 32640 16320 +14 6 1 45668 32640 16320 +15 6 1 45059 32640 16320 +16 6 1 44188 32640 16320 +17 6 1 43581 32640 16320 +18 6 1 42866 32640 16320 +19 6 1 41987 32640 16320 +20 6 1 41390 32640 16320 +21 6 1 40515 32640 16320 +22 6 1 39633 32640 16320 +23 6 1 39180 33059 16058 +24 6 1 35104 28886 18649 +25 6 1 25894 17928 25591 +26 6 1 28301 19803 24930 +27 6 1 28938 19805 25470 +28 6 1 29807 19806 25815 +29 6 1 30395 19807 26251 +30 6 1 31152 19808 26596 +31 6 1 32031 19809 27204 +32 6 1 32640 19809 27666 +33 6 1 33524 19809 28068 +34 6 1 34331 19808 28523 +35 6 1 34915 19807 28925 +36 6 1 35786 19806 29514 +37 6 1 36457 19805 29837 +38 6 1 37084 19803 30308 +39 6 1 37915 19802 30834 +40 6 1 38763 19799 31179 +41 6 1 39348 19797 31629 +42 6 1 40143 19794 31975 +43 6 1 41030 19790 32602 +44 6 1 41648 19787 33065 +45 6 1 42532 19782 33446 +46 6 1 43298 19778 33896 +47 6 1 43889 19773 34294 +48 6 1 44761 19767 34873 +49 6 1 45396 19761 35187 +50 6 1 46066 19754 35672 +51 6 1 46943 19747 36203 +52 6 1 47535 19739 36538 +53 6 1 48369 19729 37049 +54 6 1 49269 19719 37406 +55 6 1 49880 19709 38041 +56 6 1 50757 19696 38469 +57 6 1 51603 19683 38830 +58 6 1 52271 19668 39241 +59 6 1 52919 19652 39660 +60 6 1 53801 19634 40233 +61 6 1 54400 19614 40533 +62 6 1 55232 19592 41098 +63 6 1 56152 19567 41662 +0 7 1 8511 20743 13505 +1 7 1 9363 20767 13855 +2 7 1 10276 20788 14517 +3 7 1 10892 20808 14995 +4 7 1 11768 20826 15367 +5 7 1 8826 19667 15820 +6 7 1 33314 27014 16185 +7 7 1 54439 33648 16350 +8 7 1 50214 32640 16320 +9 7 1 49618 32640 16320 +10 7 1 48777 32640 16320 +11 7 1 47879 32640 16320 +12 7 1 47271 32640 16320 +13 7 1 46387 32640 16320 +14 7 1 45668 32640 16320 +15 7 1 45061 32640 16320 +16 7 1 44189 32640 16320 +17 7 1 43582 32640 16320 +18 7 1 42868 32640 16320 +19 7 1 41989 32640 16320 +20 7 1 41393 32640 16320 +21 7 1 40518 32640 16320 +22 7 1 39635 32640 16320 +23 7 1 39175 33014 16051 +24 7 1 35123 29258 18719 +25 7 1 25892 19311 26100 +26 7 1 28301 21013 25468 +27 7 1 28938 21015 25820 +28 7 1 29806 21017 26233 +29 7 1 30395 21018 26583 +30 7 1 31152 21019 27174 +31 7 1 32031 21020 27613 +32 7 1 32640 21020 28041 +33 7 1 33525 21020 28487 +34 7 1 34332 21019 28908 +35 7 1 34916 21018 29508 +36 7 1 35787 21017 29835 +37 7 1 36457 21015 30310 +38 7 1 37084 21013 30831 +39 7 1 37916 21010 31185 +40 7 1 38764 21007 31604 +41 7 1 39349 21003 31956 +42 7 1 40145 20999 32564 +43 7 1 41033 20994 33010 +44 7 1 41650 20989 33432 +45 7 1 42535 20984 33876 +46 7 1 43299 20978 34291 +47 7 1 43890 20972 34876 +48 7 1 44762 20965 35195 +49 7 1 45396 20957 35678 +50 7 1 46068 20949 36195 +51 7 1 46945 20940 36545 +52 7 1 47537 20931 37006 +53 7 1 48375 20921 37366 +54 7 1 49274 20910 38016 +55 7 1 49884 20899 38458 +56 7 1 50760 20886 38835 +57 7 1 51605 20873 39262 +58 7 1 52272 20858 39681 +59 7 1 52922 20843 40246 +60 7 1 53804 20826 40548 +61 7 1 54403 20808 41091 +62 7 1 55240 20788 41627 +63 7 1 56159 20767 41989 +0 8 1 8507 22100 13819 +1 8 1 9354 22113 14445 +2 8 1 10270 22124 14952 +3 8 1 10888 22135 15348 +4 8 1 11765 22144 15799 +5 8 1 8891 21127 16293 +6 8 1 33104 27579 16509 +7 8 1 54509 33555 16298 +8 8 1 50217 32640 16320 +9 8 1 49621 32640 16320 +10 8 1 48783 32640 16320 +11 8 1 47884 32640 16320 +12 8 1 47275 32640 16320 +13 8 1 46390 32640 16320 +14 8 1 45669 32640 16320 +15 8 1 45062 32640 16320 +16 8 1 44191 32640 16320 +17 8 1 43583 32640 16320 +18 8 1 42870 32640 16320 +19 8 1 41991 32640 16320 +20 8 1 41395 32640 16320 +21 8 1 40522 32640 16320 +22 8 1 39638 32640 16320 +23 8 1 39171 32969 16037 +24 8 1 35142 29633 18893 +25 8 1 25890 20719 26695 +26 8 1 28301 22241 25818 +27 8 1 28937 22242 26226 +28 8 1 29806 22243 26576 +29 8 1 30395 22244 27156 +30 8 1 31152 22244 27575 +31 8 1 32031 22245 28014 +32 8 1 32640 22245 28433 +33 8 1 33525 22245 28879 +34 8 1 34332 22244 29494 +35 8 1 34916 22244 29824 +36 8 1 35787 22243 30308 +37 8 1 36457 22242 30832 +38 8 1 37085 22241 31188 +39 8 1 37916 22239 31592 +40 8 1 38765 22237 31945 +41 8 1 39350 22235 32540 +42 8 1 40148 22233 32958 +43 8 1 41035 22231 33405 +44 8 1 41653 22228 33836 +45 8 1 42537 22225 34275 +46 8 1 43300 22222 34871 +47 8 1 43892 22219 35193 +48 8 1 44764 22215 35681 +49 8 1 45397 22211 36191 +50 8 1 46070 22207 36552 +51 8 1 46948 22203 36973 +52 8 1 47540 22198 37335 +53 8 1 48381 22193 37967 +54 8 1 49278 22188 38414 +55 8 1 49888 22182 38830 +56 8 1 50763 22175 39258 +57 8 1 51608 22169 39689 +58 8 1 52273 22161 40251 +59 8 1 52925 22153 40558 +60 8 1 53808 22144 41086 +61 8 1 54407 22135 41593 +62 8 1 55248 22124 41964 +63 8 1 56166 22113 42489 +0 9 1 8503 23150 14388 +1 9 1 9345 23159 14875 +2 9 1 10264 23167 15311 +3 9 1 10883 23175 15773 +4 9 1 11761 23183 16212 +5 9 1 8957 22282 16885 +6 9 1 32894 28027 16675 +7 9 1 54579 33481 16263 +8 9 1 50220 32640 16320 +9 9 1 49624 32640 16320 +10 9 1 48789 32640 16320 +11 9 1 47888 32640 16320 +12 9 1 47278 32640 16320 +13 9 1 46393 32640 16320 +14 9 1 45670 32640 16320 +15 9 1 45064 32640 16320 +16 9 1 44192 32640 16320 +17 9 1 43583 32640 16320 +18 9 1 42872 32640 16320 +19 9 1 41994 32640 16320 +20 9 1 41397 32640 16320 +21 9 1 40525 32640 16320 +22 9 1 39640 32640 16320 +23 9 1 39166 32931 16031 +24 9 1 35161 29949 18970 +25 9 1 25887 21911 27126 +26 9 1 28301 23282 26231 +27 9 1 28937 23283 26575 +28 9 1 29806 23285 27148 +29 9 1 30395 23286 27556 +30 9 1 31151 23287 27999 +31 9 1 32030 23287 28381 +32 9 1 32640 23287 28836 +33 9 1 33525 23287 29470 +34 9 1 34332 23287 29804 +35 9 1 34917 23286 30302 +36 9 1 35788 23285 30835 +37 9 1 36457 23283 31185 +38 9 1 37085 23282 31594 +39 9 1 37917 23280 31943 +40 9 1 38766 23278 32528 +41 9 1 39351 23275 32926 +42 9 1 40150 23272 33383 +43 9 1 41038 23269 33780 +44 9 1 41655 23266 34246 +45 9 1 42540 23262 34858 +46 9 1 43301 23259 35182 +47 9 1 43893 23254 35681 +48 9 1 44765 23250 36192 +49 9 1 45397 23246 36554 +50 9 1 46072 23241 36956 +51 9 1 46950 23236 37318 +52 9 1 47543 23231 37930 +53 9 1 48387 23226 38348 +54 9 1 49283 23221 38813 +55 9 1 49892 23215 39231 +56 9 1 50766 23209 39683 +57 9 1 51611 23203 40252 +58 9 1 52274 23196 40562 +59 9 1 52928 23190 41084 +60 9 1 53811 23183 41568 +61 9 1 54411 23175 41952 +62 9 1 55257 23167 42419 +63 9 1 56172 23159 42834 +0 10 1 8499 24225 14819 +1 10 1 9337 24232 15257 +2 10 1 10257 24240 15715 +3 10 1 10878 24247 16176 +4 10 1 11758 24254 16780 +5 10 1 9024 23471 17248 +6 10 1 32683 28505 16868 +7 10 1 54649 33400 16227 +8 10 1 50223 32640 16320 +9 10 1 49627 32640 16320 +10 10 1 48795 32640 16320 +11 10 1 47893 32640 16320 +12 10 1 47282 32640 16320 +13 10 1 46396 32640 16320 +14 10 1 45671 32640 16320 +15 10 1 45066 32640 16320 +16 10 1 44194 32640 16320 +17 10 1 43584 32640 16320 +18 10 1 42874 32640 16320 +19 10 1 41996 32640 16320 +20 10 1 41400 32640 16320 +21 10 1 40528 32640 16320 +22 10 1 39642 32640 16320 +23 10 1 39162 32893 16021 +24 10 1 35180 30274 19097 +25 10 1 25885 23150 27609 +26 10 1 28301 24364 26578 +27 10 1 28936 24366 27151 +28 10 1 29805 24368 27555 +29 10 1 30394 24369 27994 +30 10 1 31151 24370 28353 +31 10 1 32030 24371 28798 +32 10 1 32640 24371 29434 +33 10 1 33525 24371 29776 +34 10 1 34332 24370 30293 +35 10 1 34917 24369 30841 +36 10 1 35788 24368 31178 +37 10 1 36458 24366 31609 +38 10 1 37086 24364 31947 +39 10 1 37918 24362 32530 +40 10 1 38767 24359 32916 +41 10 1 39352 24356 33374 +42 10 1 40152 24352 33735 +43 10 1 41041 24349 34206 +44 10 1 41658 24345 34838 +45 10 1 42542 24341 35164 +46 10 1 43302 24336 35680 +47 10 1 43895 24331 36200 +48 10 1 44767 24326 36553 +49 10 1 45398 24321 36958 +50 10 1 46074 24316 37313 +51 10 1 46952 24310 37913 +52 10 1 47546 24304 38301 +53 10 1 48393 24298 38784 +54 10 1 49288 24292 39186 +55 10 1 49896 24286 39667 +56 10 1 50770 24280 40249 +57 10 1 51614 24274 40560 +58 10 1 52274 24267 41085 +59 10 1 52931 24260 41558 +60 10 1 53814 24254 41949 +61 10 1 54415 24247 42364 +62 10 1 55265 24240 42775 +63 10 1 56179 24232 43444 +0 11 1 8495 25300 15223 +1 11 1 9329 25308 15635 +2 11 1 10250 25316 16117 +3 11 1 10873 25324 16757 +4 11 1 11754 25331 17085 +5 11 1 9091 24665 17797 +6 11 1 32473 28998 17199 +7 11 1 54718 33314 16170 +8 11 1 50226 32640 16320 +9 11 1 49630 32640 16320 +10 11 1 48801 32640 16320 +11 11 1 47898 32640 16320 +12 11 1 47285 32640 16320 +13 11 1 46399 32640 16320 +14 11 1 45672 32640 16320 +15 11 1 45067 32640 16320 +16 11 1 44195 32640 16320 +17 11 1 43585 32640 16320 +18 11 1 42876 32640 16320 +19 11 1 41998 32640 16320 +20 11 1 41402 32640 16320 +21 11 1 40532 32640 16320 +22 11 1 39645 32640 16320 +23 11 1 39158 32856 16016 +24 11 1 35199 30601 19164 +25 11 1 25883 24414 28025 +26 11 1 28300 25468 27164 +27 11 1 28936 25470 27573 +28 11 1 29805 25473 27995 +29 11 1 30394 25474 28352 +30 11 1 31150 25475 28784 +31 11 1 32030 25476 29406 +32 11 1 32640 25476 29741 +33 11 1 33526 25476 30279 +34 11 1 34333 25475 30847 +35 11 1 34917 25474 31170 +36 11 1 35789 25473 31636 +37 11 1 36458 25470 31960 +38 11 1 37087 25468 32544 +39 11 1 37918 25465 32928 +40 11 1 38768 25461 33375 +41 11 1 39353 25457 33720 +42 11 1 40155 25453 34178 +43 11 1 41044 25449 34808 +44 11 1 41660 25443 35141 +45 11 1 42545 25438 35677 +46 11 1 43303 25432 36212 +47 11 1 43896 25426 36550 +48 11 1 44769 25420 36979 +49 11 1 45398 25414 37321 +50 11 1 46076 25407 37914 +51 11 1 46955 25400 38284 +52 11 1 47549 25393 38767 +53 11 1 48399 25385 39131 +54 11 1 49292 25378 39640 +55 11 1 49900 25370 40242 +56 11 1 50773 25363 40554 +57 11 1 51617 25355 41090 +58 11 1 52275 25347 41563 +59 11 1 52934 25339 41950 +60 11 1 53818 25331 42338 +61 11 1 54419 25324 42738 +62 11 1 55274 25316 43387 +63 11 1 56185 25308 43786 +0 12 1 8492 26389 15589 +1 12 1 9321 26398 16044 +2 12 1 10243 26408 16712 +3 12 1 10868 26418 17059 +4 12 1 11751 26428 17608 +5 12 1 9158 25877 18335 +6 12 1 32262 29514 17331 +7 12 1 54787 33223 16137 +8 12 1 50228 32640 16320 +9 12 1 49633 32640 16320 +10 12 1 48807 32640 16320 +11 12 1 47903 32640 16320 +12 12 1 47289 32640 16320 +13 12 1 46402 32640 16320 +14 12 1 45673 32640 16320 +15 12 1 45069 32640 16320 +16 12 1 44197 32640 16320 +17 12 1 43585 32640 16320 +18 12 1 42878 32640 16320 +19 12 1 42000 32640 16320 +20 12 1 41404 32640 16320 +21 12 1 40535 32640 16320 +22 12 1 39647 32640 16320 +23 12 1 39153 32819 16006 +24 12 1 35218 30932 19288 +25 12 1 25881 25712 28672 +26 12 1 28300 26601 27607 +27 12 1 28935 26604 28006 +28 12 1 29804 26607 28377 +29 12 1 30394 26609 28793 +30 12 1 31150 26611 29404 +31 12 1 32030 26611 29722 +32 12 1 32640 26612 30261 +33 12 1 33526 26611 30851 +34 12 1 34333 26611 31166 +35 12 1 34918 26609 31671 +36 12 1 35789 26607 31983 +37 12 1 36458 26604 32573 +38 12 1 37087 26601 32962 +39 12 1 37919 26597 33386 +40 12 1 38769 26593 33735 +41 12 1 39354 26588 34178 +42 12 1 40157 26583 34792 +43 12 1 41047 26577 35116 +44 12 1 41663 26571 35674 +45 12 1 42547 26564 36228 +46 12 1 43304 26557 36550 +47 12 1 43898 26549 37016 +48 12 1 44770 26542 37342 +49 12 1 45399 26533 37934 +50 12 1 46079 26525 38298 +51 12 1 46958 26516 38767 +52 12 1 47552 26507 39104 +53 12 1 48405 26497 39605 +54 12 1 49297 26488 40230 +55 12 1 49903 26478 40546 +56 12 1 50776 26468 41100 +57 12 1 51619 26458 41584 +58 12 1 52276 26448 41953 +59 12 1 52937 26438 42344 +60 12 1 53821 26428 42728 +61 12 1 54423 26418 43354 +62 12 1 55282 26408 43720 +63 12 1 56191 26398 44259 +0 13 1 8488 27505 16009 +1 13 1 9313 27518 16656 +2 13 1 10236 27531 17016 +3 13 1 10863 27544 17589 +4 13 1 11747 27557 18135 +5 13 1 9225 27120 18726 +6 13 1 32051 30059 17641 +7 13 1 54855 33124 16083 +8 13 1 50231 32640 16320 +9 13 1 49636 32640 16320 +10 13 1 48813 32640 16320 +11 13 1 47908 32640 16320 +12 13 1 47292 32640 16320 +13 13 1 46405 32640 16320 +14 13 1 45674 32640 16320 +15 13 1 45070 32640 16320 +16 13 1 44198 32640 16320 +17 13 1 43586 32640 16320 +18 13 1 42880 32640 16320 +19 13 1 42002 32640 16320 +20 13 1 41407 32640 16320 +21 13 1 40538 32640 16320 +22 13 1 39650 32640 16320 +23 13 1 39149 32782 15995 +24 13 1 35237 31271 19452 +25 13 1 25879 27054 29225 +26 13 1 28300 27773 28027 +27 13 1 28935 27777 28426 +28 13 1 29804 27780 28826 +29 13 1 30394 27783 29428 +30 13 1 31150 27784 29738 +31 13 1 32030 27786 30259 +32 13 1 32640 27786 30852 +33 13 1 33526 27786 31169 +34 13 1 34333 27784 31710 +35 13 1 34918 27783 32016 +36 13 1 35790 27780 32615 +37 13 1 36458 27777 33016 +38 13 1 37088 27773 33411 +39 13 1 37920 27769 33780 +40 13 1 38770 27763 34207 +41 13 1 39356 27757 34806 +42 13 1 40160 27751 35113 +43 13 1 41050 27744 35670 +44 13 1 41665 27736 36245 +45 13 1 42550 27728 36558 +46 13 1 43305 27719 37063 +47 13 1 43899 27710 37377 +48 13 1 44772 27700 37973 +49 13 1 45399 27690 38341 +50 13 1 46081 27679 38783 +51 13 1 46960 27668 39118 +52 13 1 47555 27657 39598 +53 13 1 48411 27645 40213 +54 13 1 49301 27633 40539 +55 13 1 49907 27621 41115 +56 13 1 50779 27608 41617 +57 13 1 51622 27596 41962 +58 13 1 52276 27583 42381 +59 13 1 52940 27570 42745 +60 13 1 53825 27557 43356 +61 13 1 54427 27544 43692 +62 13 1 55291 27531 44239 +63 13 1 56197 27518 44590 +0 14 1 8485 28663 16640 +1 14 1 9305 28680 16977 +2 14 1 10229 28696 17558 +3 14 1 10858 28713 18148 +4 14 1 11743 28730 18483 +5 14 1 9293 28408 19255 +6 14 1 31840 30640 17790 +7 14 1 54924 33017 16048 +8 14 1 50234 32640 16320 +9 14 1 49639 32640 16320 +10 14 1 48818 32640 16320 +11 14 1 47913 32640 16320 +12 14 1 47296 32640 16320 +13 14 1 46408 32640 16320 +14 14 1 45675 32640 16320 +15 14 1 45072 32640 16320 +16 14 1 44200 32640 16320 +17 14 1 43587 32640 16320 +18 14 1 42882 32640 16320 +19 14 1 42004 32640 16320 +20 14 1 41409 32640 16320 +21 14 1 40542 32640 16320 +22 14 1 39652 32640 16320 +23 14 1 39145 32745 15991 +24 14 1 35256 31610 19509 +25 14 1 25877 28411 29706 +26 14 1 28300 28956 28482 +27 14 1 28934 28958 28868 +28 14 1 29804 28961 29463 +29 14 1 30393 28963 29772 +30 14 1 31149 28964 30274 +31 14 1 32029 28965 30850 +32 14 1 32640 28965 31169 +33 14 1 33526 28965 31730 +34 14 1 34333 28964 32045 +35 14 1 34918 28963 32654 +36 14 1 35790 28961 33071 +37 14 1 36458 28958 33440 +38 14 1 37088 28956 33837 +39 14 1 37920 28952 34250 +40 14 1 38770 28948 34838 +41 14 1 39357 28944 35140 +42 14 1 40162 28939 35677 +43 14 1 41052 28933 36251 +44 14 1 41668 28927 36572 +45 14 1 42552 28920 37109 +46 14 1 43306 28912 37423 +47 14 1 43901 28904 38026 +48 14 1 44773 28895 38406 +49 14 1 45400 28885 38815 +50 14 1 46083 28875 39169 +51 14 1 46963 28863 39629 +52 14 1 47558 28851 40223 +53 14 1 48417 28838 40538 +54 14 1 49306 28824 41136 +55 14 1 49911 28809 41660 +56 14 1 50781 28794 41985 +57 14 1 51625 28778 42443 +58 14 1 52277 28762 42789 +59 14 1 52943 28746 43392 +60 14 1 53828 28730 43716 +61 14 1 54431 28713 44236 +62 14 1 55300 28696 44565 +63 14 1 56203 28680 45154 +0 15 1 8482 29875 16993 +1 15 1 9297 29893 17532 +2 15 1 10221 29910 18145 +3 15 1 10853 29926 18485 +4 15 1 11739 29940 19020 +5 15 1 9362 29726 19687 +6 15 1 31629 31247 18152 +7 15 1 54991 32904 15984 +8 15 1 50237 32640 16320 +9 15 1 49642 32640 16320 +10 15 1 48824 32640 16320 +11 15 1 47918 32640 16320 +12 15 1 47300 32640 16320 +13 15 1 46411 32640 16320 +14 15 1 45676 32640 16320 +15 15 1 45073 32640 16320 +16 15 1 44201 32640 16320 +17 15 1 43587 32640 16320 +18 15 1 42884 32640 16320 +19 15 1 42006 32640 16320 +20 15 1 41411 32640 16320 +21 15 1 40545 32640 16320 +22 15 1 39654 32640 16320 +23 15 1 39141 32711 15982 +24 15 1 35276 31934 19655 +25 15 1 25875 29723 30220 +26 15 1 28300 30099 28897 +27 15 1 28934 30101 29487 +28 15 1 29803 30102 29800 +29 15 1 30393 30104 30286 +30 15 1 31149 30104 30845 +31 15 1 32029 30105 31164 +32 15 1 32640 30105 31711 +33 15 1 33527 30105 32046 +34 15 1 34334 30104 32667 +35 15 1 34919 30104 33100 +36 15 1 35790 30102 33455 +37 15 1 36458 30101 33877 +38 15 1 37089 30099 34281 +39 15 1 37921 30097 34861 +40 15 1 38771 30094 35163 +41 15 1 39358 30091 35683 +42 15 1 40164 30088 36237 +43 15 1 41055 30084 36567 +44 15 1 41670 30080 37118 +45 15 1 42555 30075 37445 +46 15 1 43307 30070 38059 +47 15 1 43902 30064 38456 +48 15 1 44775 30058 38836 +49 15 1 45400 30052 39218 +50 15 1 46085 30045 39663 +51 15 1 46965 30037 40240 +52 15 1 47562 30029 40544 +53 15 1 48424 30021 41133 +54 15 1 49310 30011 41682 +55 15 1 49914 30001 42008 +56 15 1 50784 29991 42504 +57 15 1 51627 29979 42845 +58 15 1 52278 29967 43447 +59 15 1 52946 29954 43779 +60 15 1 53832 29940 44257 +61 15 1 54436 29926 44580 +62 15 1 55310 29910 45156 +63 15 1 56209 29893 45730 +0 16 1 8478 31067 17526 +1 16 1 9290 31079 18113 +2 16 1 10213 31089 18463 +3 16 1 10848 31099 19029 +4 16 1 11735 31108 19373 +5 16 1 9430 30991 20344 +6 16 1 31418 31842 18443 +7 16 1 55058 32792 15924 +8 16 1 50240 32640 16320 +9 16 1 49644 32640 16320 +10 16 1 48829 32640 16320 +11 16 1 47924 32640 16320 +12 16 1 47304 32640 16320 +13 16 1 46414 32640 16320 +14 16 1 45677 32640 16320 +15 16 1 45075 32640 16320 +16 16 1 44202 32640 16320 +17 16 1 43588 32640 16320 +18 16 1 42886 32640 16320 +19 16 1 42008 32640 16320 +20 16 1 41413 32640 16320 +21 16 1 40549 32640 16320 +22 16 1 39657 32640 16320 +23 16 1 39137 32679 15979 +24 16 1 35295 32245 19704 +25 16 1 25873 30999 30689 +26 16 1 28300 31210 29501 +27 16 1 28933 31212 29821 +28 16 1 29803 31213 30295 +29 16 1 30393 31214 30839 +30 16 1 31148 31214 31167 +31 16 1 32029 31214 31672 +32 16 1 32640 31215 32018 +33 16 1 33527 31214 32656 +34 16 1 34334 31214 33102 +35 16 1 34919 31214 33459 +36 16 1 35791 31213 33898 +37 16 1 36459 31212 34299 +38 16 1 37089 31210 34875 +39 16 1 37922 31209 35182 +40 16 1 38772 31207 35687 +41 16 1 39359 31205 36221 +42 16 1 40167 31203 36556 +43 16 1 41058 31201 37087 +44 16 1 41673 31198 37438 +45 16 1 42557 31195 38066 +46 16 1 43308 31192 38478 +47 16 1 43904 31188 38847 +48 16 1 44776 31184 39251 +49 16 1 45401 31180 39685 +50 16 1 46088 31175 40250 +51 16 1 46968 31171 40552 +52 16 1 47565 31165 41117 +53 16 1 48430 31160 41656 +54 16 1 49314 31154 42009 +55 16 1 49918 31147 42526 +56 16 1 50787 31141 42875 +57 16 1 51630 31133 43481 +58 16 1 52278 31126 43831 +59 16 1 52949 31117 44265 +60 16 1 53836 31108 44604 +61 16 1 54440 31099 45150 +62 16 1 55319 31089 45718 +63 16 1 56214 31079 46065 +0 17 1 8475 32209 18091 +1 17 1 9283 32216 18434 +2 17 1 10205 32222 18984 +3 17 1 10842 32228 19355 +4 17 1 11731 32234 19995 +5 17 1 9499 32207 20773 +6 17 1 31207 32429 18617 +7 17 1 55125 32680 15885 +8 17 1 50242 32640 16320 +9 17 1 49647 32640 16320 +10 17 1 48835 32640 16320 +11 17 1 47929 32640 16320 +12 17 1 47307 32640 16320 +13 17 1 46418 32640 16320 +14 17 1 45678 32640 16320 +15 17 1 45077 32640 16320 +16 17 1 44204 32640 16320 +17 17 1 43588 32640 16320 +18 17 1 42888 32640 16320 +19 17 1 42010 32640 16320 +20 17 1 41415 32640 16320 +21 17 1 40552 32640 16320 +22 17 1 39659 32640 16320 +23 17 1 39132 32649 15969 +24 17 1 35314 32546 19856 +25 17 1 25871 32247 31332 +26 17 1 28299 32298 29833 +27 17 1 28933 32299 30300 +28 17 1 29802 32300 30833 +29 17 1 30393 32300 31175 +30 17 1 31148 32301 31637 +31 17 1 32029 32301 31984 +32 17 1 32640 32301 32618 +33 17 1 33527 32301 33075 +34 17 1 34334 32301 33456 +35 17 1 34919 32300 33900 +36 17 1 35791 32300 34306 +37 17 1 36459 32299 34881 +38 17 1 37090 32298 35194 +39 17 1 37922 32297 35690 +40 17 1 38773 32296 36207 +41 17 1 39360 32295 36555 +42 17 1 40169 32294 37039 +43 17 1 41061 32292 37405 +44 17 1 41675 32290 38050 +45 17 1 42560 32288 38472 +46 17 1 43309 32286 38849 +47 17 1 43905 32284 39266 +48 17 1 44778 32281 39698 +49 17 1 45402 32279 40255 +50 17 1 46090 32276 40559 +51 17 1 46971 32273 41105 +52 17 1 47568 32269 41617 +53 17 1 48437 32266 41987 +54 17 1 49318 32262 42508 +55 17 1 49921 32258 42877 +56 17 1 50790 32254 43493 +57 17 1 51633 32249 43861 +58 17 1 52279 32244 44265 +59 17 1 52952 32239 44624 +60 17 1 53839 32234 45146 +61 17 1 54445 32228 45683 +62 17 1 55328 32222 46046 +63 17 1 56220 32216 46588 +0 18 1 8472 33314 18428 +1 18 1 9275 33318 18915 +2 18 1 10197 33322 19299 +3 18 1 10837 33325 19965 +4 18 1 11727 33329 20383 +5 18 1 9567 33387 21177 +6 18 1 30995 33014 18913 +7 18 1 55191 32567 15829 +8 18 1 50245 32640 16320 +9 18 1 49650 32640 16320 +10 18 1 48840 32640 16320 +11 18 1 47935 32640 16320 +12 18 1 47311 32640 16320 +13 18 1 46421 32640 16320 +14 18 1 45679 32640 16320 +15 18 1 45078 32640 16320 +16 18 1 44205 32640 16320 +17 18 1 43589 32640 16320 +18 18 1 42890 32640 16320 +19 18 1 42012 32640 16320 +20 18 1 41418 32640 16320 +21 18 1 40556 32640 16320 +22 18 1 39662 32640 16320 +23 18 1 39128 32621 15965 +24 18 1 35333 32839 19950 +25 18 1 25869 33478 31750 +26 18 1 28299 33371 30302 +27 18 1 28932 33372 30830 +28 18 1 29802 33372 31182 +29 18 1 30393 33373 31609 +30 18 1 31148 33373 31960 +31 18 1 32029 33373 32575 +32 18 1 32640 33373 33022 +33 18 1 33527 33373 33442 +34 18 1 34334 33373 33881 +35 18 1 34920 33373 34302 +36 18 1 35792 33372 34882 +37 18 1 36459 33372 35198 +38 18 1 37091 33371 35691 +39 18 1 37923 33371 36197 +40 18 1 38774 33370 36558 +41 18 1 39361 33369 36998 +42 18 1 40172 33368 37364 +43 18 1 41064 33367 38009 +44 18 1 41678 33366 38440 +45 18 1 42562 33364 38846 +46 18 1 43310 33363 39263 +47 18 1 43907 33361 39702 +48 18 1 44779 33360 40257 +49 18 1 45402 33358 40563 +50 18 1 46092 33356 41097 +51 18 1 46974 33354 41586 +52 18 1 47572 33352 41967 +53 18 1 48443 33349 42453 +54 18 1 49322 33347 42854 +55 18 1 49924 33344 43487 +56 18 1 50793 33342 43866 +57 18 1 51635 33339 44264 +58 18 1 52280 33336 44637 +59 18 1 52955 33332 45143 +60 18 1 53843 33329 45655 +61 18 1 54450 33325 46025 +62 18 1 55338 33322 46536 +63 18 1 56225 33318 46932 +0 19 1 8470 34396 18865 +1 19 1 9268 34399 19239 +2 19 1 10189 34402 19899 +3 19 1 10831 34404 20341 +4 19 1 11723 34407 20768 +5 19 1 9293 34576 21660 +6 19 1 32852 33424 18792 +7 19 1 60183 32016 14948 +8 19 1 54512 32241 15507 +9 19 1 53808 32238 15444 +10 19 1 52837 32235 15363 +11 19 1 51739 32232 15315 +12 19 1 50986 32229 15234 +13 19 1 49896 32226 15162 +14 19 1 48986 32224 15113 +15 19 1 48253 32221 15048 +16 19 1 47175 32219 14997 +17 19 1 46414 32217 14914 +18 19 1 45551 32215 14866 +19 19 1 44457 32214 14795 +20 19 1 43719 32212 14749 +21 19 1 42643 32211 14670 +22 19 1 41522 32209 14591 +23 19 1 40811 32150 14138 +24 19 1 36474 32811 18667 +25 19 1 25636 34766 32540 +26 19 1 28299 34438 30829 +27 19 1 28932 34439 31185 +28 19 1 29802 34439 31593 +29 19 1 30392 34439 31947 +30 19 1 31147 34439 32545 +31 19 1 32028 34440 32966 +32 19 1 32640 34440 33416 +33 19 1 33528 34440 33844 +34 19 1 34335 34439 34286 +35 19 1 34920 34439 34877 +36 19 1 35792 34439 35195 +37 19 1 36459 34439 35692 +38 19 1 37091 34438 36193 +39 19 1 37924 34438 36561 +40 19 1 38775 34437 36971 +41 19 1 39362 34436 37337 +42 19 1 40174 34435 37963 +43 19 1 41067 34435 38384 +44 19 1 41680 34434 38835 +45 19 1 42564 34433 39243 +46 19 1 43311 34431 39698 +47 19 1 43908 34430 40257 +48 19 1 44781 34429 40565 +49 19 1 45403 34428 41094 +50 19 1 46095 34426 41566 +51 19 1 46976 34425 41958 +52 19 1 47575 34423 42392 +53 19 1 48450 34421 42806 +54 19 1 49326 34419 43461 +55 19 1 49928 34418 43846 +56 19 1 50795 34416 44264 +57 19 1 51638 34413 44640 +58 19 1 52281 34411 45142 +59 19 1 52959 34409 45638 +60 19 1 53847 34407 46018 +61 19 1 54455 34404 46468 +62 19 1 55348 34402 46881 +63 19 1 56230 34399 47544 +0 20 1 8467 35472 19206 +1 20 1 9261 35474 19833 +2 20 1 10182 35476 20262 +3 20 1 10825 35478 20747 +4 20 1 11718 35480 21147 +5 20 1 11343 35564 21829 +6 20 1 20621 34995 21024 +7 20 1 31378 34297 19720 +8 20 1 29632 34422 20193 +9 20 1 29623 34432 20463 +10 20 1 29662 34442 20882 +11 20 1 29732 34450 21060 +12 20 1 29766 34459 21443 +13 20 1 29860 34467 21804 +14 20 1 29939 34475 22000 +15 20 1 29998 34482 22317 +16 20 1 30122 34489 22515 +17 20 1 30204 34496 22938 +18 20 1 30310 34502 23137 +19 20 1 30463 34507 23487 +20 20 1 30562 34512 23670 +21 20 1 30726 34517 24028 +22 20 1 30905 34522 24435 +23 20 1 31175 34501 24459 +24 20 1 30077 34792 26456 +25 20 1 26953 35651 31600 +26 20 1 28299 35508 31182 +27 20 1 28931 35508 31591 +28 20 1 29801 35508 31942 +29 20 1 30392 35509 32529 +30 20 1 31147 35509 32930 +31 20 1 32028 35509 33390 +32 20 1 32640 35509 33789 +33 20 1 33528 35509 34258 +34 20 1 34335 35509 34865 +35 20 1 34921 35509 35185 +36 20 1 35793 35508 35692 +37 20 1 36459 35508 36196 +38 20 1 37092 35508 36562 +39 20 1 37924 35507 36960 +40 20 1 38776 35507 37324 +41 20 1 39363 35506 37934 +42 20 1 40177 35505 38327 +43 20 1 41070 35504 38812 +44 20 1 41682 35504 39208 +45 20 1 42567 35503 39685 +46 20 1 43312 35502 40255 +47 20 1 43909 35500 40563 +48 20 1 44782 35499 41094 +49 20 1 45403 35498 41559 +50 20 1 46097 35497 41955 +51 20 1 46979 35495 42353 +52 20 1 47579 35494 42758 +53 20 1 48457 35492 43414 +54 20 1 49330 35491 43804 +55 20 1 49931 35489 44265 +56 20 1 50798 35488 44632 +57 20 1 51640 35486 45141 +58 20 1 52281 35484 45633 +59 20 1 52962 35482 46016 +60 20 1 53851 35480 46424 +61 20 1 54460 35478 46826 +62 20 1 55357 35476 47494 +63 20 1 56235 35474 47888 +0 21 1 8464 36554 19799 +1 21 1 9254 36556 20197 +2 21 1 10174 36558 20705 +3 21 1 10819 36560 21103 +4 21 1 11714 36562 21616 +5 21 1 12770 36552 22137 +6 21 1 12041 36639 22707 +7 21 1 10988 36746 23396 +8 21 1 12317 36728 23744 +9 21 1 13022 36728 24375 +10 21 1 13957 36728 24788 +11 21 1 15005 36728 25293 +12 21 1 15737 36728 25952 +13 21 1 16765 36728 26332 +14 21 1 17628 36728 26827 +15 21 1 18320 36728 27180 +16 21 1 19318 36728 27759 +17 21 1 20028 36728 28210 +18 21 1 20820 36728 28663 +19 21 1 21818 36728 29050 +20 21 1 22493 36728 29509 +21 21 1 23464 36728 30167 +22 21 1 24476 36728 30555 +23 21 1 25379 36731 31114 +24 21 1 26232 36691 31439 +25 21 1 27738 36568 31053 +26 21 1 28299 36589 31603 +27 21 1 28931 36590 31944 +28 21 1 29801 36590 32527 +29 21 1 30392 36590 32917 +30 21 1 31147 36590 33377 +31 21 1 32028 36591 33741 +32 21 1 32640 36591 34218 +33 21 1 33528 36591 34846 +34 21 1 34335 36590 35169 +35 21 1 34921 36590 35691 +36 21 1 35793 36590 36205 +37 21 1 36459 36590 36560 +38 21 1 37092 36589 36967 +39 21 1 37925 36589 37323 +40 21 1 38777 36588 37923 +41 21 1 39364 36588 38295 +42 21 1 40179 36587 38787 +43 21 1 41072 36586 39161 +44 21 1 41685 36585 39664 +45 21 1 42569 36584 40251 +46 21 1 43313 36583 40559 +47 21 1 43911 36582 41100 +48 21 1 44784 36581 41567 +49 21 1 45404 36580 41955 +50 21 1 46100 36579 42339 +51 21 1 46982 36577 42735 +52 21 1 47583 36576 43369 +53 21 1 48464 36574 43746 +54 21 1 49334 36573 44261 +55 21 1 49934 36571 44616 +56 21 1 50801 36570 45141 +57 21 1 51643 36568 45640 +58 21 1 52282 36566 46015 +59 21 1 52965 36564 46409 +60 21 1 53855 36562 46799 +61 21 1 54465 36560 47439 +62 21 1 55366 36558 47832 +63 21 1 56240 36556 48340 +0 22 1 8462 37660 20177 +1 22 1 9248 37662 20671 +2 22 1 10166 37664 21039 +3 22 1 10813 37666 21583 +4 22 1 11709 37668 22170 +5 22 1 12566 37669 22510 +6 22 1 13252 37671 22957 +7 22 1 13890 37673 23302 +8 22 1 14770 37674 23905 +9 22 1 15366 37676 24275 +10 22 1 16164 37677 24721 +11 22 1 17068 37679 25365 +12 22 1 17698 37680 25714 +13 22 1 18590 37682 26235 +14 22 1 19341 37683 26558 +15 22 1 19940 37684 27124 +16 22 1 20814 37685 27564 +17 22 1 21434 37686 27967 +18 22 1 22127 37687 28337 +19 22 1 23006 37688 28752 +20 22 1 23599 37689 29379 +21 22 1 24456 37690 29723 +22 22 1 25353 37690 30256 +23 22 1 26169 37691 30836 +24 22 1 26757 37692 31163 +25 22 1 27630 37692 31628 +26 22 1 28299 37693 31955 +27 22 1 28930 37693 32539 +28 22 1 29800 37693 32926 +29 22 1 30392 37693 33376 +30 22 1 31146 37694 33722 +31 22 1 32028 37694 34186 +32 22 1 32640 37694 34817 +33 22 1 33528 37694 35148 +34 22 1 34335 37694 35689 +35 22 1 34921 37693 36220 +36 22 1 35794 37693 36558 +37 22 1 36459 37693 36991 +38 22 1 37093 37693 37334 +39 22 1 37926 37692 37930 +40 22 1 38778 37692 38291 +41 22 1 39366 37691 38778 +42 22 1 40182 37690 39120 +43 22 1 41075 37690 39635 +44 22 1 41687 37689 40244 +45 22 1 42571 37688 40554 +46 22 1 43314 37687 41109 +47 22 1 43912 37686 41587 +48 22 1 44785 37685 41958 +49 22 1 45405 37684 42351 +50 22 1 46102 37683 42734 +51 22 1 46985 37682 43353 +52 22 1 47586 37680 43699 +53 22 1 48470 37679 44246 +54 22 1 49337 37677 44594 +55 22 1 49938 37676 45141 +56 22 1 50803 37674 45658 +57 22 1 51645 37673 46015 +58 22 1 52283 37671 46424 +59 22 1 52969 37669 46796 +60 22 1 53859 37668 47418 +61 22 1 54471 37666 47778 +62 22 1 55376 37664 48301 +63 22 1 56244 37662 48921 +0 23 1 8459 38804 20662 +1 23 1 9241 38806 21008 +2 23 1 10159 38807 21535 +3 23 1 10807 38809 22164 +4 23 1 11705 38810 22495 +5 23 1 12563 38811 22994 +6 23 1 13251 38812 23321 +7 23 1 13887 38813 23923 +8 23 1 14767 38815 24291 +9 23 1 15364 38816 24714 +10 23 1 16159 38817 25339 +11 23 1 17063 38818 25673 +12 23 1 17694 38819 26243 +13 23 1 18587 38820 26560 +14 23 1 19340 38820 27141 +15 23 1 19939 38821 27599 +16 23 1 20812 38822 27975 +17 23 1 21433 38823 28366 +18 23 1 22125 38823 28762 +19 23 1 23004 38824 29377 +20 23 1 23597 38825 29701 +21 23 1 24452 38825 30229 +22 23 1 25350 38826 30835 +23 23 1 26168 38826 31153 +24 23 1 26756 38827 31662 +25 23 1 27629 38827 31975 +26 23 1 28298 38827 32565 +27 23 1 28930 38828 32958 +28 23 1 29800 38828 33385 +29 23 1 30391 38828 33735 +30 23 1 31146 38828 34183 +31 23 1 32028 38828 34799 +32 23 1 32640 38828 35124 +33 23 1 33529 38828 35687 +34 23 1 34336 38828 36238 +35 23 1 34922 38828 36560 +36 23 1 35794 38828 37030 +37 23 1 36460 38828 37358 +38 23 1 37093 38827 37955 +39 23 1 37927 38827 38315 +40 23 1 38779 38827 38783 +41 23 1 39367 38826 39114 +42 23 1 40185 38826 39613 +43 23 1 41078 38825 40232 +44 23 1 41689 38825 40550 +45 23 1 42574 38824 41123 +46 23 1 43315 38823 41618 +47 23 1 43914 38823 41967 +48 23 1 44787 38822 42388 +49 23 1 45405 38821 42754 +50 23 1 46104 38820 43364 +51 23 1 46988 38820 43695 +52 23 1 47590 38819 44231 +53 23 1 48477 38818 44569 +54 23 1 49341 38817 45141 +55 23 1 49941 38816 45682 +56 23 1 50806 38815 46019 +57 23 1 51648 38813 46464 +58 23 1 52284 38812 46817 +59 23 1 52972 38811 47429 +60 23 1 53863 38810 47771 +61 23 1 54476 38809 48266 +62 23 1 55385 38807 48905 +63 23 1 56249 38806 49243 +0 24 1 8457 40003 21036 +1 24 1 9235 40003 21524 +2 24 1 10151 40003 22144 +3 24 1 10801 40003 22485 +4 24 1 11700 40003 23038 +5 24 1 12559 40003 23355 +6 24 1 13250 40003 23964 +7 24 1 13885 40003 24342 +8 24 1 14764 40003 24743 +9 24 1 15361 40003 25353 +10 24 1 16154 40003 25668 +11 24 1 17058 40003 26236 +12 24 1 17689 40003 26565 +13 24 1 18583 40003 27166 +14 24 1 19339 40003 27647 +15 24 1 19937 40003 27996 +16 24 1 20811 40003 28421 +17 24 1 21433 40003 28799 +18 24 1 22123 40003 29404 +19 24 1 23002 40003 29720 +20 24 1 23595 40003 30226 +21 24 1 24449 40003 30828 +22 24 1 25348 40003 31151 +23 24 1 26167 40003 31699 +24 24 1 26755 40003 32006 +25 24 1 27629 40003 32606 +26 24 1 28298 40003 33011 +27 24 1 28929 40003 33409 +28 24 1 29800 40003 33779 +29 24 1 30391 40003 34210 +30 24 1 31145 40003 34812 +31 24 1 32027 40003 35121 +32 24 1 32640 40003 35684 +33 24 1 33529 40003 36256 +34 24 1 34336 40003 36571 +35 24 1 34922 40003 37079 +36 24 1 35795 40003 37398 +37 24 1 36460 40003 37999 +38 24 1 37094 40003 38366 +39 24 1 37927 40003 38805 +40 24 1 38780 40003 39145 +41 24 1 39368 40003 39625 +42 24 1 40187 40003 40226 +43 24 1 41081 40003 40549 +44 24 1 41691 40003 41142 +45 24 1 42576 40003 41657 +46 24 1 43315 40003 41987 +47 24 1 43915 40003 42445 +48 24 1 44788 40003 42797 +49 24 1 45406 40003 43402 +50 24 1 46107 40003 43730 +51 24 1 46991 40003 44238 +52 24 1 47594 40003 44560 +53 24 1 48483 40003 45141 +54 24 1 49344 40003 45709 +55 24 1 49944 40003 46033 +56 24 1 50808 40003 46522 +57 24 1 51650 40003 46861 +58 24 1 52285 40003 47470 +59 24 1 52976 40003 47808 +60 24 1 53867 40003 48276 +61 24 1 54482 40003 48893 +62 24 1 55393 40003 49233 +63 24 1 56253 40003 49801 +0 25 1 8455 41218 21548 +1 25 1 9228 41217 22144 +2 25 1 10144 41215 22471 +3 25 1 10795 41214 23056 +4 25 1 11695 41213 23386 +5 25 1 12556 41211 24007 +6 25 1 13250 41210 24404 +7 25 1 13882 41209 24789 +8 25 1 14762 41207 25388 +9 25 1 15359 41206 25703 +10 25 1 16149 41205 26230 +11 25 1 17052 41204 26557 +12 25 1 17685 41203 27179 +13 25 1 18580 41202 27687 +14 25 1 19338 41201 28020 +15 25 1 19935 41200 28482 +16 25 1 20809 41199 28847 +17 25 1 21432 41198 29447 +18 25 1 22121 41198 29764 +19 25 1 23000 41197 30249 +20 25 1 23593 41196 30831 +21 25 1 24446 41196 31149 +22 25 1 25345 41195 31718 +23 25 1 26166 41195 32035 +24 25 1 26754 41194 32647 +25 25 1 27628 41194 33069 +26 25 1 28298 41194 33438 +27 25 1 28929 41193 33836 +28 25 1 29799 41193 34252 +29 25 1 30391 41193 34842 +30 25 1 31145 41193 35144 +31 25 1 32027 41193 35687 +32 25 1 32640 41193 36260 +33 25 1 33529 41193 36583 +34 25 1 34336 41193 37120 +35 25 1 34922 41193 37439 +36 25 1 35795 41193 38046 +37 25 1 36460 41193 38427 +38 25 1 37095 41194 38833 +39 25 1 37928 41194 39195 +40 25 1 38781 41194 39657 +41 25 1 39369 41195 40242 +42 25 1 40190 41195 40550 +43 25 1 41083 41196 41150 +44 25 1 41694 41196 41686 +45 25 1 42578 41197 42010 +46 25 1 43316 41198 42502 +47 25 1 43917 41198 42846 +48 25 1 44790 41199 43450 +49 25 1 45406 41200 43788 +50 25 1 46109 41201 44256 +51 25 1 46994 41202 44583 +52 25 1 47598 41203 45138 +53 25 1 48489 41204 45718 +54 25 1 49348 41205 46049 +55 25 1 49947 41206 46574 +56 25 1 50811 41207 46909 +57 25 1 51653 41209 47521 +58 25 1 52285 41210 47866 +59 25 1 52979 41211 48312 +60 25 1 53872 41213 48907 +61 25 1 54488 41214 49229 +62 25 1 55402 41215 49815 +63 25 1 56257 41217 50151 +0 26 1 8453 42432 22144 +1 26 1 9222 42429 22471 +2 26 1 10137 42426 23012 +3 26 1 10789 42424 23373 +4 26 1 11690 42421 24016 +5 26 1 12552 42419 24437 +6 26 1 13249 42417 24817 +7 26 1 13880 42414 25411 +8 26 1 14759 42412 25741 +9 26 1 15357 42410 26216 +10 26 1 16144 42408 26546 +11 26 1 17047 42406 27149 +12 26 1 17681 42404 27682 +13 26 1 18576 42402 28025 +14 26 1 19336 42401 28515 +15 26 1 19934 42399 28877 +16 26 1 20808 42398 29475 +17 26 1 21432 42396 29799 +18 26 1 22120 42395 30266 +19 26 1 22998 42394 30831 +20 26 1 23591 42392 31149 +21 26 1 24442 42391 31693 +22 26 1 25343 42390 32033 +23 26 1 26165 42390 32659 +24 26 1 26753 42389 33099 +25 26 1 27627 42388 33453 +26 26 1 28298 42387 33878 +27 26 1 28928 42387 34283 +28 26 1 29799 42386 34863 +29 26 1 30391 42386 35166 +30 26 1 31145 42386 35691 +31 26 1 32027 42386 36242 +32 26 1 32640 42386 36575 +33 26 1 33529 42386 37123 +34 26 1 34336 42386 37455 +35 26 1 34923 42386 38071 +36 26 1 35796 42386 38468 +37 26 1 36460 42387 38848 +38 26 1 37095 42387 39234 +39 26 1 37929 42388 39682 +40 26 1 38782 42389 40251 +41 26 1 39370 42390 40554 +42 26 1 40192 42390 41132 +43 26 1 41086 42391 41675 +44 26 1 41696 42392 42015 +45 26 1 42580 42394 42526 +46 26 1 43317 42395 42874 +47 26 1 43918 42396 43480 +48 26 1 44791 42398 43833 +49 26 1 45407 42399 44262 +50 26 1 46112 42401 44606 +51 26 1 46997 42402 45136 +52 26 1 47602 42404 45691 +53 26 1 48496 42406 46041 +54 26 1 49351 42408 46584 +55 26 1 49950 42410 46930 +56 26 1 50813 42412 47548 +57 26 1 51655 42414 47905 +58 26 1 52286 42417 48338 +59 26 1 52983 42419 48915 +60 26 1 53876 42421 49240 +61 26 1 54494 42424 49776 +62 26 1 55411 42426 50139 +63 26 1 56261 42429 50787 +0 27 1 8450 43789 22484 +1 27 1 9216 43787 22961 +2 27 1 10130 43784 23320 +3 27 1 10782 43782 23984 +4 27 1 11685 43780 24433 +5 27 1 12548 43778 24823 +6 27 1 13248 43776 25422 +7 27 1 13878 43774 25770 +8 27 1 14757 43772 26200 +9 27 1 15355 43770 26543 +10 27 1 16139 43768 27116 +11 27 1 17042 43767 27633 +12 27 1 17676 43765 28011 +13 27 1 18572 43763 28513 +14 27 1 19335 43762 28885 +15 27 1 19932 43760 29489 +16 27 1 20806 43759 29822 +17 27 1 21431 43758 30279 +18 27 1 22118 43756 30828 +19 27 1 22996 43755 31159 +20 27 1 23589 43754 31654 +21 27 1 24439 43753 31999 +22 27 1 25340 43752 32642 +23 27 1 26163 43751 33097 +24 27 1 26752 43751 33457 +25 27 1 27626 43750 33899 +26 27 1 28298 43750 34301 +27 27 1 28928 43749 34877 +28 27 1 29798 43749 35184 +29 27 1 30391 43748 35693 +30 27 1 31144 43748 36223 +31 27 1 32027 43748 36563 +32 27 1 32640 43748 37089 +33 27 1 33530 43748 37444 +34 27 1 34337 43748 38073 +35 27 1 34923 43748 38483 +36 27 1 35796 43749 38854 +37 27 1 36460 43749 39259 +38 27 1 37096 43750 39698 +39 27 1 37929 43750 40256 +40 27 1 38783 43751 40559 +41 27 1 39372 43751 41117 +42 27 1 40195 43752 41635 +43 27 1 41089 43753 42001 +44 27 1 41698 43754 42517 +45 27 1 42583 43755 42879 +46 27 1 43318 43756 43492 +47 27 1 43919 43758 43860 +48 27 1 44793 43759 44262 +49 27 1 45408 43760 44624 +50 27 1 46115 43762 45135 +51 27 1 47000 43763 45665 +52 27 1 47606 43765 46019 +53 27 1 48502 43767 46552 +54 27 1 49355 43768 46922 +55 27 1 49953 43770 47553 +56 27 1 50816 43772 47924 +57 27 1 51657 43774 48351 +58 27 1 52287 43776 48918 +59 27 1 52987 43778 49256 +60 27 1 53881 43780 49735 +61 27 1 54500 43782 50093 +62 27 1 55419 43784 50758 +63 27 1 56265 43787 51216 +0 28 1 8448 44840 22923 +1 28 1 9210 44838 23282 +2 28 1 10123 44836 23915 +3 28 1 10775 44833 24387 +4 28 1 11680 44831 24803 +5 28 1 12544 44829 25419 +6 28 1 13247 44827 25781 +7 28 1 13875 44825 26189 +8 28 1 14755 44823 26545 +9 28 1 15353 44822 27094 +10 28 1 16135 44820 27583 +11 28 1 17037 44818 27977 +12 28 1 17672 44817 28474 +13 28 1 18568 44815 28870 +14 28 1 19334 44814 29487 +15 28 1 19930 44812 29831 +16 28 1 20805 44811 30284 +17 28 1 21431 44810 30826 +18 28 1 22116 44809 31170 +19 28 1 22995 44807 31619 +20 28 1 23587 44806 31967 +21 28 1 24436 44806 32596 +22 28 1 25337 44805 33063 +23 28 1 26162 44804 33451 +24 28 1 26751 44803 33898 +25 28 1 27625 44803 34307 +26 28 1 28298 44802 34883 +27 28 1 28927 44802 35195 +28 28 1 29798 44801 35694 +29 28 1 30390 44801 36208 +30 28 1 31144 44801 36560 +31 28 1 32026 44801 37038 +32 28 1 32640 44801 37408 +33 28 1 33530 44801 38054 +34 28 1 34337 44801 38472 +35 28 1 34923 44801 38855 +36 28 1 35796 44801 39269 +37 28 1 36460 44802 39707 +38 28 1 37096 44802 40258 +39 28 1 37930 44803 40563 +40 28 1 38784 44803 41106 +41 28 1 39373 44804 41601 +42 28 1 40198 44805 41976 +43 28 1 41091 44806 42476 +44 28 1 41700 44806 42862 +45 28 1 42585 44807 43488 +46 28 1 43319 44809 43866 +47 28 1 43921 44810 44261 +48 28 1 44794 44811 44636 +49 28 1 45409 44812 45134 +50 28 1 46117 44814 45646 +51 28 1 47003 44815 46010 +52 28 1 47610 44817 46491 +53 28 1 48508 44818 46886 +54 28 1 49358 44820 47536 +55 28 1 49956 44822 47920 +56 28 1 50818 44823 48353 +57 28 1 51660 44825 48919 +58 28 1 52288 44827 49269 +59 28 1 52991 44829 49703 +60 28 1 53885 44831 50060 +61 28 1 54506 44833 50698 +62 28 1 55427 44836 51170 +63 28 1 56269 44838 51591 +0 29 1 8447 45918 23264 +1 29 1 9204 45915 23867 +2 29 1 10117 45912 24308 +3 29 1 10769 45910 24753 +4 29 1 11675 45907 25398 +5 29 1 12541 45905 25767 +6 29 1 13246 45903 26186 +7 29 1 13873 45901 26546 +8 29 1 14752 45899 27081 +9 29 1 15351 45897 27550 +10 29 1 16130 45895 27953 +11 29 1 17032 45893 28402 +12 29 1 17668 45891 28831 +13 29 1 18565 45890 29468 +14 29 1 19332 45888 29821 +15 29 1 19929 45887 30283 +16 29 1 20803 45885 30825 +17 29 1 21430 45884 31178 +18 29 1 22114 45883 31595 +19 29 1 22993 45882 31948 +20 29 1 23585 45880 32555 +21 29 1 24433 45879 33000 +22 29 1 25335 45879 33434 +23 29 1 26161 45878 33874 +24 29 1 26750 45877 34300 +25 29 1 27624 45876 34882 +26 29 1 28297 45876 35199 +27 29 1 28927 45875 35695 +28 29 1 29798 45875 36198 +29 29 1 30390 45875 36562 +30 29 1 31144 45875 36997 +31 29 1 32026 45874 37366 +32 29 1 32640 45874 38011 +33 29 1 33530 45874 38436 +34 29 1 34337 45875 38851 +35 29 1 34924 45875 39262 +36 29 1 35797 45875 39708 +37 29 1 36460 45875 40259 +38 29 1 37097 45876 40566 +39 29 1 37931 45876 41100 +40 29 1 38785 45877 41576 +41 29 1 39374 45878 41963 +42 29 1 40200 45879 42415 +43 29 1 41094 45879 42824 +44 29 1 41703 45880 43467 +45 29 1 42587 45882 43849 +46 29 1 43320 45883 44261 +47 29 1 43922 45884 44638 +48 29 1 44796 45885 45133 +49 29 1 45409 45887 45634 +50 29 1 46120 45888 46008 +51 29 1 47006 45890 46441 +52 29 1 47614 45891 46831 +53 29 1 48514 45893 47496 +54 29 1 49361 45895 47893 +55 29 1 49959 45897 48343 +56 29 1 50820 45899 48918 +57 29 1 51662 45901 49272 +58 29 1 52289 45903 49686 +59 29 1 52994 45905 50044 +60 29 1 53890 45907 50653 +61 29 1 54513 45910 51095 +62 29 1 55435 45912 51569 +63 29 1 56273 45915 51992 +0 30 1 8445 47001 23845 +1 30 1 9199 46997 24260 +2 30 1 10110 46993 24683 +3 30 1 10762 46989 25352 +4 30 1 11670 46986 25727 +5 30 1 12537 46983 26190 +6 30 1 13245 46980 26542 +7 30 1 13870 46977 27077 +8 30 1 14750 46974 27537 +9 30 1 15349 46971 27943 +10 30 1 16126 46969 28345 +11 30 1 17027 46966 28770 +12 30 1 17663 46964 29430 +13 30 1 18561 46962 29794 +14 30 1 19331 46960 30273 +15 30 1 19927 46958 30825 +16 30 1 20802 46956 31177 +17 30 1 21430 46955 31586 +18 30 1 22112 46953 31938 +19 30 1 22991 46952 32529 +20 30 1 23583 46950 32947 +21 30 1 24429 46949 33401 +22 30 1 25332 46948 33828 +23 30 1 26160 46947 34280 +24 30 1 26749 46946 34876 +25 30 1 27624 46945 35194 +26 30 1 28297 46945 35696 +27 30 1 28926 46944 36194 +28 30 1 29797 46944 36565 +29 30 1 30390 46943 36970 +30 30 1 31143 46943 37340 +31 30 1 32026 46943 37965 +32 30 1 32640 46943 38378 +33 30 1 33530 46943 38839 +34 30 1 34337 46943 39239 +35 30 1 34924 46943 39702 +36 30 1 35797 46944 40259 +37 30 1 36460 46944 40566 +38 30 1 37098 46945 41098 +39 30 1 37931 46945 41562 +40 30 1 38786 46946 41958 +41 30 1 39375 46947 42368 +42 30 1 40203 46948 42773 +43 30 1 41096 46949 43429 +44 30 1 41705 46950 43814 +45 30 1 42589 46952 44261 +46 30 1 43320 46953 44631 +47 30 1 43924 46955 45133 +48 30 1 44797 46956 45631 +49 30 1 45410 46958 46008 +50 30 1 46123 46960 46411 +51 30 1 47009 46962 46796 +52 30 1 47619 46964 47440 +53 30 1 48520 46966 47845 +54 30 1 49364 46969 48319 +55 30 1 49962 46971 48913 +56 30 1 50823 46974 49263 +57 30 1 51665 46977 49686 +58 30 1 52290 46980 50038 +59 30 1 52998 46983 50630 +60 30 1 53894 46986 51042 +61 30 1 54519 46989 51529 +62 30 1 55443 46993 51945 +63 30 1 56277 46997 52448 +0 31 1 8443 48106 24253 +1 31 1 9193 48100 24649 +2 31 1 10104 48093 25293 +3 31 1 10755 48088 25668 +4 31 1 11664 48082 26194 +5 31 1 12533 48077 26534 +6 31 1 13244 48072 27080 +7 31 1 13868 48067 27545 +8 31 1 14747 48063 27940 +9 31 1 15348 48058 28321 +10 31 1 16121 48055 28732 +11 31 1 17022 48051 29375 +12 31 1 17658 48047 29749 +13 31 1 18557 48044 30254 +14 31 1 19330 48041 30825 +15 31 1 19925 48038 31168 +16 31 1 20800 48036 31593 +17 31 1 21429 48033 31938 +18 31 1 22111 48031 32520 +19 31 1 22989 48029 32919 +20 31 1 23582 48027 33380 +21 31 1 24426 48026 33766 +22 31 1 25329 48024 34246 +23 31 1 26159 48023 34862 +24 31 1 26748 48021 35182 +25 31 1 27623 48020 35696 +26 31 1 28297 48019 36199 +27 31 1 28926 48018 36565 +28 31 1 29797 48018 36962 +29 31 1 30390 48017 37328 +30 31 1 31143 48017 37938 +31 31 1 32026 48017 38323 +32 31 1 32640 48017 38815 +33 31 1 33531 48017 39203 +34 31 1 34338 48017 39688 +35 31 1 34925 48017 40258 +36 31 1 35798 48018 40564 +37 31 1 36461 48018 41100 +38 31 1 37098 48019 41561 +39 31 1 37932 48020 41957 +40 31 1 38787 48021 42343 +41 31 1 39376 48023 42742 +42 31 1 40206 48024 43381 +43 31 1 41099 48026 43762 +44 31 1 41707 48027 44258 +45 31 1 42591 48029 44616 +46 31 1 43321 48031 45132 +47 31 1 43925 48033 45637 +48 31 1 44799 48036 46007 +49 31 1 45411 48038 46404 +50 31 1 46125 48041 46780 +51 31 1 47012 48044 47405 +52 31 1 47623 48047 47785 +53 31 1 48526 48051 48282 +54 31 1 49367 48055 48902 +55 31 1 49965 48058 49245 +56 31 1 50825 48063 49700 +57 31 1 51667 48067 50040 +58 31 1 52291 48072 50626 +59 31 1 53002 48077 51023 +60 31 1 53899 48082 51504 +61 31 1 54526 48088 51881 +62 31 1 55451 48093 52416 +63 31 1 56280 48100 53018 +0 32 1 8441 49252 24656 +1 32 1 9187 49241 25277 +2 32 1 10097 49230 25615 +3 32 1 10747 49220 26185 +4 32 1 11659 49211 26521 +5 32 1 12529 49202 27089 +6 32 1 13243 49194 27571 +7 32 1 13866 49187 27943 +8 32 1 14745 49179 28334 +9 32 1 15346 49173 28725 +10 32 1 16116 49166 29348 +11 32 1 17017 49160 29696 +12 32 1 17654 49155 30225 +13 32 1 18553 49150 30823 +14 32 1 19328 49145 31153 +15 32 1 19923 49140 31617 +16 32 1 20798 49136 31946 +17 32 1 21429 49132 32528 +18 32 1 22109 49129 32918 +19 32 1 22987 49126 33373 +20 32 1 23580 49123 33727 +21 32 1 24423 49120 34201 +22 32 1 25326 49117 34838 +23 32 1 26157 49115 35164 +24 32 1 26747 49113 35696 +25 32 1 27622 49112 36211 +26 32 1 28297 49110 36564 +27 32 1 28925 49109 36974 +28 32 1 29796 49108 37330 +29 32 1 30389 49107 37931 +30 32 1 31142 49107 38296 +31 32 1 32026 49106 38792 +32 32 1 32640 49106 39156 +33 32 1 33531 49106 39668 +34 32 1 34338 49107 40255 +35 32 1 34925 49107 40561 +36 32 1 35798 49108 41108 +37 32 1 36461 49109 41574 +38 32 1 37099 49110 41958 +39 32 1 37933 49112 42342 +40 32 1 38788 49113 42732 +41 32 1 39378 49115 43355 +42 32 1 40209 49117 43709 +43 32 1 41102 49120 44247 +44 32 1 41709 49123 44595 +45 32 1 42593 49126 45130 +46 32 1 43322 49129 45651 +47 32 1 43926 49132 46005 +48 32 1 44800 49136 46419 +49 32 1 45411 49140 46782 +50 32 1 46128 49145 47396 +51 32 1 47015 49150 47754 +52 32 1 47627 49155 48237 +53 32 1 48532 49160 48881 +54 32 1 49370 49166 49220 +55 32 1 49968 49173 49723 +56 32 1 50827 49179 50049 +57 32 1 51669 49187 50639 +58 32 1 52292 49194 51037 +59 32 1 53006 49202 51498 +60 32 1 53904 49211 51850 +61 32 1 54533 49220 52370 +62 32 1 55459 49230 53009 +63 32 1 56283 49241 53332 +0 33 1 8440 50456 25310 +1 33 1 9182 50437 25630 +2 33 1 10091 50420 26171 +3 33 1 10740 50404 26507 +4 33 1 11653 50390 27103 +5 33 1 12525 50375 27613 +6 33 1 13242 50362 27956 +7 33 1 13863 50350 28382 +8 33 1 14743 50338 28749 +9 33 1 15344 50328 29359 +10 33 1 16112 50317 29686 +11 33 1 17013 50308 30194 +12 33 1 17649 50299 30815 +13 33 1 18549 50291 31139 +14 33 1 19327 50283 31653 +15 33 1 19922 50276 31965 +16 33 1 20797 50270 32553 +17 33 1 21428 50263 32945 +18 33 1 22107 50258 33379 +19 33 1 22986 50253 33726 +20 33 1 23578 50248 34183 +21 33 1 24420 50244 34808 +22 33 1 25324 50240 35142 +23 33 1 26156 50236 35696 +24 33 1 26746 50233 36230 +25 33 1 27621 50231 36564 +26 33 1 28297 50228 37006 +27 33 1 28925 50227 37346 +28 33 1 29796 50225 37944 +29 33 1 30389 50224 38299 +30 33 1 31142 50223 38787 +31 33 1 32025 50222 39122 +32 33 1 32640 50222 39640 +33 33 1 33531 50222 40249 +34 33 1 34338 50223 40558 +35 33 1 34925 50224 41121 +36 33 1 35799 50225 41599 +37 33 1 36461 50227 41964 +38 33 1 37099 50228 42365 +39 33 1 37934 50231 42742 +40 33 1 38789 50233 43355 +41 33 1 39379 50236 43690 +42 33 1 40211 50240 44228 +43 33 1 41104 50244 44569 +44 33 1 41711 50248 45129 +45 33 1 42596 50253 45670 +46 33 1 43323 50258 46006 +47 33 1 43928 50263 46454 +48 33 1 44801 50270 46801 +49 33 1 45412 50276 47410 +50 33 1 46131 50283 47759 +51 33 1 47019 50291 48222 +52 33 1 47632 50299 48853 +53 33 1 48537 50308 49195 +54 33 1 49373 50317 49749 +55 33 1 49970 50328 50066 +56 33 1 50829 50338 50667 +57 33 1 51672 50350 51078 +58 33 1 52293 50362 51508 +59 33 1 53010 50375 51866 +60 33 1 53909 50390 52357 +61 33 1 54540 50404 52989 +62 33 1 55466 50420 53334 +63 33 1 56287 50437 53951 +0 34 1 8438 51679 25694 +1 34 1 9177 51663 26180 +2 34 1 10085 51647 26506 +3 34 1 10733 51631 27109 +4 34 1 11647 51614 27656 +5 34 1 12520 51599 27984 +6 34 1 13241 51583 28457 +7 34 1 13861 51567 28806 +8 34 1 14740 51552 29407 +9 34 1 15342 51537 29727 +10 34 1 16108 51522 30207 +11 34 1 17008 51508 30808 +12 34 1 17644 51494 31134 +13 34 1 18545 51482 31692 +14 34 1 19325 51470 31999 +15 34 1 19920 51459 32596 +16 34 1 20795 51449 33001 +17 34 1 21428 51439 33401 +18 34 1 22106 51431 33764 +19 34 1 22984 51423 34200 +20 34 1 23576 51416 34807 +21 34 1 24417 51409 35123 +22 34 1 25321 51403 35697 +23 34 1 26155 51398 36253 +24 34 1 26744 51393 36571 +25 34 1 27620 51389 37053 +26 34 1 28296 51386 37379 +27 34 1 28925 51383 37978 +28 34 1 29796 51380 38334 +29 34 1 30389 51378 38799 +30 34 1 31142 51377 39127 +31 34 1 32025 51376 39627 +32 34 1 32640 51376 40239 +33 34 1 33531 51376 40558 +34 34 1 34338 51377 41139 +35 34 1 34926 51378 41635 +36 34 1 35799 51380 41978 +37 34 1 36461 51383 42411 +38 34 1 37100 51386 42772 +39 34 1 37934 51389 43379 +40 34 1 38791 51393 43708 +41 34 1 39380 51398 44228 +42 34 1 40214 51403 44550 +43 34 1 41107 51409 45126 +44 34 1 41714 51416 45691 +45 34 1 42598 51423 46015 +46 34 1 43323 51431 46504 +47 34 1 43929 51439 46838 +48 34 1 44803 51449 47447 +49 34 1 45413 51459 47798 +50 34 1 46134 51470 48242 +51 34 1 47022 51482 48856 +52 34 1 47636 51494 49180 +53 34 1 48543 51508 49769 +54 34 1 49376 51522 50092 +55 34 1 49973 51537 50708 +56 34 1 50832 51552 51141 +57 34 1 51674 51567 51536 +58 34 1 52294 51583 51920 +59 34 1 53015 51599 52383 +60 34 1 53914 51614 52992 +61 34 1 54547 51631 53327 +62 34 1 55474 51647 53967 +63 34 1 56290 51663 54475 +0 35 1 8437 52842 26176 +1 35 1 9171 52829 26518 +2 35 1 10079 52817 27083 +3 35 1 10725 52805 27641 +4 35 1 11641 52793 27991 +5 35 1 12516 52780 28501 +6 35 1 13240 52768 28851 +7 35 1 13859 52756 29453 +8 35 1 14738 52744 29780 +9 35 1 15341 52732 30240 +10 35 1 16103 52721 30818 +11 35 1 17003 52709 31134 +12 35 1 17640 52698 31706 +13 35 1 18541 52688 32030 +14 35 1 19323 52677 32643 +15 35 1 19918 52667 33067 +16 35 1 20794 52657 33436 +17 35 1 21427 52648 33829 +18 35 1 22104 52639 34247 +19 35 1 22982 52631 34839 +20 35 1 23574 52623 35142 +21 35 1 24414 52616 35699 +22 35 1 25318 52609 36274 +23 35 1 26153 52603 36591 +24 35 1 26743 52597 37110 +25 35 1 27619 52592 37429 +26 35 1 28296 52588 38032 +27 35 1 28924 52584 38397 +28 35 1 29795 52581 38828 +29 35 1 30389 52579 39170 +30 35 1 31141 52577 39652 +31 35 1 32025 52576 40244 +32 35 1 32640 52576 40562 +33 35 1 33532 52576 41163 +34 35 1 34339 52577 41678 +35 35 1 34926 52579 42005 +36 35 1 35800 52581 42474 +37 35 1 36461 52584 42823 +38 35 1 37101 52588 43428 +39 35 1 37935 52592 43760 +40 35 1 38792 52597 44246 +41 35 1 39382 52603 44568 +42 35 1 40217 52609 45126 +43 35 1 41109 52616 45708 +44 35 1 41716 52623 46032 +45 35 1 42600 52631 46556 +46 35 1 43324 52639 46886 +47 35 1 43931 52648 47498 +48 35 1 44804 52657 47856 +49 35 1 45414 52667 48282 +50 35 1 46136 52677 48880 +51 35 1 47025 52688 49195 +52 35 1 47641 52698 49758 +53 35 1 48549 52709 50100 +54 35 1 49379 52721 50732 +55 35 1 49976 52732 51188 +56 35 1 50834 52744 51555 +57 35 1 51676 52756 51970 +58 35 1 52295 52768 52408 +59 35 1 53019 52780 52997 +60 35 1 53919 52793 53314 +61 35 1 54555 52805 53937 +62 35 1 55481 52817 54476 +63 35 1 56293 52829 54836 +0 36 1 8435 53959 26531 +1 36 1 9166 53950 27066 +2 36 1 10073 53940 27589 +3 36 1 10718 53931 27968 +4 36 1 11635 53922 28494 +5 36 1 12512 53913 28863 +6 36 1 13239 53904 29475 +7 36 1 13856 53895 29815 +8 36 1 14736 53886 30262 +9 36 1 15339 53877 30822 +10 36 1 16099 53868 31145 +11 36 1 16999 53859 31669 +12 36 1 17635 53851 32019 +13 36 1 18537 53842 32654 +14 36 1 19322 53834 33100 +15 36 1 19916 53826 33454 +16 36 1 20792 53819 33878 +17 36 1 21427 53812 34284 +18 36 1 22102 53805 34864 +19 36 1 22980 53798 35166 +20 36 1 23572 53792 35701 +21 36 1 24410 53786 36258 +22 36 1 25315 53781 36594 +23 36 1 26152 53776 37135 +24 36 1 26742 53772 37462 +25 36 1 27618 53768 38072 +26 36 1 28296 53764 38453 +27 36 1 28924 53761 38850 +28 36 1 29795 53759 39217 +29 36 1 30389 53757 39681 +30 36 1 31141 53756 40255 +31 36 1 32025 53755 40561 +32 36 1 32640 53754 41160 +33 36 1 33532 53755 41694 +34 36 1 34339 53756 42022 +35 36 1 34926 53757 42518 +36 36 1 35800 53759 42864 +37 36 1 36461 53761 43467 +38 36 1 37101 53764 43812 +39 36 1 37936 53768 44258 +40 36 1 38793 53772 44594 +41 36 1 39383 53776 45128 +42 36 1 40220 53781 45691 +43 36 1 41112 53786 46031 +44 36 1 41718 53792 46575 +45 36 1 42602 53798 46911 +46 36 1 43325 53805 47528 +47 36 1 43932 53812 47897 +48 36 1 44806 53819 48312 +49 36 1 45415 53826 48896 +50 36 1 46139 53834 49218 +51 36 1 47029 53842 49732 +52 36 1 47646 53851 50073 +53 36 1 48554 53859 50721 +54 36 1 49382 53868 51198 +55 36 1 49979 53877 51560 +56 36 1 50836 53886 52000 +57 36 1 51679 53895 52424 +58 36 1 52296 53904 53000 +59 36 1 53023 53913 53311 +60 36 1 53924 53922 53897 +61 36 1 54563 53931 54428 +62 36 1 55488 53940 54822 +63 36 1 56296 53950 55310 +0 37 1 8434 55048 27056 +1 37 1 9161 55040 27551 +2 37 1 10067 55033 27938 +3 37 1 10710 55026 28431 +4 37 1 11629 55019 28841 +5 37 1 12507 55011 29473 +6 37 1 13237 55004 29827 +7 37 1 13854 54997 30273 +8 37 1 14733 54990 30823 +9 37 1 15338 54984 31162 +10 37 1 16095 54977 31629 +11 37 1 16994 54970 31978 +12 37 1 17630 54964 32625 +13 37 1 18533 54957 33090 +14 37 1 19320 54951 33458 +15 37 1 19915 54945 33901 +16 37 1 20791 54940 34306 +17 37 1 21427 54934 34880 +18 37 1 22100 54929 35185 +19 37 1 22979 54924 35703 +20 37 1 23571 54919 36235 +21 37 1 24407 54914 36578 +22 37 1 25313 54910 37117 +23 37 1 26151 54907 37465 +24 37 1 26741 54903 38087 +25 37 1 27618 54900 38484 +26 37 1 28296 54897 38861 +27 37 1 28923 54895 39251 +28 37 1 29794 54893 39702 +29 37 1 30388 54892 40261 +30 37 1 31141 54891 40563 +31 37 1 32025 54890 41140 +32 37 1 32640 54890 41670 +33 37 1 33532 54890 42021 +34 37 1 34339 54891 42530 +35 37 1 34927 54892 42883 +36 37 1 35800 54893 43490 +37 37 1 36461 54895 43849 +38 37 1 37102 54897 44262 +39 37 1 37937 54900 44615 +40 37 1 38794 54903 45129 +41 37 1 39384 54907 45669 +42 37 1 40222 54910 46013 +43 37 1 41114 54914 46555 +44 37 1 41720 54919 46909 +45 37 1 42604 54924 47537 +46 37 1 43325 54929 47917 +47 37 1 43933 54934 48328 +48 37 1 44807 54940 48905 +49 37 1 45416 54945 49240 +50 37 1 46142 54951 49705 +51 37 1 47032 54957 50047 +52 37 1 47651 54964 50676 +53 37 1 48560 54970 51171 +54 37 1 49384 54977 51554 +55 37 1 49981 54984 52008 +56 37 1 50838 54990 52429 +57 37 1 51681 54997 53002 +58 37 1 52298 55004 53312 +59 37 1 53028 55011 53870 +60 37 1 53930 55019 54370 +61 37 1 54570 55026 54786 +62 37 1 55495 55033 55279 +63 37 1 56299 55040 55705 +0 38 1 8432 56127 27533 +1 38 1 9156 56119 27927 +2 38 1 10062 56112 28355 +3 38 1 10702 56105 28780 +4 38 1 11623 56098 29444 +5 38 1 12503 56092 29813 +6 38 1 13236 56085 30273 +7 38 1 13851 56079 30822 +8 38 1 14731 56073 31174 +9 38 1 15336 56067 31598 +10 38 1 16091 56062 31952 +11 38 1 16990 56056 32571 +12 38 1 17625 56051 33039 +13 38 1 18528 56046 33450 +14 38 1 19319 56041 33895 +15 38 1 19913 56036 34312 +16 38 1 20789 56032 34886 +17 38 1 21426 56027 35197 +18 38 1 22099 56023 35704 +19 38 1 22977 56019 36215 +20 38 1 23569 56015 36571 +21 38 1 24404 56012 37062 +22 38 1 25310 56009 37439 +23 38 1 26149 56006 38078 +24 38 1 26740 56003 38486 +25 38 1 27617 56001 38865 +26 38 1 28296 55999 39269 +27 38 1 28923 55997 39715 +28 38 1 29794 55996 40263 +29 38 1 30388 55994 40566 +30 38 1 31140 55994 41125 +31 38 1 32024 55993 41627 +32 38 1 32640 55993 42001 +33 38 1 33532 55993 42509 +34 38 1 34339 55994 42881 +35 38 1 34927 55994 43496 +36 38 1 35801 55996 43866 +37 38 1 36462 55997 44262 +38 38 1 37102 55999 44631 +39 38 1 37937 56001 45131 +40 38 1 38795 56003 45650 +41 38 1 39386 56006 46004 +42 38 1 40225 56009 46501 +43 38 1 41116 56012 46882 +44 38 1 41722 56015 47525 +45 38 1 42606 56019 47916 +46 38 1 43326 56023 48333 +47 38 1 43935 56027 48909 +48 38 1 44809 56032 49256 +49 38 1 45416 56036 49684 +50 38 1 46145 56041 50033 +51 38 1 47036 56046 50634 +52 38 1 47656 56051 51110 +53 38 1 48565 56056 51535 +54 38 1 49387 56062 51990 +55 38 1 49984 56067 52423 +56 38 1 50840 56073 53001 +57 38 1 51684 56079 53314 +58 38 1 52299 56085 53855 +59 38 1 53032 56092 54331 +60 38 1 53935 56098 54749 +61 38 1 54578 56105 55212 +62 38 1 55502 56112 55670 +63 38 1 56301 56119 56292 +0 39 1 8431 57215 27925 +1 39 1 9151 57205 28316 +2 39 1 10056 57195 28725 +3 39 1 10694 57186 29384 +4 39 1 11616 57177 29772 +5 39 1 12498 57170 30259 +6 39 1 13235 57162 30822 +7 39 1 13849 57155 31175 +8 39 1 14729 57149 31585 +9 39 1 15335 57143 31939 +10 39 1 16087 57137 32536 +11 39 1 16985 57131 32967 +12 39 1 17620 57126 33424 +13 39 1 18524 57121 33861 +14 39 1 19317 57117 34303 +15 39 1 19911 57112 34886 +16 39 1 20787 57108 35201 +17 39 1 21426 57104 35705 +18 39 1 22097 57100 36201 +19 39 1 22975 57097 36571 +20 39 1 23567 57094 37011 +21 39 1 24401 57091 37390 +22 39 1 25307 57088 38044 +23 39 1 26148 57085 38459 +24 39 1 26739 57083 38865 +25 39 1 27616 57081 39270 +26 39 1 28296 57079 39721 +27 39 1 28922 57078 40263 +28 39 1 29794 57077 40569 +29 39 1 30388 57076 41115 +30 39 1 31140 57075 41593 +31 39 1 32024 57075 41979 +32 39 1 32640 57075 42457 +33 39 1 33533 57075 42857 +34 39 1 34340 57075 43486 +35 39 1 34927 57076 43863 +36 39 1 35801 57077 44262 +37 39 1 36462 57078 44638 +38 39 1 37103 57079 45132 +39 39 1 37938 57081 45637 +40 39 1 38796 57083 46003 +41 39 1 39387 57085 46452 +42 39 1 40228 57088 46833 +43 39 1 41119 57091 47491 +44 39 1 41724 57094 47893 +45 39 1 42608 57097 48325 +46 39 1 43327 57100 48908 +47 39 1 43936 57104 49261 +48 39 1 44810 57108 49673 +49 39 1 45417 57112 50026 +50 39 1 46148 57117 50607 +51 39 1 47039 57121 51054 +52 39 1 47661 57126 51500 +53 39 1 48570 57131 51948 +54 39 1 49390 57137 52403 +55 39 1 49987 57143 52996 +56 39 1 50842 57149 53311 +57 39 1 51686 57155 53847 +58 39 1 52300 57162 54314 +59 39 1 53037 57170 54731 +60 39 1 53941 57177 55143 +61 39 1 54586 57186 55612 +62 39 1 55509 57195 56268 +63 39 1 56304 57205 56628 +0 40 1 8430 58331 28324 +1 40 1 9147 58315 28709 +2 40 1 10051 58300 29339 +3 40 1 10687 58286 29708 +4 40 1 11610 58273 30232 +5 40 1 12494 58262 30822 +6 40 1 13234 58251 31164 +7 40 1 13847 58241 31595 +8 40 1 14726 58233 31938 +9 40 1 15333 58224 32523 +10 40 1 16083 58217 32925 +11 40 1 16981 58210 33392 +12 40 1 17615 58203 33800 +13 40 1 18520 58197 34277 +14 40 1 19315 58191 34878 +15 40 1 19909 58186 35195 +16 40 1 20786 58181 35707 +17 40 1 21425 58177 36197 +18 40 1 22095 58173 36573 +19 40 1 22974 58169 36977 +20 40 1 23566 58166 37356 +21 40 1 24398 58163 37991 +22 40 1 25304 58160 38406 +23 40 1 26147 58157 38859 +24 40 1 26738 58155 39254 +25 40 1 27615 58153 39720 +26 40 1 28295 58151 40264 +27 40 1 28922 58150 40570 +28 40 1 29793 58148 41110 +29 40 1 30388 58147 41571 +30 40 1 31139 58147 41970 +31 40 1 32024 58146 42397 +32 40 1 32640 58146 42812 +33 40 1 33533 58146 43460 +34 40 1 34340 58147 43838 +35 40 1 34928 58147 44264 +36 40 1 35802 58148 44636 +37 40 1 36462 58150 45133 +38 40 1 37104 58151 45631 +39 40 1 37939 58153 46004 +40 40 1 38797 58155 46417 +41 40 1 39388 58157 46796 +42 40 1 40231 58160 47438 +43 40 1 41121 58163 47849 +44 40 1 41726 58166 48303 +45 40 1 42610 58169 48902 +46 40 1 43327 58173 49253 +47 40 1 43938 58177 49673 +48 40 1 44812 58181 50024 +49 40 1 45418 58186 50594 +50 40 1 46151 58191 51021 +51 40 1 47043 58197 51471 +52 40 1 47666 58203 51883 +53 40 1 48576 58210 52368 +54 40 1 49392 58217 52984 +55 40 1 49989 58224 53304 +56 40 1 50844 58233 53845 +57 40 1 51688 58241 54315 +58 40 1 52301 58251 54724 +59 40 1 53041 58262 55109 +60 40 1 53946 58273 55555 +61 40 1 54593 58286 56225 +62 40 1 55515 58300 56590 +63 40 1 56306 58315 57100 +0 41 1 8429 59494 28734 +1 41 1 9142 59468 29343 +2 41 1 10045 59444 29677 +3 41 1 10680 59423 30192 +4 41 1 11603 59403 30816 +5 41 1 12489 59385 31146 +6 41 1 13232 59369 31626 +7 41 1 13844 59354 31950 +8 41 1 14724 59340 32533 +9 41 1 15332 59328 32919 +10 41 1 16079 59316 33381 +11 41 1 16977 59306 33741 +12 41 1 17609 59296 34235 +13 41 1 18515 59288 34862 +14 41 1 19313 59280 35183 +15 41 1 19907 59272 35711 +16 41 1 20784 59266 36206 +17 41 1 21425 59259 36575 +18 41 1 22094 59254 36968 +19 41 1 22972 59249 37341 +20 41 1 23564 59244 37958 +21 41 1 24395 59240 38341 +22 41 1 25301 59236 38842 +23 41 1 26145 59233 39224 +24 41 1 26737 59230 39712 +25 41 1 27614 59228 40266 +26 41 1 28295 59225 40571 +27 41 1 28921 59224 41112 +28 41 1 29793 59222 41563 +29 41 1 30387 59221 41967 +30 41 1 31139 59220 42358 +31 41 1 32024 59220 42768 +32 41 1 32640 59220 43417 +33 41 1 33533 59220 43796 +34 41 1 34340 59220 44265 +35 41 1 34928 59221 44626 +36 41 1 35802 59222 45133 +37 41 1 36462 59224 45633 +38 41 1 37104 59225 46005 +39 41 1 37940 59228 46402 +40 41 1 38798 59230 46777 +41 41 1 39390 59233 47401 +42 41 1 40234 59236 47790 +43 41 1 41124 59240 48269 +44 41 1 41728 59244 48888 +45 41 1 42612 59249 49233 +46 41 1 43328 59254 49683 +47 41 1 43939 59259 50024 +48 41 1 44813 59266 50592 +49 41 1 45419 59272 51011 +50 41 1 46154 59280 51458 +51 41 1 47046 59288 51833 +52 41 1 47671 59296 52318 +53 41 1 48581 59306 52963 +54 41 1 49395 59316 53291 +55 41 1 49992 59328 53846 +56 41 1 50846 59340 54332 +57 41 1 51691 59354 54722 +58 41 1 52303 59369 55110 +59 41 1 53046 59385 55534 +60 41 1 53952 59403 56181 +61 41 1 54601 59423 56544 +62 41 1 55522 59444 57114 +63 41 1 56309 59468 57444 +0 42 1 8428 60722 29399 +1 42 1 9137 60684 29716 +2 42 1 10040 60648 30193 +3 42 1 10672 60615 30803 +4 42 1 11596 60585 31135 +5 42 1 12484 60557 31672 +6 42 1 13231 60532 31979 +7 42 1 13842 60509 32567 +8 42 1 14722 60488 32953 +9 42 1 15331 60469 33389 +10 42 1 16075 60451 33731 +11 42 1 16972 60435 34203 +12 42 1 17604 60420 34836 +13 42 1 18511 60407 35166 +14 42 1 19311 60395 35717 +15 42 1 19906 60384 36226 +16 42 1 20783 60374 36577 +17 42 1 21424 60365 36987 +18 42 1 22092 60356 37345 +19 42 1 22970 60349 37951 +20 42 1 23562 60342 38307 +21 42 1 24392 60336 38817 +22 42 1 25298 60331 39182 +23 42 1 26144 60326 39699 +24 42 1 26736 60322 40268 +25 42 1 27613 60318 40571 +26 42 1 28295 60315 41122 +27 42 1 28921 60312 41572 +28 42 1 29792 60310 41969 +29 42 1 30387 60309 42345 +30 42 1 31139 60307 42747 +31 42 1 32024 60307 43378 +32 42 1 32640 60307 43742 +33 42 1 33533 60307 44262 +34 42 1 34340 60307 44609 +35 42 1 34928 60309 45135 +36 42 1 35803 60310 45644 +37 42 1 36462 60312 46005 +38 42 1 37105 60315 46408 +39 42 1 37940 60318 46774 +40 42 1 38799 60322 47386 +41 42 1 39391 60326 47753 +42 42 1 40237 60331 48223 +43 42 1 41126 60336 48866 +44 42 1 41730 60342 49206 +45 42 1 42614 60349 49701 +46 42 1 43329 60356 50028 +47 42 1 43940 60365 50602 +48 42 1 44815 60374 51024 +49 42 1 45420 60384 51457 +50 42 1 46157 60395 51818 +51 42 1 47050 60407 52283 +52 42 1 47676 60420 52929 +53 42 1 48586 60435 53272 +54 42 1 49397 60451 53848 +55 42 1 49994 60469 54360 +56 42 1 50848 60488 54726 +57 42 1 51693 60509 55143 +58 42 1 52304 60532 55544 +59 42 1 53051 60557 56176 +60 42 1 53958 60585 56514 +61 42 1 54608 60615 57114 +62 42 1 55528 60648 57456 +63 42 1 56311 60684 58085 +0 43 1 8426 61932 29782 +1 43 1 9133 61904 30236 +2 43 1 10035 61876 30818 +3 43 1 10665 61846 31137 +4 43 1 11590 61816 31713 +5 43 1 12480 61786 32027 +6 43 1 13229 61754 32629 +7 43 1 13840 61723 33027 +8 43 1 14720 61692 33420 +9 43 1 15329 61663 33776 +10 43 1 16071 61637 34220 +11 43 1 16968 61613 34826 +12 43 1 17599 61591 35149 +13 43 1 18506 61571 35728 +14 43 1 19310 61552 36258 +15 43 1 19904 61536 36586 +16 43 1 20781 61521 37032 +17 43 1 21424 61507 37372 +18 43 1 22091 61494 37971 +19 43 1 22969 61483 38313 +20 43 1 23561 61473 38812 +21 43 1 24389 61464 39144 +22 43 1 25295 61456 39681 +23 43 1 26142 61448 40271 +24 43 1 26735 61442 40575 +25 43 1 27612 61437 41140 +26 43 1 28295 61432 41598 +27 43 1 28920 61428 41976 +28 43 1 29792 61425 42361 +29 43 1 30387 61423 42750 +30 43 1 31138 61421 43367 +31 43 1 32023 61420 43704 +32 43 1 32640 61420 44249 +33 43 1 33534 61420 44589 +34 43 1 34340 61421 45137 +35 43 1 34929 61423 45662 +36 43 1 35803 61425 46005 +37 43 1 36462 61428 46435 +38 43 1 37105 61432 46787 +39 43 1 37941 61437 47393 +40 43 1 38800 61442 47747 +41 43 1 39393 61448 48202 +42 43 1 40240 61456 48835 +43 43 1 41128 61464 49176 +44 43 1 41732 61473 49719 +45 43 1 42616 61483 50037 +46 43 1 43329 61494 50623 +47 43 1 43942 61507 51057 +48 43 1 44816 61521 51466 +49 43 1 45421 61536 51838 +50 43 1 46160 61552 52283 +51 43 1 47054 61571 52911 +52 43 1 47681 61591 53250 +53 43 1 48591 61613 53851 +54 43 1 49399 61637 54393 +55 43 1 49997 61663 54740 +56 43 1 50850 61692 55201 +57 43 1 51695 61723 55583 +58 43 1 52306 61754 56207 +59 43 1 53055 61786 56537 +60 43 1 53964 61816 57099 +61 43 1 54615 61846 57455 +62 43 1 55535 61876 58103 +63 43 1 56314 61904 58577 +0 44 1 8425 63069 30264 +1 44 1 9128 63051 30824 +2 44 1 10030 63032 31148 +3 44 1 10659 63013 31685 +4 44 1 11582 62992 32038 +5 44 1 12475 62971 32667 +6 44 1 13228 62949 33093 +7 44 1 13837 62927 33457 +8 44 1 14717 62904 33849 +9 44 1 15328 62881 34273 +10 44 1 16067 62858 34858 +11 44 1 16964 62834 35161 +12 44 1 17593 62811 35740 +13 44 1 18502 62788 36296 +14 44 1 19308 62765 36612 +15 44 1 19902 62742 37101 +16 44 1 20779 62720 37425 +17 44 1 21424 62700 38022 +18 44 1 22089 62681 38362 +19 44 1 22967 62665 38830 +20 44 1 23559 62650 39152 +21 44 1 24386 62636 39670 +22 44 1 25292 62624 40274 +23 44 1 26141 62614 40585 +24 44 1 26734 62605 41168 +25 44 1 27611 62597 41640 +26 44 1 28295 62590 41993 +27 44 1 28920 62584 42405 +28 44 1 29792 62579 42778 +29 44 1 30387 62576 43385 +30 44 1 31138 62573 43707 +31 44 1 32023 62572 44241 +32 44 1 32640 62571 44568 +33 44 1 33534 62572 45141 +34 44 1 34341 62573 45686 +35 44 1 34929 62576 46012 +36 44 1 35804 62579 46480 +37 44 1 36462 62584 46817 +38 44 1 37106 62590 47422 +39 44 1 37942 62597 47773 +40 44 1 38801 62605 48212 +41 44 1 39394 62614 48829 +42 44 1 40243 62624 49153 +43 44 1 41131 62636 49734 +44 44 1 41734 62650 50051 +45 44 1 42618 62665 50654 +46 44 1 43330 62681 51108 +47 44 1 43943 62700 51488 +48 44 1 44818 62720 51889 +49 44 1 45422 62742 52315 +50 44 1 46164 62765 52929 +51 44 1 47057 62788 53250 +52 44 1 47687 62811 53847 +53 44 1 48596 62834 54420 +54 44 1 49402 62858 54757 +55 44 1 49999 62881 55257 +56 44 1 50852 62904 55624 +57 44 1 51698 62927 56238 +58 44 1 52307 62949 56574 +59 44 1 53060 62971 57080 +60 44 1 53970 62992 57424 +61 44 1 54622 63013 58079 +62 44 1 55541 63032 58583 +63 44 1 56316 63051 58959 +0 45 0 0 0 0 +1 45 0 0 0 0 +2 45 0 0 0 0 +3 45 0 0 0 0 +4 45 0 0 0 0 +5 45 0 0 0 0 +6 45 0 0 0 0 +7 45 0 0 0 0 +8 45 0 0 0 0 +9 45 0 0 0 0 +10 45 0 0 0 0 +11 45 0 0 0 0 +12 45 0 0 0 0 +13 45 0 0 0 0 +14 45 0 0 0 0 +15 45 0 0 0 0 +16 45 0 0 0 0 +17 45 0 0 0 0 +18 45 0 0 0 0 +19 45 0 0 0 0 +20 45 0 0 0 0 +21 45 0 0 0 0 +22 45 0 0 0 0 +23 45 0 0 0 0 +24 45 0 0 0 0 +25 45 0 0 0 0 +26 45 0 0 0 0 +27 45 0 0 0 0 +28 45 0 0 0 0 +29 45 0 0 0 0 +30 45 0 0 0 0 +31 45 0 0 0 0 +32 45 0 0 0 0 +33 45 0 0 0 0 +34 45 0 0 0 0 +35 45 0 0 0 0 +36 45 0 0 0 0 +37 45 0 0 0 0 +38 45 0 0 0 0 +39 45 0 0 0 0 +40 45 0 0 0 0 +41 45 0 0 0 0 +42 45 0 0 0 0 +43 45 0 0 0 0 +44 45 0 0 0 0 +45 45 0 0 0 0 +46 45 0 0 0 0 +47 45 0 0 0 0 +48 45 0 0 0 0 +49 45 0 0 0 0 +50 45 0 0 0 0 +51 45 0 0 0 0 +52 45 0 0 0 0 +53 45 0 0 0 0 +54 45 0 0 0 0 +55 45 0 0 0 0 +56 45 0 0 0 0 +57 45 0 0 0 0 +58 45 0 0 0 0 +59 45 0 0 0 0 +60 45 0 0 0 0 +61 45 0 0 0 0 +62 45 0 0 0 0 +63 45 0 0 0 0 +0 46 0 0 0 0 +1 46 0 0 0 0 +2 46 0 0 0 0 +3 46 0 0 0 0 +4 46 0 0 0 0 +5 46 0 0 0 0 +6 46 0 0 0 0 +7 46 0 0 0 0 +8 46 0 0 0 0 +9 46 0 0 0 0 +10 46 0 0 0 0 +11 46 0 0 0 0 +12 46 0 0 0 0 +13 46 0 0 0 0 +14 46 0 0 0 0 +15 46 0 0 0 0 +16 46 0 0 0 0 +17 46 0 0 0 0 +18 46 0 0 0 0 +19 46 0 0 0 0 +20 46 0 0 0 0 +21 46 0 0 0 0 +22 46 0 0 0 0 +23 46 0 0 0 0 +24 46 0 0 0 0 +25 46 0 0 0 0 +26 46 0 0 0 0 +27 46 0 0 0 0 +28 46 0 0 0 0 +29 46 0 0 0 0 +30 46 0 0 0 0 +31 46 0 0 0 0 +32 46 0 0 0 0 +33 46 0 0 0 0 +34 46 0 0 0 0 +35 46 0 0 0 0 +36 46 0 0 0 0 +37 46 0 0 0 0 +38 46 0 0 0 0 +39 46 0 0 0 0 +40 46 0 0 0 0 +41 46 0 0 0 0 +42 46 0 0 0 0 +43 46 0 0 0 0 +44 46 0 0 0 0 +45 46 0 0 0 0 +46 46 0 0 0 0 +47 46 0 0 0 0 +48 46 0 0 0 0 +49 46 0 0 0 0 +50 46 0 0 0 0 +51 46 0 0 0 0 +52 46 0 0 0 0 +53 46 0 0 0 0 +54 46 0 0 0 0 +55 46 0 0 0 0 +56 46 0 0 0 0 +57 46 0 0 0 0 +58 46 0 0 0 0 +59 46 0 0 0 0 +60 46 0 0 0 0 +61 46 0 0 0 0 +62 46 0 0 0 0 +63 46 0 0 0 0 +0 47 0 0 0 0 +1 47 0 0 0 0 +2 47 0 0 0 0 +3 47 0 0 0 0 +4 47 0 0 0 0 +5 47 0 0 0 0 +6 47 0 0 0 0 +7 47 0 0 0 0 +8 47 0 0 0 0 +9 47 0 0 0 0 +10 47 0 0 0 0 +11 47 0 0 0 0 +12 47 0 0 0 0 +13 47 0 0 0 0 +14 47 0 0 0 0 +15 47 0 0 0 0 +16 47 0 0 0 0 +17 47 0 0 0 0 +18 47 0 0 0 0 +19 47 0 0 0 0 +20 47 0 0 0 0 +21 47 0 0 0 0 +22 47 0 0 0 0 +23 47 0 0 0 0 +24 47 0 0 0 0 +25 47 0 0 0 0 +26 47 0 0 0 0 +27 47 0 0 0 0 +28 47 0 0 0 0 +29 47 0 0 0 0 +30 47 0 0 0 0 +31 47 0 0 0 0 +32 47 0 0 0 0 +33 47 0 0 0 0 +34 47 0 0 0 0 +35 47 0 0 0 0 +36 47 0 0 0 0 +37 47 0 0 0 0 +38 47 0 0 0 0 +39 47 0 0 0 0 +40 47 0 0 0 0 +41 47 0 0 0 0 +42 47 0 0 0 0 +43 47 0 0 0 0 +44 47 0 0 0 0 +45 47 0 0 0 0 +46 47 0 0 0 0 +47 47 0 0 0 0 +48 47 0 0 0 0 +49 47 0 0 0 0 +50 47 0 0 0 0 +51 47 0 0 0 0 +52 47 0 0 0 0 +53 47 0 0 0 0 +54 47 0 0 0 0 +55 47 0 0 0 0 +56 47 0 0 0 0 +57 47 0 0 0 0 +58 47 0 0 0 0 +59 47 0 0 0 0 +60 47 0 0 0 0 +61 47 0 0 0 0 +62 47 0 0 0 0 +63 47 0 0 0 0 diff --git a/tests/golden/stitch_rect_near.txt b/tests/golden/stitch_rect_near.txt new file mode 100644 index 0000000..94e9465 --- /dev/null +++ b/tests/golden/stitch_rect_near.txt @@ -0,0 +1,3072 @@ +0 0 1 8670 12240 10200 +1 0 1 9435 13260 10965 +2 0 1 10200 13260 11475 +3 0 1 10710 13260 11730 +4 0 1 11475 13260 12240 +5 0 1 12240 13260 12750 +6 0 1 13515 13260 13515 +7 0 1 14280 13260 13770 +8 0 1 15045 13260 14280 +9 0 1 15555 13260 14790 +10 0 1 16320 13260 15045 +11 0 1 17085 13260 15555 +12 0 1 17595 13260 15810 +13 0 1 18360 13260 16320 +14 0 1 19125 13260 16830 +15 0 1 19635 13260 17085 +16 0 1 21165 13260 18105 +17 0 1 21675 13260 18360 +18 0 1 22440 13260 18870 +19 0 1 23205 13260 19125 +20 0 1 23715 13260 19635 +21 0 1 24480 13260 20145 +22 0 1 25245 13260 20400 +23 0 1 26010 13260 20910 +24 0 1 26520 13260 21165 +25 0 1 27285 13260 21675 +26 0 1 28050 13260 22185 +27 0 1 29325 13260 22950 +28 0 1 30090 13260 23205 +29 0 1 30600 13260 23715 +30 0 1 31365 13260 24225 +31 0 1 32130 13260 24480 +32 0 1 32640 13260 24990 +33 0 1 33405 13260 25500 +34 0 1 34170 13260 25755 +35 0 1 34680 13260 26265 +36 0 1 35445 13260 26520 +37 0 1 36210 13260 27030 +38 0 1 37485 13260 27795 +39 0 1 38250 13260 28305 +40 0 1 39015 13260 28560 +41 0 1 39525 13260 29070 +42 0 1 40290 13260 29580 +43 0 1 41055 13260 29835 +44 0 1 41565 13260 30345 +45 0 1 42330 13260 30855 +46 0 1 43095 13260 31110 +47 0 1 43605 13260 31620 +48 0 1 44370 13260 31875 +49 0 1 45645 13260 32895 +50 0 1 46410 13260 33150 +51 0 1 47175 13260 33660 +52 0 1 47685 13260 33915 +53 0 1 48450 13260 34425 +54 0 1 49215 13260 34935 +55 0 1 49725 13260 35190 +56 0 1 50490 13260 35700 +57 0 1 51255 13260 36210 +58 0 1 52020 13260 36465 +59 0 1 53295 13260 37230 +60 0 1 54060 13260 37740 +61 0 1 54570 13260 38250 +62 0 1 55335 13260 38505 +63 0 1 56100 13260 39015 +0 1 1 8670 14280 10965 +1 1 1 9435 14280 11475 +2 1 1 10200 14280 11730 +3 1 1 10710 14280 12240 +4 1 1 11475 14280 12750 +5 1 1 12240 14280 13005 +6 1 1 13515 14280 13770 +7 1 1 14280 14280 14280 +8 1 1 15045 14280 14790 +9 1 1 15555 14280 15045 +10 1 1 16320 14280 15555 +11 1 1 17085 14280 15810 +12 1 1 17595 14280 16320 +13 1 1 18360 14280 16830 +14 1 1 19125 14280 17085 +15 1 1 19635 14280 17595 +16 1 1 21165 14280 18360 +17 1 1 21675 14280 18870 +18 1 1 22440 14280 19125 +19 1 1 23205 14280 19635 +20 1 1 23715 14280 20145 +21 1 1 24480 14280 20400 +22 1 1 25245 14280 20910 +23 1 1 26010 14280 21165 +24 1 1 26520 14280 21675 +25 1 1 27285 14280 22185 +26 1 1 28050 14280 22440 +27 1 1 29325 14280 23205 +28 1 1 30090 14280 23715 +29 1 1 30600 14280 24225 +30 1 1 31365 14280 24480 +31 1 1 32130 14280 24990 +32 1 1 32640 14280 25500 +33 1 1 33405 14280 25755 +34 1 1 34170 14280 26265 +35 1 1 34680 14280 26520 +36 1 1 35445 14280 27030 +37 1 1 36210 14280 27540 +38 1 1 37485 14280 28305 +39 1 1 38250 14280 28560 +40 1 1 39015 14280 29070 +41 1 1 39525 14280 29580 +42 1 1 40290 14280 29835 +43 1 1 41055 14280 30345 +44 1 1 41565 14280 30855 +45 1 1 42330 14280 31110 +46 1 1 43095 14280 31620 +47 1 1 43605 14280 31875 +48 1 1 44370 14280 32385 +49 1 1 45645 14280 33150 +50 1 1 46410 14280 33660 +51 1 1 47175 14280 33915 +52 1 1 47685 14280 34425 +53 1 1 48450 14280 34935 +54 1 1 49215 14280 35190 +55 1 1 49725 14280 35700 +56 1 1 50490 14280 36210 +57 1 1 51255 14280 36465 +58 1 1 52020 14280 36975 +59 1 1 53295 14280 37740 +60 1 1 54060 14280 38250 +61 1 1 54570 14280 38505 +62 1 1 55335 14280 39015 +63 1 1 56100 14280 39270 +0 2 1 8670 15300 11475 +1 2 1 9435 15300 11730 +2 2 1 10200 15300 12240 +3 2 1 10710 15300 12750 +4 2 1 11475 15300 13005 +5 2 1 12240 15300 13515 +6 2 1 51510 32640 16320 +7 2 1 50745 32640 16320 +8 2 1 49980 32640 16320 +9 2 1 49470 32640 16320 +10 2 1 48705 32640 16320 +11 2 1 47940 32640 16320 +12 2 1 47430 32640 16320 +13 2 1 46665 32640 16320 +14 2 1 45900 32640 16320 +15 2 1 45390 32640 16320 +16 2 1 43860 32640 16320 +17 2 1 43350 32640 16320 +18 2 1 42585 32640 16320 +19 2 1 41820 32640 16320 +20 2 1 41310 32640 16320 +21 2 1 40545 32640 16320 +22 2 1 39780 32640 16320 +23 2 1 39015 32640 16320 +24 2 1 38505 32640 16320 +25 2 1 27285 15300 22440 +26 2 1 28050 15300 22950 +27 2 1 29325 15300 23715 +28 2 1 30090 15300 24225 +29 2 1 30600 15300 24480 +30 2 1 31365 15300 24990 +31 2 1 32130 15300 25500 +32 2 1 32640 15300 25755 +33 2 1 33405 15300 26265 +34 2 1 34170 15300 26520 +35 2 1 34680 15300 27030 +36 2 1 35445 15300 27540 +37 2 1 36210 15300 27795 +38 2 1 37485 15300 28560 +39 2 1 38250 15300 29070 +40 2 1 39015 15300 29580 +41 2 1 39525 15300 29835 +42 2 1 40290 15300 30345 +43 2 1 41055 15300 30855 +44 2 1 41565 15300 31110 +45 2 1 42330 15300 31620 +46 2 1 43095 15300 31875 +47 2 1 43605 15300 32385 +48 2 1 44370 15300 32895 +49 2 1 45645 15300 33660 +50 2 1 46410 15300 33915 +51 2 1 47175 15300 34425 +52 2 1 47685 15300 34935 +53 2 1 48450 15300 35190 +54 2 1 49215 15300 35700 +55 2 1 49725 15300 36210 +56 2 1 50490 15300 36465 +57 2 1 51255 15300 36975 +58 2 1 52020 15300 37230 +59 2 1 53295 15300 38250 +60 2 1 54060 15300 38505 +61 2 1 54570 15300 39015 +62 2 1 55335 15300 39270 +63 2 1 56100 15300 39780 +0 3 1 8670 16320 11730 +1 3 1 9435 16320 12240 +2 3 1 10200 16320 12750 +3 3 1 10710 16320 13005 +4 3 1 11475 16320 13515 +5 3 1 12240 16320 13770 +6 3 1 51510 32640 16320 +7 3 1 50745 32640 16320 +8 3 1 49980 32640 16320 +9 3 1 49470 32640 16320 +10 3 1 48705 32640 16320 +11 3 1 47940 32640 16320 +12 3 1 47430 32640 16320 +13 3 1 46665 32640 16320 +14 3 1 45900 32640 16320 +15 3 1 45390 32640 16320 +16 3 1 43860 32640 16320 +17 3 1 43350 32640 16320 +18 3 1 42585 32640 16320 +19 3 1 41820 32640 16320 +20 3 1 41310 32640 16320 +21 3 1 40545 32640 16320 +22 3 1 39780 32640 16320 +23 3 1 39015 32640 16320 +24 3 1 38505 32640 16320 +25 3 1 27285 16320 22950 +26 3 1 28050 16320 23205 +27 3 1 29325 16320 24225 +28 3 1 30090 16320 24480 +29 3 1 30600 16320 24990 +30 3 1 31365 16320 25500 +31 3 1 32130 16320 25755 +32 3 1 32640 16320 26265 +33 3 1 33405 16320 26520 +34 3 1 34170 16320 27030 +35 3 1 34680 16320 27540 +36 3 1 35445 16320 27795 +37 3 1 36210 16320 28305 +38 3 1 37485 16320 29070 +39 3 1 38250 16320 29580 +40 3 1 39015 16320 29835 +41 3 1 39525 16320 30345 +42 3 1 40290 16320 30855 +43 3 1 41055 16320 31110 +44 3 1 41565 16320 31620 +45 3 1 42330 16320 31875 +46 3 1 43095 16320 32385 +47 3 1 43605 16320 32895 +48 3 1 44370 16320 33150 +49 3 1 45645 16320 33915 +50 3 1 46410 16320 34425 +51 3 1 47175 16320 34935 +52 3 1 47685 16320 35190 +53 3 1 48450 16320 35700 +54 3 1 49215 16320 36210 +55 3 1 49725 16320 36465 +56 3 1 50490 16320 36975 +57 3 1 51255 16320 37230 +58 3 1 52020 16320 37740 +59 3 1 53295 16320 38505 +60 3 1 54060 16320 39015 +61 3 1 54570 16320 39270 +62 3 1 55335 16320 39780 +63 3 1 56100 16320 40290 +0 4 1 8670 17340 12240 +1 4 1 9435 17340 12750 +2 4 1 10200 17340 13005 +3 4 1 10710 17340 13515 +4 4 1 11475 17340 13770 +5 4 1 12240 17340 14280 +6 4 1 51510 32640 16320 +7 4 1 50745 32640 16320 +8 4 1 49980 32640 16320 +9 4 1 49470 32640 16320 +10 4 1 48705 32640 16320 +11 4 1 47940 32640 16320 +12 4 1 47430 32640 16320 +13 4 1 46665 32640 16320 +14 4 1 45900 32640 16320 +15 4 1 45390 32640 16320 +16 4 1 43860 32640 16320 +17 4 1 43350 32640 16320 +18 4 1 42585 32640 16320 +19 4 1 41820 32640 16320 +20 4 1 41310 32640 16320 +21 4 1 40545 32640 16320 +22 4 1 39780 32640 16320 +23 4 1 39015 32640 16320 +24 4 1 38505 32640 16320 +25 4 1 27285 17340 23205 +26 4 1 28050 17340 23715 +27 4 1 29325 17340 24480 +28 4 1 30090 17340 24990 +29 4 1 30600 17340 25500 +30 4 1 31365 17340 25755 +31 4 1 32130 17340 26265 +32 4 1 32640 17340 26520 +33 4 1 33405 17340 27030 +34 4 1 34170 17340 27540 +35 4 1 34680 17340 27795 +36 4 1 35445 17340 28305 +37 4 1 36210 17340 28560 +38 4 1 37485 17340 29580 +39 4 1 38250 17340 29835 +40 4 1 39015 17340 30345 +41 4 1 39525 17340 30855 +42 4 1 40290 17340 31110 +43 4 1 41055 17340 31620 +44 4 1 41565 17340 31875 +45 4 1 42330 17340 32385 +46 4 1 43095 17340 32895 +47 4 1 43605 17340 33150 +48 4 1 44370 17340 33660 +49 4 1 45645 17340 34425 +50 4 1 46410 17340 34935 +51 4 1 47175 17340 35190 +52 4 1 47685 17340 35700 +53 4 1 48450 17340 36210 +54 4 1 49215 17340 36465 +55 4 1 49725 17340 36975 +56 4 1 50490 17340 37230 +57 4 1 51255 17340 37740 +58 4 1 52020 17340 38250 +59 4 1 53295 17340 39015 +60 4 1 54060 17340 39270 +61 4 1 54570 17340 39780 +62 4 1 55335 17340 40290 +63 4 1 56100 17340 40545 +0 5 1 8670 18360 12750 +1 5 1 9435 18360 13005 +2 5 1 10200 18360 13515 +3 5 1 10710 18360 13770 +4 5 1 11475 18360 14280 +5 5 1 12240 18360 14790 +6 5 1 51510 32640 16320 +7 5 1 50745 32640 16320 +8 5 1 49980 32640 16320 +9 5 1 49470 32640 16320 +10 5 1 48705 32640 16320 +11 5 1 47940 32640 16320 +12 5 1 47430 32640 16320 +13 5 1 46665 32640 16320 +14 5 1 45900 32640 16320 +15 5 1 45390 32640 16320 +16 5 1 43860 32640 16320 +17 5 1 43350 32640 16320 +18 5 1 42585 32640 16320 +19 5 1 41820 32640 16320 +20 5 1 41310 32640 16320 +21 5 1 40545 32640 16320 +22 5 1 39780 32640 16320 +23 5 1 39015 32640 16320 +24 5 1 38505 32640 16320 +25 5 1 27285 18360 23715 +26 5 1 28050 18360 24225 +27 5 1 29325 18360 24990 +28 5 1 30090 18360 25500 +29 5 1 30600 18360 25755 +30 5 1 31365 18360 26265 +31 5 1 32130 18360 26520 +32 5 1 32640 18360 27030 +33 5 1 33405 18360 27540 +34 5 1 34170 18360 27795 +35 5 1 34680 18360 28305 +36 5 1 35445 18360 28560 +37 5 1 36210 18360 29070 +38 5 1 37485 18360 29835 +39 5 1 38250 18360 30345 +40 5 1 39015 18360 30855 +41 5 1 39525 18360 31110 +42 5 1 40290 18360 31620 +43 5 1 41055 18360 31875 +44 5 1 41565 18360 32385 +45 5 1 42330 18360 32895 +46 5 1 43095 18360 33150 +47 5 1 43605 18360 33660 +48 5 1 44370 18360 33915 +49 5 1 45645 18360 34935 +50 5 1 46410 18360 35190 +51 5 1 47175 18360 35700 +52 5 1 47685 18360 36210 +53 5 1 48450 18360 36465 +54 5 1 49215 18360 36975 +55 5 1 49725 18360 37230 +56 5 1 50490 18360 37740 +57 5 1 51255 18360 38250 +58 5 1 52020 18360 38505 +59 5 1 53295 18360 39270 +60 5 1 54060 18360 39780 +61 5 1 54570 18360 40290 +62 5 1 55335 18360 40545 +63 5 1 56100 18360 41055 +0 6 1 8670 19380 13005 +1 6 1 9435 19380 13515 +2 6 1 10200 19380 13770 +3 6 1 10710 19380 14280 +4 6 1 11475 19380 14790 +5 6 1 12240 19380 15045 +6 6 1 51510 32640 16320 +7 6 1 50745 32640 16320 +8 6 1 49980 32640 16320 +9 6 1 49470 32640 16320 +10 6 1 48705 32640 16320 +11 6 1 47940 32640 16320 +12 6 1 47430 32640 16320 +13 6 1 46665 32640 16320 +14 6 1 45900 32640 16320 +15 6 1 45390 32640 16320 +16 6 1 43860 32640 16320 +17 6 1 43350 32640 16320 +18 6 1 42585 32640 16320 +19 6 1 41820 32640 16320 +20 6 1 41310 32640 16320 +21 6 1 40545 32640 16320 +22 6 1 39780 32640 16320 +23 6 1 39015 32640 16320 +24 6 1 38505 32640 16320 +25 6 1 27285 19380 24225 +26 6 1 28050 19380 24480 +27 6 1 29325 19380 25500 +28 6 1 30090 19380 25755 +29 6 1 30600 19380 26265 +30 6 1 31365 19380 26520 +31 6 1 32130 19380 27030 +32 6 1 32640 19380 27540 +33 6 1 33405 19380 27795 +34 6 1 34170 19380 28305 +35 6 1 34680 19380 28560 +36 6 1 35445 19380 29070 +37 6 1 36210 19380 29580 +38 6 1 37485 19380 30345 +39 6 1 38250 19380 30855 +40 6 1 39015 19380 31110 +41 6 1 39525 19380 31620 +42 6 1 40290 19380 31875 +43 6 1 41055 19380 32385 +44 6 1 41565 19380 32895 +45 6 1 42330 19380 33150 +46 6 1 43095 19380 33660 +47 6 1 43605 19380 33915 +48 6 1 44370 19380 34425 +49 6 1 45645 19380 35190 +50 6 1 46410 19380 35700 +51 6 1 47175 19380 36210 +52 6 1 47685 19380 36465 +53 6 1 48450 19380 36975 +54 6 1 49215 19380 37230 +55 6 1 49725 19380 37740 +56 6 1 50490 19380 38250 +57 6 1 51255 19380 38505 +58 6 1 52020 19380 39015 +59 6 1 53295 19380 39780 +60 6 1 54060 19380 40290 +61 6 1 54570 19380 40545 +62 6 1 55335 19380 41055 +63 6 1 56100 19380 41565 +0 7 1 8670 20400 13515 +1 7 1 9435 20400 13770 +2 7 1 10200 20400 14280 +3 7 1 10710 20400 14790 +4 7 1 11475 20400 15045 +5 7 1 12240 20400 15555 +6 7 1 51510 32640 16320 +7 7 1 50745 32640 16320 +8 7 1 49980 32640 16320 +9 7 1 49470 32640 16320 +10 7 1 48705 32640 16320 +11 7 1 47940 32640 16320 +12 7 1 47430 32640 16320 +13 7 1 46665 32640 16320 +14 7 1 45900 32640 16320 +15 7 1 45390 32640 16320 +16 7 1 43860 32640 16320 +17 7 1 43350 32640 16320 +18 7 1 42585 32640 16320 +19 7 1 41820 32640 16320 +20 7 1 41310 32640 16320 +21 7 1 40545 32640 16320 +22 7 1 39780 32640 16320 +23 7 1 39015 32640 16320 +24 7 1 38505 32640 16320 +25 7 1 27285 20400 24480 +26 7 1 28050 20400 24990 +27 7 1 29325 20400 25755 +28 7 1 30090 20400 26265 +29 7 1 30600 20400 26520 +30 7 1 31365 20400 27030 +31 7 1 32130 20400 27540 +32 7 1 32640 20400 27795 +33 7 1 33405 20400 28305 +34 7 1 34170 20400 28560 +35 7 1 34680 20400 29070 +36 7 1 35445 20400 29580 +37 7 1 36210 20400 29835 +38 7 1 37485 20400 30855 +39 7 1 38250 20400 31110 +40 7 1 39015 20400 31620 +41 7 1 39525 20400 31875 +42 7 1 40290 20400 32385 +43 7 1 41055 20400 32895 +44 7 1 41565 20400 33150 +45 7 1 42330 20400 33660 +46 7 1 43095 20400 33915 +47 7 1 43605 20400 34425 +48 7 1 44370 20400 34935 +49 7 1 45645 20400 35700 +50 7 1 46410 20400 36210 +51 7 1 47175 20400 36465 +52 7 1 47685 20400 36975 +53 7 1 48450 20400 37230 +54 7 1 49215 20400 37740 +55 7 1 49725 20400 38250 +56 7 1 50490 20400 38505 +57 7 1 51255 20400 39015 +58 7 1 52020 20400 39270 +59 7 1 53295 20400 40290 +60 7 1 54060 20400 40545 +61 7 1 54570 20400 41055 +62 7 1 55335 20400 41565 +63 7 1 56100 20400 41820 +0 8 1 8670 21675 13770 +1 8 1 9435 21675 14280 +2 8 1 10200 21675 14790 +3 8 1 10710 21675 15045 +4 8 1 11475 21675 15555 +5 8 1 12240 21675 15810 +6 8 1 51510 32640 16320 +7 8 1 50745 32640 16320 +8 8 1 49980 32640 16320 +9 8 1 49470 32640 16320 +10 8 1 48705 32640 16320 +11 8 1 47940 32640 16320 +12 8 1 47430 32640 16320 +13 8 1 46665 32640 16320 +14 8 1 45900 32640 16320 +15 8 1 45390 32640 16320 +16 8 1 43860 32640 16320 +17 8 1 43350 32640 16320 +18 8 1 42585 32640 16320 +19 8 1 41820 32640 16320 +20 8 1 41310 32640 16320 +21 8 1 40545 32640 16320 +22 8 1 39780 32640 16320 +23 8 1 39015 32640 16320 +24 8 1 38505 32640 16320 +25 8 1 27285 22695 25500 +26 8 1 28050 22695 25755 +27 8 1 29325 22695 26520 +28 8 1 30090 22695 27030 +29 8 1 30600 22695 27540 +30 8 1 31365 22695 27795 +31 8 1 32130 22695 28305 +32 8 1 32640 22695 28560 +33 8 1 33405 22695 29070 +34 8 1 34170 22695 29580 +35 8 1 34680 22695 29835 +36 8 1 35445 22695 30345 +37 8 1 36210 22695 30855 +38 8 1 37485 22695 31620 +39 8 1 38250 22695 31875 +40 8 1 39015 22695 32385 +41 8 1 39525 22695 32895 +42 8 1 40290 22695 33150 +43 8 1 41055 22695 33660 +44 8 1 41565 22695 33915 +45 8 1 42330 22695 34425 +46 8 1 43095 22695 34935 +47 8 1 43605 22695 35190 +48 8 1 44370 21675 35190 +49 8 1 45645 21675 36210 +50 8 1 46410 21675 36465 +51 8 1 47175 21675 36975 +52 8 1 47685 21675 37230 +53 8 1 48450 21675 37740 +54 8 1 49215 21675 38250 +55 8 1 49725 21675 38505 +56 8 1 50490 21675 39015 +57 8 1 51255 21675 39270 +58 8 1 52020 21675 39780 +59 8 1 53295 21675 40545 +60 8 1 54060 21675 41055 +61 8 1 54570 21675 41565 +62 8 1 55335 21675 41820 +63 8 1 56100 21675 42330 +0 9 1 8670 22695 14280 +1 9 1 9435 22695 14790 +2 9 1 10200 22695 15045 +3 9 1 10710 22695 15555 +4 9 1 11475 22695 15810 +5 9 1 12240 22695 16320 +6 9 1 51510 32640 16320 +7 9 1 50745 32640 16320 +8 9 1 49980 32640 16320 +9 9 1 49470 32640 16320 +10 9 1 48705 32640 16320 +11 9 1 47940 32640 16320 +12 9 1 47430 32640 16320 +13 9 1 46665 32640 16320 +14 9 1 45900 32640 16320 +15 9 1 45390 32640 16320 +16 9 1 43860 32640 16320 +17 9 1 43350 32640 16320 +18 9 1 42585 32640 16320 +19 9 1 41820 32640 16320 +20 9 1 41310 32640 16320 +21 9 1 40545 32640 16320 +22 9 1 39780 32640 16320 +23 9 1 39015 32640 16320 +24 9 1 38505 32640 16320 +25 9 1 27285 23715 25755 +26 9 1 28050 23715 26265 +27 9 1 29325 23715 27030 +28 9 1 30090 23715 27540 +29 9 1 30600 23715 27795 +30 9 1 31365 23715 28305 +31 9 1 32130 23715 28560 +32 9 1 32640 23715 29070 +33 9 1 33405 23715 29580 +34 9 1 34170 23715 29835 +35 9 1 34680 23715 30345 +36 9 1 35445 23715 30855 +37 9 1 36210 23715 31110 +38 9 1 37485 23715 31875 +39 9 1 38250 23715 32385 +40 9 1 39015 23715 32895 +41 9 1 39525 23715 33150 +42 9 1 40290 23715 33660 +43 9 1 41055 23715 33915 +44 9 1 41565 23715 34425 +45 9 1 42330 23715 34935 +46 9 1 43095 23715 35190 +47 9 1 43605 23715 35700 +48 9 1 44370 23715 36210 +49 9 1 45645 23715 36975 +50 9 1 46410 23715 37230 +51 9 1 47175 23715 37740 +52 9 1 47685 23715 38250 +53 9 1 48450 23715 38505 +54 9 1 49215 23715 39015 +55 9 1 49725 23715 39270 +56 9 1 50490 23715 39780 +57 9 1 51255 22695 39780 +58 9 1 52020 22695 40290 +59 9 1 53295 22695 41055 +60 9 1 54060 22695 41565 +61 9 1 54570 22695 41820 +62 9 1 55335 22695 42330 +63 9 1 56100 22695 42585 +0 10 1 8670 23715 14790 +1 10 1 9435 24735 15555 +2 10 1 10200 24735 15810 +3 10 1 10710 24735 16320 +4 10 1 11475 24735 16830 +5 10 1 12240 24735 17085 +6 10 1 51510 32640 16320 +7 10 1 50745 32640 16320 +8 10 1 49980 32640 16320 +9 10 1 49470 32640 16320 +10 10 1 48705 32640 16320 +11 10 1 47940 32640 16320 +12 10 1 47430 32640 16320 +13 10 1 46665 32640 16320 +14 10 1 45900 32640 16320 +15 10 1 45390 32640 16320 +16 10 1 43860 32640 16320 +17 10 1 43350 32640 16320 +18 10 1 42585 32640 16320 +19 10 1 41820 32640 16320 +20 10 1 41310 32640 16320 +21 10 1 40545 32640 16320 +22 10 1 39780 32640 16320 +23 10 1 39015 32640 16320 +24 10 1 38505 32640 16320 +25 10 1 27285 24735 26265 +26 10 1 28050 24735 26520 +27 10 1 29325 24735 27540 +28 10 1 30090 24735 27795 +29 10 1 30600 24735 28305 +30 10 1 31365 24735 28560 +31 10 1 32130 24735 29070 +32 10 1 32640 24735 29580 +33 10 1 33405 24735 29835 +34 10 1 34170 24735 30345 +35 10 1 34680 24735 30855 +36 10 1 35445 24735 31110 +37 10 1 36210 24735 31620 +38 10 1 37485 24735 32385 +39 10 1 38250 24735 32895 +40 10 1 39015 24735 33150 +41 10 1 39525 24735 33660 +42 10 1 40290 24735 33915 +43 10 1 41055 24735 34425 +44 10 1 41565 24735 34935 +45 10 1 42330 24735 35190 +46 10 1 43095 24735 35700 +47 10 1 43605 24735 36210 +48 10 1 44370 24735 36465 +49 10 1 45645 24735 37230 +50 10 1 46410 24735 37740 +51 10 1 47175 24735 38250 +52 10 1 47685 24735 38505 +53 10 1 48450 24735 39015 +54 10 1 49215 24735 39270 +55 10 1 49725 24735 39780 +56 10 1 50490 24735 40290 +57 10 1 51255 24735 40545 +58 10 1 52020 24735 41055 +59 10 1 53295 24735 41820 +60 10 1 54060 24735 42330 +61 10 1 54570 24735 42585 +62 10 1 55335 24735 43095 +63 10 1 56100 24735 43605 +0 11 1 8670 25755 15555 +1 11 1 9435 25755 15810 +2 11 1 10200 25755 16320 +3 11 1 10710 25755 16830 +4 11 1 11475 25755 17085 +5 11 1 12240 25755 17595 +6 11 1 51510 32640 16320 +7 11 1 50745 32640 16320 +8 11 1 49980 32640 16320 +9 11 1 49470 32640 16320 +10 11 1 48705 32640 16320 +11 11 1 47940 32640 16320 +12 11 1 47430 32640 16320 +13 11 1 46665 32640 16320 +14 11 1 45900 32640 16320 +15 11 1 45390 32640 16320 +16 11 1 43860 32640 16320 +17 11 1 43350 32640 16320 +18 11 1 42585 32640 16320 +19 11 1 41820 32640 16320 +20 11 1 41310 32640 16320 +21 11 1 40545 32640 16320 +22 11 1 39780 32640 16320 +23 11 1 39015 32640 16320 +24 11 1 38505 32640 16320 +25 11 1 27285 25755 26520 +26 11 1 28050 25755 27030 +27 11 1 29325 25755 27795 +28 11 1 30090 25755 28305 +29 11 1 30600 25755 28560 +30 11 1 31365 25755 29070 +31 11 1 32130 25755 29580 +32 11 1 32640 25755 29835 +33 11 1 33405 25755 30345 +34 11 1 34170 25755 30855 +35 11 1 34680 25755 31110 +36 11 1 35445 25755 31620 +37 11 1 36210 25755 31875 +38 11 1 37485 25755 32895 +39 11 1 38250 25755 33150 +40 11 1 39015 25755 33660 +41 11 1 39525 25755 33915 +42 11 1 40290 25755 34425 +43 11 1 41055 25755 34935 +44 11 1 41565 25755 35190 +45 11 1 42330 25755 35700 +46 11 1 43095 25755 36210 +47 11 1 43605 25755 36465 +48 11 1 44370 25755 36975 +49 11 1 45645 25755 37740 +50 11 1 46410 25755 38250 +51 11 1 47175 25755 38505 +52 11 1 47685 25755 39015 +53 11 1 48450 25755 39270 +54 11 1 49215 25755 39780 +55 11 1 49725 25755 40290 +56 11 1 50490 25755 40545 +57 11 1 51255 25755 41055 +58 11 1 52020 25755 41565 +59 11 1 53295 25755 42330 +60 11 1 54060 25755 42585 +61 11 1 54570 25755 43095 +62 11 1 55335 25755 43605 +63 11 1 56100 25755 43860 +0 12 1 8670 26775 15810 +1 12 1 9435 26775 16320 +2 12 1 10200 26775 16830 +3 12 1 10710 26775 17085 +4 12 1 11475 26775 17595 +5 12 1 12240 26775 18105 +6 12 1 13005 26775 18360 +7 12 1 50745 32640 16320 +8 12 1 49980 32640 16320 +9 12 1 49470 32640 16320 +10 12 1 48705 32640 16320 +11 12 1 47940 32640 16320 +12 12 1 47430 32640 16320 +13 12 1 46665 32640 16320 +14 12 1 45900 32640 16320 +15 12 1 45390 32640 16320 +16 12 1 43860 32640 16320 +17 12 1 43350 32640 16320 +18 12 1 42585 32640 16320 +19 12 1 41820 32640 16320 +20 12 1 41310 32640 16320 +21 12 1 40545 32640 16320 +22 12 1 39780 32640 16320 +23 12 1 39015 32640 16320 +24 12 1 38505 32640 16320 +25 12 1 27285 26775 27030 +26 12 1 28050 26775 27540 +27 12 1 29325 26775 28305 +28 12 1 30090 26775 28560 +29 12 1 30600 26775 29070 +30 12 1 31365 26775 29580 +31 12 1 32130 26775 29835 +32 12 1 32640 26775 30345 +33 12 1 33405 26775 30855 +34 12 1 34170 26775 31110 +35 12 1 34680 26775 31620 +36 12 1 35445 26775 31875 +37 12 1 36210 26775 32385 +38 12 1 37485 26775 33150 +39 12 1 38250 26775 33660 +40 12 1 39015 26775 33915 +41 12 1 39525 26775 34425 +42 12 1 40290 26775 34935 +43 12 1 41055 26775 35190 +44 12 1 41565 26775 35700 +45 12 1 42330 26775 36210 +46 12 1 43095 26775 36465 +47 12 1 43605 26775 36975 +48 12 1 44370 26775 37230 +49 12 1 45645 26775 38250 +50 12 1 46410 26775 38505 +51 12 1 47175 26775 39015 +52 12 1 47685 26775 39270 +53 12 1 48450 26775 39780 +54 12 1 49215 26775 40290 +55 12 1 49725 26775 40545 +56 12 1 50490 26775 41055 +57 12 1 51255 26775 41565 +58 12 1 52530 26775 42330 +59 12 1 53295 26775 42585 +60 12 1 54060 26775 43095 +61 12 1 54570 26775 43605 +62 12 1 55335 26775 43860 +63 12 1 56100 26775 44370 +0 13 1 8670 27795 16320 +1 13 1 9435 27795 16830 +2 13 1 10200 27795 17085 +3 13 1 10710 27795 17595 +4 13 1 11475 27795 18105 +5 13 1 12240 27795 18360 +6 13 1 13005 27795 18870 +7 13 1 50745 32640 16320 +8 13 1 49980 32640 16320 +9 13 1 49470 32640 16320 +10 13 1 48705 32640 16320 +11 13 1 47940 32640 16320 +12 13 1 47430 32640 16320 +13 13 1 46665 32640 16320 +14 13 1 45900 32640 16320 +15 13 1 45390 32640 16320 +16 13 1 43860 32640 16320 +17 13 1 43350 32640 16320 +18 13 1 42585 32640 16320 +19 13 1 41820 32640 16320 +20 13 1 41310 32640 16320 +21 13 1 40545 32640 16320 +22 13 1 39780 32640 16320 +23 13 1 39015 32640 16320 +24 13 1 38505 32640 16320 +25 13 1 27285 27795 27540 +26 13 1 28050 27795 27795 +27 13 1 29325 27795 28560 +28 13 1 30090 27795 29070 +29 13 1 30600 27795 29580 +30 13 1 31365 27795 29835 +31 13 1 32130 27795 30345 +32 13 1 32640 27795 30855 +33 13 1 33405 27795 31110 +34 13 1 34170 27795 31620 +35 13 1 34680 27795 31875 +36 13 1 35445 27795 32385 +37 13 1 36210 27795 32895 +38 13 1 37485 27795 33660 +39 13 1 38250 27795 33915 +40 13 1 39015 27795 34425 +41 13 1 39525 27795 34935 +42 13 1 40290 27795 35190 +43 13 1 41055 27795 35700 +44 13 1 41565 27795 36210 +45 13 1 42330 27795 36465 +46 13 1 43095 27795 36975 +47 13 1 43605 27795 37230 +48 13 1 44370 27795 37740 +49 13 1 45645 27795 38505 +50 13 1 46410 27795 39015 +51 13 1 47175 27795 39270 +52 13 1 47685 27795 39780 +53 13 1 48450 27795 40290 +54 13 1 49215 27795 40545 +55 13 1 49725 27795 41055 +56 13 1 50490 27795 41565 +57 13 1 51255 27795 41820 +58 13 1 52530 27795 42585 +59 13 1 53295 27795 43095 +60 13 1 54060 27795 43605 +61 13 1 54570 27795 43860 +62 13 1 55335 27795 44370 +63 13 1 56100 27795 44625 +0 14 1 8670 28815 16830 +1 14 1 9435 28815 17085 +2 14 1 10200 28815 17595 +3 14 1 10710 28815 18105 +4 14 1 11475 28815 18360 +5 14 1 12240 28815 18870 +6 14 1 13005 28815 19125 +7 14 1 50745 32640 16320 +8 14 1 49980 32640 16320 +9 14 1 49470 32640 16320 +10 14 1 48705 32640 16320 +11 14 1 47940 32640 16320 +12 14 1 47430 32640 16320 +13 14 1 46665 32640 16320 +14 14 1 45900 32640 16320 +15 14 1 45390 32640 16320 +16 14 1 43860 32640 16320 +17 14 1 43350 32640 16320 +18 14 1 42585 32640 16320 +19 14 1 41820 32640 16320 +20 14 1 41310 32640 16320 +21 14 1 40545 32640 16320 +22 14 1 39780 32640 16320 +23 14 1 39015 32640 16320 +24 14 1 38505 32640 16320 +25 14 1 27285 28815 27795 +26 14 1 28050 28815 28305 +27 14 1 29325 28815 29070 +28 14 1 30090 28815 29580 +29 14 1 30600 28815 29835 +30 14 1 31365 28815 30345 +31 14 1 32130 28815 30855 +32 14 1 32640 28815 31110 +33 14 1 33405 28815 31620 +34 14 1 34170 28815 31875 +35 14 1 34680 28815 32385 +36 14 1 35445 28815 32895 +37 14 1 36210 28815 33150 +38 14 1 37485 28815 33915 +39 14 1 38250 28815 34425 +40 14 1 39015 28815 34935 +41 14 1 39525 28815 35190 +42 14 1 40290 28815 35700 +43 14 1 41055 28815 36210 +44 14 1 41565 28815 36465 +45 14 1 42330 28815 36975 +46 14 1 43095 28815 37230 +47 14 1 43605 28815 37740 +48 14 1 44370 28815 38250 +49 14 1 45645 28815 39015 +50 14 1 46410 28815 39270 +51 14 1 47175 28815 39780 +52 14 1 47685 28815 40290 +53 14 1 48450 28815 40545 +54 14 1 49215 28815 41055 +55 14 1 49725 28815 41565 +56 14 1 50490 28815 41820 +57 14 1 51255 28815 42330 +58 14 1 52530 28815 43095 +59 14 1 53295 28815 43605 +60 14 1 54060 28815 43860 +61 14 1 54570 28815 44370 +62 14 1 55335 28815 44625 +63 14 1 56100 28815 45135 +0 15 1 8670 29835 17085 +1 15 1 9435 29835 17595 +2 15 1 10200 29835 18105 +3 15 1 10710 29835 18360 +4 15 1 11475 29835 18870 +5 15 1 12240 29835 19125 +6 15 1 13005 29835 19635 +7 15 1 50745 32640 16320 +8 15 1 49980 32640 16320 +9 15 1 49470 32640 16320 +10 15 1 48705 32640 16320 +11 15 1 47940 32640 16320 +12 15 1 47430 32640 16320 +13 15 1 46665 32640 16320 +14 15 1 45900 32640 16320 +15 15 1 45390 32640 16320 +16 15 1 43860 32640 16320 +17 15 1 43350 32640 16320 +18 15 1 42585 32640 16320 +19 15 1 41820 32640 16320 +20 15 1 41310 32640 16320 +21 15 1 40545 32640 16320 +22 15 1 39780 32640 16320 +23 15 1 39015 32640 16320 +24 15 1 38505 32640 16320 +25 15 1 27285 29835 28305 +26 15 1 28050 29835 28560 +27 15 1 29325 29835 29580 +28 15 1 30090 29835 29835 +29 15 1 30600 29835 30345 +30 15 1 31365 29835 30855 +31 15 1 32130 29835 31110 +32 15 1 32640 29835 31620 +33 15 1 33405 29835 31875 +34 15 1 34170 29835 32385 +35 15 1 34680 29835 32895 +36 15 1 35445 29835 33150 +37 15 1 36210 29835 33660 +38 15 1 37485 29835 34425 +39 15 1 38250 29835 34935 +40 15 1 39015 29835 35190 +41 15 1 39525 29835 35700 +42 15 1 40290 29835 36210 +43 15 1 41055 29835 36465 +44 15 1 41565 29835 36975 +45 15 1 42330 29835 37230 +46 15 1 43095 29835 37740 +47 15 1 43605 29835 38250 +48 15 1 44370 29835 38505 +49 15 1 45645 29835 39270 +50 15 1 46410 29835 39780 +51 15 1 47175 29835 40290 +52 15 1 47685 29835 40545 +53 15 1 48450 29835 41055 +54 15 1 49215 29835 41565 +55 15 1 49725 29835 41820 +56 15 1 50490 29835 42330 +57 15 1 51255 29835 42585 +58 15 1 52530 29835 43605 +59 15 1 53295 29835 43860 +60 15 1 54060 29835 44370 +61 15 1 54570 29835 44625 +62 15 1 55335 29835 45135 +63 15 1 56100 29835 45645 +0 16 1 8670 30855 17595 +1 16 1 9435 30855 18105 +2 16 1 10200 30855 18360 +3 16 1 10710 30855 18870 +4 16 1 11475 30855 19125 +5 16 1 12240 30855 19635 +6 16 1 13005 30855 20145 +7 16 1 50745 32640 16320 +8 16 1 49980 32640 16320 +9 16 1 49470 32640 16320 +10 16 1 48705 32640 16320 +11 16 1 47940 32640 16320 +12 16 1 47430 32640 16320 +13 16 1 46665 32640 16320 +14 16 1 45900 32640 16320 +15 16 1 45390 32640 16320 +16 16 1 43860 32640 16320 +17 16 1 43350 32640 16320 +18 16 1 42585 32640 16320 +19 16 1 41820 32640 16320 +20 16 1 41310 32640 16320 +21 16 1 40545 32640 16320 +22 16 1 39780 32640 16320 +23 16 1 39015 32640 16320 +24 16 1 38505 32640 16320 +25 16 1 27285 30855 28560 +26 16 1 28050 30855 29070 +27 16 1 29325 30855 29835 +28 16 1 30090 30855 30345 +29 16 1 30600 30855 30855 +30 16 1 31365 30855 31110 +31 16 1 32130 30855 31620 +32 16 1 32640 30855 31875 +33 16 1 33405 30855 32385 +34 16 1 34170 30855 32895 +35 16 1 34680 30855 33150 +36 16 1 35445 30855 33660 +37 16 1 36210 30855 33915 +38 16 1 37485 30855 34935 +39 16 1 38250 30855 35190 +40 16 1 39015 30855 35700 +41 16 1 39525 30855 36210 +42 16 1 40290 30855 36465 +43 16 1 41055 30855 36975 +44 16 1 41565 30855 37230 +45 16 1 42330 30855 37740 +46 16 1 43095 30855 38250 +47 16 1 43605 30855 38505 +48 16 1 44370 30855 39015 +49 16 1 45645 30855 39780 +50 16 1 46410 30855 40290 +51 16 1 47175 30855 40545 +52 16 1 47685 30855 41055 +53 16 1 48450 30855 41565 +54 16 1 49215 30855 41820 +55 16 1 49725 30855 42330 +56 16 1 50490 30855 42585 +57 16 1 51255 30855 43095 +58 16 1 52530 30855 43860 +59 16 1 53295 30855 44370 +60 16 1 54060 30855 44625 +61 16 1 54570 30855 45135 +62 16 1 55335 30855 45645 +63 16 1 56100 30855 45900 +0 17 1 8670 31875 18105 +1 17 1 9435 31875 18360 +2 17 1 10200 31875 18870 +3 17 1 10710 31875 19125 +4 17 1 11475 31875 19635 +5 17 1 12240 31875 20145 +6 17 1 13005 31875 20400 +7 17 1 50745 32640 16320 +8 17 1 49980 32640 16320 +9 17 1 49470 32640 16320 +10 17 1 48705 32640 16320 +11 17 1 47940 32640 16320 +12 17 1 47430 32640 16320 +13 17 1 46665 32640 16320 +14 17 1 45900 32640 16320 +15 17 1 45390 32640 16320 +16 17 1 43860 32640 16320 +17 17 1 43350 32640 16320 +18 17 1 42585 32640 16320 +19 17 1 41820 32640 16320 +20 17 1 41310 32640 16320 +21 17 1 40545 32640 16320 +22 17 1 39780 32640 16320 +23 17 1 39015 32640 16320 +24 17 1 38505 32640 16320 +25 17 1 27285 31875 29070 +26 17 1 28050 31875 29580 +27 17 1 29325 31875 30345 +28 17 1 30090 31875 30855 +29 17 1 30600 31875 31110 +30 17 1 31365 31875 31620 +31 17 1 32130 31875 31875 +32 17 1 32640 31875 32385 +33 17 1 33405 31875 32895 +34 17 1 34170 31875 33150 +35 17 1 34680 31875 33660 +36 17 1 35445 31875 33915 +37 17 1 36210 31875 34425 +38 17 1 37485 31875 35190 +39 17 1 38250 31875 35700 +40 17 1 39015 31875 36210 +41 17 1 39525 31875 36465 +42 17 1 40290 31875 36975 +43 17 1 41055 31875 37230 +44 17 1 41565 31875 37740 +45 17 1 42330 31875 38250 +46 17 1 43095 31875 38505 +47 17 1 43605 31875 39015 +48 17 1 44370 31875 39270 +49 17 1 45645 31875 40290 +50 17 1 46410 31875 40545 +51 17 1 47175 31875 41055 +52 17 1 47685 31875 41565 +53 17 1 48450 31875 41820 +54 17 1 49215 31875 42330 +55 17 1 49725 31875 42585 +56 17 1 50490 31875 43095 +57 17 1 51255 31875 43605 +58 17 1 52530 31875 44370 +59 17 1 53295 31875 44625 +60 17 1 54060 31875 45135 +61 17 1 54570 31875 45645 +62 17 1 55335 31875 45900 +63 17 1 56100 31875 46410 +0 18 1 8670 32895 18360 +1 18 1 9435 32895 18870 +2 18 1 10200 32895 19125 +3 18 1 10710 32895 19635 +4 18 1 11475 32895 20145 +5 18 1 12240 32895 20400 +6 18 1 13005 32895 20910 +7 18 1 50745 32640 16320 +8 18 1 49980 32640 16320 +9 18 1 49470 32640 16320 +10 18 1 48705 32640 16320 +11 18 1 47940 32640 16320 +12 18 1 47430 32640 16320 +13 18 1 46665 32640 16320 +14 18 1 45900 32640 16320 +15 18 1 45390 32640 16320 +16 18 1 43860 32640 16320 +17 18 1 43350 32640 16320 +18 18 1 42585 32640 16320 +19 18 1 41820 32640 16320 +20 18 1 41310 32640 16320 +21 18 1 40545 32640 16320 +22 18 1 39780 32640 16320 +23 18 1 39015 32640 16320 +24 18 1 38505 32640 16320 +25 18 1 27285 32895 29580 +26 18 1 28050 32895 29835 +27 18 1 29325 32895 30855 +28 18 1 30090 32895 31110 +29 18 1 30600 32895 31620 +30 18 1 31365 32895 31875 +31 18 1 32130 32895 32385 +32 18 1 32640 32895 32895 +33 18 1 33405 32895 33150 +34 18 1 34170 32895 33660 +35 18 1 34680 32895 33915 +36 18 1 35445 32895 34425 +37 18 1 36210 32895 34935 +38 18 1 37485 32895 35700 +39 18 1 38250 32895 36210 +40 18 1 39015 32895 36465 +41 18 1 39525 32895 36975 +42 18 1 40290 32895 37230 +43 18 1 41055 32895 37740 +44 18 1 41565 32895 38250 +45 18 1 42330 32895 38505 +46 18 1 43095 32895 39015 +47 18 1 43605 32895 39270 +48 18 1 44370 32895 39780 +49 18 1 45645 32895 40545 +50 18 1 46410 32895 41055 +51 18 1 47175 32895 41565 +52 18 1 47685 32895 41820 +53 18 1 48450 32895 42330 +54 18 1 49215 32895 42585 +55 18 1 49725 32895 43095 +56 18 1 50490 32895 43605 +57 18 1 51255 32895 43860 +58 18 1 52530 32895 44625 +59 18 1 53295 32895 45135 +60 18 1 54060 32895 45645 +61 18 1 54570 32895 45900 +62 18 1 55335 32895 46410 +63 18 1 56100 32895 46665 +0 19 1 8670 33915 18870 +1 19 1 9435 33915 19125 +2 19 1 10200 33915 19635 +3 19 1 10710 33915 20145 +4 19 1 11475 33915 20400 +5 19 1 12240 33915 20910 +6 19 1 13005 33915 21165 +7 19 1 50745 32640 16320 +8 19 1 49980 32640 16320 +9 19 1 49470 32640 16320 +10 19 1 48705 32640 16320 +11 19 1 47940 32640 16320 +12 19 1 47430 32640 16320 +13 19 1 46665 32640 16320 +14 19 1 45900 32640 16320 +15 19 1 45390 32640 16320 +16 19 1 43860 32640 16320 +17 19 1 43350 32640 16320 +18 19 1 42585 32640 16320 +19 19 1 41820 32640 16320 +20 19 1 41310 32640 16320 +21 19 1 40545 32640 16320 +22 19 1 39780 32640 16320 +23 19 1 39015 32640 16320 +24 19 1 38505 32640 16320 +25 19 1 27285 34935 30345 +26 19 1 28050 34935 30855 +27 19 1 29325 34935 31620 +28 19 1 30090 34935 31875 +29 19 1 30600 34935 32385 +30 19 1 31365 34935 32895 +31 19 1 32130 34935 33150 +32 19 1 32640 34935 33660 +33 19 1 33405 34935 33915 +34 19 1 34170 34935 34425 +35 19 1 34680 34935 34935 +36 19 1 35445 34935 35190 +37 19 1 36210 34935 35700 +38 19 1 37485 34935 36465 +39 19 1 38250 34935 36975 +40 19 1 39015 34935 37230 +41 19 1 39525 34935 37740 +42 19 1 40290 34935 38250 +43 19 1 41055 34935 38505 +44 19 1 41565 34935 39015 +45 19 1 42330 34935 39270 +46 19 1 43095 34935 39780 +47 19 1 43605 34935 40290 +48 19 1 44370 34935 40545 +49 19 1 45645 34935 41565 +50 19 1 46410 34935 41820 +51 19 1 47175 33915 41820 +52 19 1 47685 33915 42330 +53 19 1 48450 33915 42585 +54 19 1 49215 33915 43095 +55 19 1 49725 33915 43605 +56 19 1 50490 33915 43860 +57 19 1 51255 33915 44370 +58 19 1 52530 33915 45135 +59 19 1 53295 33915 45645 +60 19 1 54060 33915 45900 +61 19 1 54570 33915 46410 +62 19 1 55335 33915 46665 +63 19 1 56100 33915 47175 +0 20 1 8670 35955 19635 +1 20 1 9435 35955 20145 +2 20 1 10200 35955 20400 +3 20 1 10710 35955 20910 +4 20 1 11475 35955 21165 +5 20 1 12240 35955 21675 +6 20 1 13005 35955 22185 +7 20 1 14280 35955 22950 +8 20 1 15045 35955 23205 +9 20 1 15555 35955 23715 +10 20 1 16320 35955 24225 +11 20 1 17085 35955 24480 +12 20 1 17595 35955 24990 +13 20 1 18360 35955 25500 +14 20 1 19125 35955 25755 +15 20 1 19635 35955 26265 +16 20 1 21165 35955 27030 +17 20 1 21675 35955 27540 +18 20 1 22440 35955 27795 +19 20 1 23205 35955 28305 +20 20 1 23715 35955 28560 +21 20 1 24480 35955 29070 +22 20 1 25245 35955 29580 +23 20 1 26010 35955 29835 +24 20 1 26520 35955 30345 +25 20 1 27285 35955 30855 +26 20 1 28050 35955 31110 +27 20 1 29325 35955 31875 +28 20 1 30090 35955 32385 +29 20 1 30600 35955 32895 +30 20 1 31365 35955 33150 +31 20 1 32130 35955 33660 +32 20 1 32640 35955 33915 +33 20 1 33405 35955 34425 +34 20 1 34170 35955 34935 +35 20 1 34680 35955 35190 +36 20 1 35445 35955 35700 +37 20 1 36210 35955 36210 +38 20 1 37485 35955 36975 +39 20 1 38250 35955 37230 +40 20 1 39015 35955 37740 +41 20 1 39525 35955 38250 +42 20 1 40290 35955 38505 +43 20 1 41055 35955 39015 +44 20 1 41565 35955 39270 +45 20 1 42330 35955 39780 +46 20 1 43095 35955 40290 +47 20 1 43605 35955 40545 +48 20 1 44370 35955 41055 +49 20 1 45645 35955 41820 +50 20 1 46410 35955 42330 +51 20 1 47175 35955 42585 +52 20 1 47685 35955 43095 +53 20 1 48450 35955 43605 +54 20 1 49215 35955 43860 +55 20 1 49725 35955 44370 +56 20 1 50490 35955 44625 +57 20 1 51255 35955 45135 +58 20 1 52530 35955 45900 +59 20 1 53295 35955 46410 +60 20 1 54060 35955 46665 +61 20 1 54570 35955 47175 +62 20 1 55335 35955 47685 +63 20 1 56100 35955 47940 +0 21 1 8670 36975 20145 +1 21 1 9435 36975 20400 +2 21 1 10200 36975 20910 +3 21 1 10710 36975 21165 +4 21 1 11475 36975 21675 +5 21 1 12240 36975 22185 +6 21 1 13005 36975 22440 +7 21 1 14280 36975 23205 +8 21 1 15045 36975 23715 +9 21 1 15555 36975 24225 +10 21 1 16320 36975 24480 +11 21 1 17085 36975 24990 +12 21 1 17595 36975 25500 +13 21 1 18360 36975 25755 +14 21 1 19125 36975 26265 +15 21 1 19635 36975 26520 +16 21 1 21165 36975 27540 +17 21 1 21675 36975 27795 +18 21 1 22440 36975 28305 +19 21 1 23205 36975 28560 +20 21 1 23715 36975 29070 +21 21 1 24480 36975 29580 +22 21 1 25245 36975 29835 +23 21 1 26010 36975 30345 +24 21 1 26520 36975 30855 +25 21 1 27285 36975 31110 +26 21 1 28050 36975 31620 +27 21 1 29325 36975 32385 +28 21 1 30090 36975 32895 +29 21 1 30600 36975 33150 +30 21 1 31365 36975 33660 +31 21 1 32130 36975 33915 +32 21 1 32640 36975 34425 +33 21 1 33405 36975 34935 +34 21 1 34170 36975 35190 +35 21 1 34680 36975 35700 +36 21 1 35445 36975 36210 +37 21 1 36210 36975 36465 +38 21 1 37485 36975 37230 +39 21 1 38250 36975 37740 +40 21 1 39015 36975 38250 +41 21 1 39525 36975 38505 +42 21 1 40290 36975 39015 +43 21 1 41055 36975 39270 +44 21 1 41565 36975 39780 +45 21 1 42330 36975 40290 +46 21 1 43095 36975 40545 +47 21 1 43605 36975 41055 +48 21 1 44370 36975 41565 +49 21 1 45645 36975 42330 +50 21 1 46410 36975 42585 +51 21 1 47175 36975 43095 +52 21 1 47685 36975 43605 +53 21 1 48450 36975 43860 +54 21 1 49215 36975 44370 +55 21 1 49725 36975 44625 +56 21 1 50490 36975 45135 +57 21 1 51255 36975 45645 +58 21 1 52530 36975 46410 +59 21 1 53295 36975 46665 +60 21 1 54060 36975 47175 +61 21 1 54570 36975 47685 +62 21 1 55335 36975 47940 +63 21 1 56100 36975 48450 +0 22 1 8670 37995 20400 +1 22 1 9435 37995 20910 +2 22 1 10200 37995 21165 +3 22 1 10710 37995 21675 +4 22 1 11475 37995 22185 +5 22 1 12240 37995 22440 +6 22 1 13005 37995 22950 +7 22 1 14280 37995 23715 +8 22 1 15045 37995 24225 +9 22 1 15555 37995 24480 +10 22 1 16320 37995 24990 +11 22 1 17085 37995 25500 +12 22 1 17595 37995 25755 +13 22 1 18360 37995 26265 +14 22 1 19125 37995 26520 +15 22 1 19635 37995 27030 +16 22 1 20400 37995 27540 +17 22 1 21675 37995 28305 +18 22 1 22440 37995 28560 +19 22 1 23205 37995 29070 +20 22 1 23715 37995 29580 +21 22 1 24480 37995 29835 +22 22 1 25245 37995 30345 +23 22 1 26010 37995 30855 +24 22 1 26520 37995 31110 +25 22 1 27285 37995 31620 +26 22 1 28050 37995 31875 +27 22 1 29325 37995 32895 +28 22 1 30090 37995 33150 +29 22 1 30600 37995 33660 +30 22 1 31365 37995 33915 +31 22 1 32130 37995 34425 +32 22 1 32640 37995 34935 +33 22 1 33405 37995 35190 +34 22 1 34170 37995 35700 +35 22 1 34680 37995 36210 +36 22 1 35445 37995 36465 +37 22 1 36210 37995 36975 +38 22 1 37485 37995 37740 +39 22 1 38250 37995 38250 +40 22 1 39015 37995 38505 +41 22 1 39525 37995 39015 +42 22 1 40290 37995 39270 +43 22 1 41055 37995 39780 +44 22 1 41565 37995 40290 +45 22 1 42330 37995 40545 +46 22 1 43095 37995 41055 +47 22 1 43605 37995 41565 +48 22 1 45135 37995 42330 +49 22 1 45645 37995 42585 +50 22 1 46410 37995 43095 +51 22 1 47175 37995 43605 +52 22 1 47685 37995 43860 +53 22 1 48450 37995 44370 +54 22 1 49215 37995 44625 +55 22 1 49725 37995 45135 +56 22 1 50490 37995 45645 +57 22 1 51255 37995 45900 +58 22 1 52530 37995 46665 +59 22 1 53295 37995 47175 +60 22 1 54060 37995 47685 +61 22 1 54570 37995 47940 +62 22 1 55335 37995 48450 +63 22 1 56100 37995 48960 +0 23 1 8670 39015 20910 +1 23 1 9435 39015 21165 +2 23 1 10200 39015 21675 +3 23 1 10710 39015 22185 +4 23 1 11475 39015 22440 +5 23 1 12240 39015 22950 +6 23 1 13005 39015 23205 +7 23 1 14280 39015 24225 +8 23 1 15045 39015 24480 +9 23 1 15555 39015 24990 +10 23 1 16320 39015 25500 +11 23 1 17085 39015 25755 +12 23 1 17595 39015 26265 +13 23 1 18360 39015 26520 +14 23 1 19125 39015 27030 +15 23 1 19635 39015 27540 +16 23 1 20400 39015 27795 +17 23 1 21675 39015 28560 +18 23 1 22440 39015 29070 +19 23 1 23205 39015 29580 +20 23 1 23715 39015 29835 +21 23 1 24480 39015 30345 +22 23 1 25245 39015 30855 +23 23 1 26010 39015 31110 +24 23 1 26520 39015 31620 +25 23 1 27285 39015 31875 +26 23 1 28050 39015 32385 +27 23 1 29325 39015 33150 +28 23 1 30090 39015 33660 +29 23 1 30600 39015 33915 +30 23 1 31365 39015 34425 +31 23 1 32130 39015 34935 +32 23 1 32640 39015 35190 +33 23 1 33405 39015 35700 +34 23 1 34170 39015 36210 +35 23 1 34680 39015 36465 +36 23 1 35445 39015 36975 +37 23 1 36210 39015 37230 +38 23 1 37485 39015 38250 +39 23 1 38250 39015 38505 +40 23 1 39015 39015 39015 +41 23 1 39525 39015 39270 +42 23 1 40290 39015 39780 +43 23 1 41055 39015 40290 +44 23 1 41565 39015 40545 +45 23 1 42330 39015 41055 +46 23 1 43095 39015 41565 +47 23 1 43605 39015 41820 +48 23 1 45135 39015 42585 +49 23 1 45645 39015 43095 +50 23 1 46410 39015 43605 +51 23 1 47175 39015 43860 +52 23 1 47685 39015 44370 +53 23 1 48450 39015 44625 +54 23 1 49215 39015 45135 +55 23 1 49725 39015 45645 +56 23 1 50490 39015 45900 +57 23 1 51255 39015 46410 +58 23 1 52530 39015 47175 +59 23 1 53295 39015 47685 +60 23 1 54060 39015 47940 +61 23 1 54570 39015 48450 +62 23 1 55335 39015 48960 +63 23 1 56100 39015 49215 +0 24 1 8670 40035 21165 +1 24 1 9435 40035 21675 +2 24 1 10200 40035 22185 +3 24 1 10710 40035 22440 +4 24 1 11475 40035 22950 +5 24 1 12240 40035 23205 +6 24 1 13005 40035 23715 +7 24 1 14280 40035 24480 +8 24 1 15045 40035 24990 +9 24 1 15555 40035 25500 +10 24 1 16320 40035 25755 +11 24 1 17085 40035 26265 +12 24 1 17595 40035 26520 +13 24 1 18360 40035 27030 +14 24 1 19125 40035 27540 +15 24 1 19635 40035 27795 +16 24 1 20400 40035 28305 +17 24 1 21675 40035 29070 +18 24 1 22440 40035 29580 +19 24 1 23205 40035 29835 +20 24 1 23715 40035 30345 +21 24 1 24480 40035 30855 +22 24 1 25245 40035 31110 +23 24 1 26010 40035 31620 +24 24 1 26520 40035 31875 +25 24 1 27285 40035 32385 +26 24 1 28050 40035 32895 +27 24 1 29325 40035 33660 +28 24 1 30090 40035 33915 +29 24 1 30600 40035 34425 +30 24 1 31365 40035 34935 +31 24 1 32130 40035 35190 +32 24 1 32640 40035 35700 +33 24 1 33405 40035 36210 +34 24 1 34170 40035 36465 +35 24 1 34680 40035 36975 +36 24 1 35445 40035 37230 +37 24 1 36210 40035 37740 +38 24 1 37485 40035 38505 +39 24 1 38250 40035 39015 +40 24 1 39015 40035 39270 +41 24 1 39525 40035 39780 +42 24 1 40290 40035 40290 +43 24 1 41055 40035 40545 +44 24 1 41565 40035 41055 +45 24 1 42330 40035 41565 +46 24 1 43095 40035 41820 +47 24 1 43605 40035 42330 +48 24 1 45135 40035 43095 +49 24 1 45645 40035 43605 +50 24 1 46410 40035 43860 +51 24 1 47175 40035 44370 +52 24 1 47685 40035 44625 +53 24 1 48450 40035 45135 +54 24 1 49215 40035 45645 +55 24 1 49725 40035 45900 +56 24 1 50490 40035 46410 +57 24 1 51255 40035 46665 +58 24 1 52530 40035 47685 +59 24 1 53295 40035 47940 +60 24 1 54060 40035 48450 +61 24 1 54570 40035 48960 +62 24 1 55335 40035 49215 +63 24 1 56100 40035 49725 +0 25 1 8670 41055 21675 +1 25 1 9435 41055 22185 +2 25 1 10200 41055 22440 +3 25 1 10710 41055 22950 +4 25 1 11475 41055 23205 +5 25 1 12240 41055 23715 +6 25 1 13005 41055 24225 +7 25 1 14280 41055 24990 +8 25 1 15045 41055 25500 +9 25 1 15555 41055 25755 +10 25 1 16320 41055 26265 +11 25 1 17085 41055 26520 +12 25 1 17595 41055 27030 +13 25 1 18360 41055 27540 +14 25 1 19125 41055 27795 +15 25 1 19635 41055 28305 +16 25 1 20400 41055 28560 +17 25 1 21675 41055 29580 +18 25 1 22440 41055 29835 +19 25 1 23205 41055 30345 +20 25 1 23715 41055 30855 +21 25 1 24480 41055 31110 +22 25 1 25245 41055 31620 +23 25 1 26010 41055 31875 +24 25 1 26520 41055 32385 +25 25 1 27285 41055 32895 +26 25 1 28050 41055 33150 +27 25 1 29325 41055 33915 +28 25 1 30090 41055 34425 +29 25 1 30600 41055 34935 +30 25 1 31365 41055 35190 +31 25 1 32130 41055 35700 +32 25 1 32640 41055 36210 +33 25 1 33405 41055 36465 +34 25 1 34170 41055 36975 +35 25 1 34680 41055 37230 +36 25 1 35445 41055 37740 +37 25 1 36210 41055 38250 +38 25 1 37485 41055 39015 +39 25 1 38250 41055 39270 +40 25 1 39015 41055 39780 +41 25 1 39525 41055 40290 +42 25 1 40290 41055 40545 +43 25 1 41055 41055 41055 +44 25 1 41565 41055 41565 +45 25 1 42330 41055 41820 +46 25 1 43095 41055 42330 +47 25 1 43605 41055 42585 +48 25 1 45135 41055 43605 +49 25 1 45645 41055 43860 +50 25 1 46410 41055 44370 +51 25 1 47175 41055 44625 +52 25 1 47685 41055 45135 +53 25 1 48450 41055 45645 +54 25 1 49215 41055 45900 +55 25 1 49725 41055 46410 +56 25 1 50490 41055 46665 +57 25 1 51255 41055 47175 +58 25 1 52530 41055 47940 +59 25 1 53295 41055 48450 +60 25 1 54060 41055 48960 +61 25 1 54570 41055 49215 +62 25 1 55335 41055 49725 +63 25 1 56100 41055 49980 +0 26 1 8670 42075 22185 +1 26 1 9435 42075 22440 +2 26 1 10200 42075 22950 +3 26 1 10710 42075 23205 +4 26 1 11475 42075 23715 +5 26 1 12240 42075 24225 +6 26 1 13005 42075 24480 +7 26 1 14280 42075 25500 +8 26 1 15045 42075 25755 +9 26 1 15555 42075 26265 +10 26 1 16320 42075 26520 +11 26 1 17085 42075 27030 +12 26 1 17595 42075 27540 +13 26 1 18360 42075 27795 +14 26 1 19125 42075 28305 +15 26 1 19635 42075 28560 +16 26 1 20400 42075 29070 +17 26 1 21675 42075 29835 +18 26 1 22440 42075 30345 +19 26 1 23205 42075 30855 +20 26 1 23715 42075 31110 +21 26 1 24480 42075 31620 +22 26 1 25245 42075 31875 +23 26 1 26010 42075 32385 +24 26 1 26520 42075 32895 +25 26 1 27285 42075 33150 +26 26 1 28050 42075 33660 +27 26 1 29325 42075 34425 +28 26 1 30090 42075 34935 +29 26 1 30600 42075 35190 +30 26 1 31365 42075 35700 +31 26 1 32130 42075 36210 +32 26 1 32640 42075 36465 +33 26 1 33405 42075 36975 +34 26 1 34170 42075 37230 +35 26 1 34680 42075 37740 +36 26 1 35445 42075 38250 +37 26 1 36210 42075 38505 +38 26 1 37485 42075 39270 +39 26 1 38250 42075 39780 +40 26 1 39015 42075 40290 +41 26 1 39525 42075 40545 +42 26 1 40290 42075 41055 +43 26 1 41055 42075 41565 +44 26 1 41565 42075 41820 +45 26 1 42330 42075 42330 +46 26 1 43095 42075 42585 +47 26 1 43605 42075 43095 +48 26 1 45135 42075 43860 +49 26 1 45645 42075 44370 +50 26 1 46410 42075 44625 +51 26 1 47175 42075 45135 +52 26 1 47685 42075 45645 +53 26 1 48450 42075 45900 +54 26 1 49215 42075 46410 +55 26 1 49725 42075 46665 +56 26 1 50490 42075 47175 +57 26 1 51255 42075 47685 +58 26 1 52530 42075 48450 +59 26 1 53295 42075 48960 +60 26 1 54060 42075 49215 +61 26 1 54570 42075 49725 +62 26 1 55335 42075 49980 +63 26 1 56100 42075 50490 +0 27 1 8670 43350 22440 +1 27 1 9435 43350 22950 +2 27 1 10200 43350 23205 +3 27 1 10710 43350 23715 +4 27 1 11475 43350 24225 +5 27 1 12240 43350 24480 +6 27 1 13005 43350 24990 +7 27 1 14280 43350 25755 +8 27 1 15045 43350 26265 +9 27 1 15555 43350 26520 +10 27 1 16320 43350 27030 +11 27 1 17085 43350 27540 +12 27 1 17595 43350 27795 +13 27 1 18360 43350 28305 +14 27 1 19125 43350 28560 +15 27 1 19635 43350 29070 +16 27 1 20400 43350 29580 +17 27 1 21675 43350 30345 +18 27 1 22440 43350 30855 +19 27 1 23205 43350 31110 +20 27 1 23715 43350 31620 +21 27 1 24480 43350 31875 +22 27 1 25245 43350 32385 +23 27 1 26010 43350 32895 +24 27 1 26520 43350 33150 +25 27 1 27285 43350 33660 +26 27 1 28050 43350 33915 +27 27 1 29325 43350 34935 +28 27 1 30090 43350 35190 +29 27 1 30600 43350 35700 +30 27 1 31365 43350 36210 +31 27 1 32130 43350 36465 +32 27 1 32640 43350 36975 +33 27 1 33405 43350 37230 +34 27 1 34170 43350 37740 +35 27 1 34680 43350 38250 +36 27 1 35445 43350 38505 +37 27 1 36210 43350 39015 +38 27 1 37485 43350 39780 +39 27 1 38250 43350 40290 +40 27 1 39015 43350 40545 +41 27 1 39525 43350 41055 +42 27 1 40290 43350 41565 +43 27 1 41055 43350 41820 +44 27 1 41565 43350 42330 +45 27 1 42330 43350 42585 +46 27 1 43095 43350 43095 +47 27 1 43605 43350 43605 +48 27 1 45135 43350 44370 +49 27 1 45645 43350 44625 +50 27 1 46410 43350 45135 +51 27 1 47175 43350 45645 +52 27 1 47685 43350 45900 +53 27 1 48450 43350 46410 +54 27 1 49215 43350 46665 +55 27 1 49725 43350 47175 +56 27 1 50490 43350 47685 +57 27 1 51255 43350 47940 +58 27 1 52530 43350 48960 +59 27 1 53295 43350 49215 +60 27 1 54060 43350 49725 +61 27 1 54570 43350 49980 +62 27 1 55335 43350 50490 +63 27 1 56100 43350 51000 +0 28 1 8670 44370 22950 +1 28 1 9435 44370 23205 +2 28 1 10200 44370 23715 +3 28 1 10710 44370 24225 +4 28 1 11475 44370 24480 +5 28 1 12240 44370 24990 +6 28 1 13005 44370 25500 +7 28 1 14280 44370 26265 +8 28 1 15045 44370 26520 +9 28 1 15555 44370 27030 +10 28 1 16320 44370 27540 +11 28 1 17085 44370 27795 +12 28 1 17595 44370 28305 +13 28 1 18360 44370 28560 +14 28 1 19125 44370 29070 +15 28 1 19635 44370 29580 +16 28 1 20400 44370 29835 +17 28 1 21675 44370 30855 +18 28 1 22440 44370 31110 +19 28 1 23205 44370 31620 +20 28 1 23715 44370 31875 +21 28 1 24480 44370 32385 +22 28 1 25245 44370 32895 +23 28 1 26010 44370 33150 +24 28 1 26520 44370 33660 +25 28 1 27285 44370 33915 +26 28 1 28050 44370 34425 +27 28 1 29325 44370 35190 +28 28 1 30090 44370 35700 +29 28 1 30600 44370 36210 +30 28 1 31365 44370 36465 +31 28 1 32130 44370 36975 +32 28 1 32640 44370 37230 +33 28 1 33405 44370 37740 +34 28 1 34170 44370 38250 +35 28 1 34680 44370 38505 +36 28 1 35445 44370 39015 +37 28 1 36210 44370 39270 +38 28 1 37485 44370 40290 +39 28 1 38250 44370 40545 +40 28 1 39015 44370 41055 +41 28 1 39525 44370 41565 +42 28 1 40290 44370 41820 +43 28 1 41055 44370 42330 +44 28 1 41565 44370 42585 +45 28 1 42330 44370 43095 +46 28 1 43095 44370 43605 +47 28 1 43605 44370 43860 +48 28 1 45135 44370 44625 +49 28 1 45645 44370 45135 +50 28 1 46410 44370 45645 +51 28 1 47175 44370 45900 +52 28 1 47685 44370 46410 +53 28 1 48450 44370 46665 +54 28 1 49215 44370 47175 +55 28 1 49725 44370 47685 +56 28 1 50490 44370 47940 +57 28 1 51255 44370 48450 +58 28 1 52530 44370 49215 +59 28 1 53295 44370 49725 +60 28 1 54060 44370 49980 +61 28 1 54570 44370 50490 +62 28 1 55335 44370 51000 +63 28 1 56100 44370 51255 +0 29 1 8670 46410 23715 +1 29 1 9435 46410 24225 +2 29 1 10200 46410 24480 +3 29 1 10710 46410 24990 +4 29 1 11475 46410 25500 +5 29 1 12240 46410 25755 +6 29 1 13005 46410 26265 +7 29 1 14280 46410 27030 +8 29 1 15045 45390 27030 +9 29 1 15555 45390 27540 +10 29 1 16320 45390 27795 +11 29 1 17085 45390 28305 +12 29 1 17595 45390 28560 +13 29 1 18360 45390 29070 +14 29 1 19125 45390 29580 +15 29 1 19635 45390 29835 +16 29 1 20400 45390 30345 +17 29 1 21675 45390 31110 +18 29 1 22440 45390 31620 +19 29 1 23205 45390 31875 +20 29 1 23715 45390 32385 +21 29 1 24480 45390 32895 +22 29 1 25245 45390 33150 +23 29 1 26010 45390 33660 +24 29 1 26520 45390 33915 +25 29 1 27285 45390 34425 +26 29 1 28050 45390 34935 +27 29 1 29325 45390 35700 +28 29 1 30090 45390 36210 +29 29 1 30600 45390 36465 +30 29 1 31365 45390 36975 +31 29 1 32130 45390 37230 +32 29 1 32640 45390 37740 +33 29 1 33405 45390 38250 +34 29 1 34170 45390 38505 +35 29 1 34680 45390 39015 +36 29 1 35445 45390 39270 +37 29 1 36210 45390 39780 +38 29 1 37485 45390 40545 +39 29 1 38250 45390 41055 +40 29 1 39015 45390 41565 +41 29 1 39525 45390 41820 +42 29 1 40290 45390 42330 +43 29 1 41055 45390 42585 +44 29 1 41565 45390 43095 +45 29 1 42330 45390 43605 +46 29 1 43095 45390 43860 +47 29 1 43605 45390 44370 +48 29 1 45135 45390 45135 +49 29 1 45645 45390 45645 +50 29 1 46410 45390 45900 +51 29 1 47175 45390 46410 +52 29 1 47685 45390 46665 +53 29 1 48450 45390 47175 +54 29 1 49215 45390 47685 +55 29 1 49725 45390 47940 +56 29 1 50490 45390 48450 +57 29 1 51255 46410 49215 +58 29 1 52530 46410 49980 +59 29 1 53295 46410 50490 +60 29 1 54060 46410 51000 +61 29 1 54570 46410 51255 +62 29 1 55335 46410 51765 +63 29 1 56100 46410 52020 +0 30 1 8670 47430 24225 +1 30 1 9435 47430 24480 +2 30 1 10200 47430 24990 +3 30 1 10710 47430 25500 +4 30 1 11475 47430 25755 +5 30 1 12240 47430 26265 +6 30 1 13005 47430 26520 +7 30 1 14280 47430 27540 +8 30 1 15045 47430 27795 +9 30 1 15555 47430 28305 +10 30 1 16320 47430 28560 +11 30 1 17085 47430 29070 +12 30 1 17595 47430 29580 +13 30 1 18360 47430 29835 +14 30 1 19125 47430 30345 +15 30 1 19635 47430 30855 +16 30 1 20400 47430 31110 +17 30 1 21675 47430 31875 +18 30 1 22440 47430 32385 +19 30 1 23205 47430 32895 +20 30 1 23715 47430 33150 +21 30 1 24480 47430 33660 +22 30 1 25245 47430 33915 +23 30 1 26010 47430 34425 +24 30 1 26520 47430 34935 +25 30 1 27285 47430 35190 +26 30 1 28050 47430 35700 +27 30 1 29325 47430 36465 +28 30 1 30090 47430 36975 +29 30 1 30600 47430 37230 +30 30 1 31365 47430 37740 +31 30 1 32130 47430 38250 +32 30 1 32640 47430 38505 +33 30 1 33405 47430 39015 +34 30 1 34170 47430 39270 +35 30 1 34680 47430 39780 +36 30 1 35445 47430 40290 +37 30 1 36210 47430 40545 +38 30 1 37485 47430 41565 +39 30 1 38250 47430 41820 +40 30 1 39015 47430 42330 +41 30 1 39525 47430 42585 +42 30 1 40290 47430 43095 +43 30 1 41055 47430 43605 +44 30 1 41565 47430 43860 +45 30 1 42330 47430 44370 +46 30 1 43095 47430 44625 +47 30 1 43605 47430 45135 +48 30 1 45135 47430 45900 +49 30 1 45645 47430 46410 +50 30 1 46410 47430 46665 +51 30 1 47175 47430 47175 +52 30 1 47685 47430 47685 +53 30 1 48450 47430 47940 +54 30 1 49215 47430 48450 +55 30 1 49725 47430 48960 +56 30 1 50490 47430 49215 +57 30 1 51255 47430 49725 +58 30 1 52530 47430 50490 +59 30 1 53295 47430 51000 +60 30 1 54060 47430 51255 +61 30 1 54570 47430 51765 +62 30 1 55335 47430 52020 +63 30 1 56100 47430 52530 +0 31 1 8670 48450 24480 +1 31 1 9435 48450 24990 +2 31 1 10200 48450 25500 +3 31 1 10710 48450 25755 +4 31 1 11475 48450 26265 +5 31 1 12240 48450 26520 +6 31 1 13005 48450 27030 +7 31 1 14280 48450 27795 +8 31 1 15045 48450 28305 +9 31 1 15555 48450 28560 +10 31 1 16320 48450 29070 +11 31 1 17085 48450 29580 +12 31 1 17595 48450 29835 +13 31 1 18360 48450 30345 +14 31 1 19125 48450 30855 +15 31 1 19635 48450 31110 +16 31 1 20400 48450 31620 +17 31 1 21675 48450 32385 +18 31 1 22440 48450 32895 +19 31 1 23205 48450 33150 +20 31 1 23715 48450 33660 +21 31 1 24480 48450 33915 +22 31 1 25245 48450 34425 +23 31 1 26010 48450 34935 +24 31 1 26520 48450 35190 +25 31 1 27285 48450 35700 +26 31 1 28050 48450 36210 +27 31 1 29325 48450 36975 +28 31 1 30090 48450 37230 +29 31 1 30600 48450 37740 +30 31 1 31365 48450 38250 +31 31 1 32130 48450 38505 +32 31 1 32640 48450 39015 +33 31 1 33405 48450 39270 +34 31 1 34170 48450 39780 +35 31 1 34680 48450 40290 +36 31 1 35445 48450 40545 +37 31 1 36210 48450 41055 +38 31 1 37485 48450 41820 +39 31 1 38250 48450 42330 +40 31 1 39015 48450 42585 +41 31 1 39525 48450 43095 +42 31 1 40290 48450 43605 +43 31 1 41055 48450 43860 +44 31 1 41565 48450 44370 +45 31 1 42330 48450 44625 +46 31 1 43095 48450 45135 +47 31 1 43605 48450 45645 +48 31 1 45135 48450 46410 +49 31 1 45645 48450 46665 +50 31 1 46410 48450 47175 +51 31 1 47175 48450 47685 +52 31 1 47685 48450 47940 +53 31 1 48450 48450 48450 +54 31 1 49215 48450 48960 +55 31 1 49725 48450 49215 +56 31 1 50490 48450 49725 +57 31 1 51255 48450 49980 +58 31 1 52530 48450 51000 +59 31 1 53295 48450 51255 +60 31 1 54060 48450 51765 +61 31 1 54570 48450 52020 +62 31 1 55335 48450 52530 +63 31 1 56100 48450 53040 +0 32 1 8670 49470 24990 +1 32 1 9435 49470 25500 +2 32 1 10200 49470 25755 +3 32 1 10710 49470 26265 +4 32 1 11475 49470 26520 +5 32 1 12240 49470 27030 +6 32 1 13005 49470 27540 +7 32 1 13515 49470 27795 +8 32 1 15045 49470 28560 +9 32 1 15555 49470 29070 +10 32 1 16320 49470 29580 +11 32 1 17085 49470 29835 +12 32 1 17595 49470 30345 +13 32 1 18360 49470 30855 +14 32 1 19125 49470 31110 +15 32 1 19635 49470 31620 +16 32 1 20400 49470 31875 +17 32 1 21675 49470 32895 +18 32 1 22440 49470 33150 +19 32 1 23205 49470 33660 +20 32 1 23715 49470 33915 +21 32 1 24480 49470 34425 +22 32 1 25245 49470 34935 +23 32 1 26010 49470 35190 +24 32 1 26520 49470 35700 +25 32 1 27285 49470 36210 +26 32 1 28050 49470 36465 +27 32 1 29325 49470 37230 +28 32 1 30090 49470 37740 +29 32 1 30600 49470 38250 +30 32 1 31365 49470 38505 +31 32 1 32130 49470 39015 +32 32 1 32640 49470 39270 +33 32 1 33405 49470 39780 +34 32 1 34170 49470 40290 +35 32 1 34680 49470 40545 +36 32 1 35445 49470 41055 +37 32 1 36210 49470 41565 +38 32 1 37485 49470 42330 +39 32 1 38250 49470 42585 +40 32 1 39015 49470 43095 +41 32 1 39525 49470 43605 +42 32 1 40290 49470 43860 +43 32 1 41055 49470 44370 +44 32 1 41565 49470 44625 +45 32 1 42330 49470 45135 +46 32 1 43095 49470 45645 +47 32 1 43605 49470 45900 +48 32 1 45135 49470 46665 +49 32 1 45645 49470 47175 +50 32 1 46410 49470 47685 +51 32 1 47175 49470 47940 +52 32 1 47685 49470 48450 +53 32 1 48450 49470 48960 +54 32 1 49215 49470 49215 +55 32 1 49725 49470 49725 +56 32 1 50490 49470 49980 +57 32 1 52020 49470 51000 +58 32 1 52530 49470 51255 +59 32 1 53295 49470 51765 +60 32 1 54060 49470 52020 +61 32 1 54570 49470 52530 +62 32 1 55335 49470 53040 +63 32 1 56100 49470 53295 +0 33 1 8670 50490 25500 +1 33 1 9435 50490 25755 +2 33 1 10200 50490 26265 +3 33 1 10710 50490 26520 +4 33 1 11475 50490 27030 +5 33 1 12240 50490 27540 +6 33 1 13005 50490 27795 +7 33 1 13515 50490 28305 +8 33 1 15045 50490 29070 +9 33 1 15555 50490 29580 +10 33 1 16320 50490 29835 +11 33 1 17085 50490 30345 +12 33 1 17595 50490 30855 +13 33 1 18360 50490 31110 +14 33 1 19125 50490 31620 +15 33 1 19635 50490 31875 +16 33 1 20400 50490 32385 +17 33 1 21675 50490 33150 +18 33 1 22440 50490 33660 +19 33 1 23205 50490 33915 +20 33 1 23715 50490 34425 +21 33 1 24480 50490 34935 +22 33 1 25245 50490 35190 +23 33 1 26010 50490 35700 +24 33 1 26520 50490 36210 +25 33 1 27285 50490 36465 +26 33 1 28050 50490 36975 +27 33 1 29325 50490 37740 +28 33 1 30090 50490 38250 +29 33 1 30600 50490 38505 +30 33 1 31365 50490 39015 +31 33 1 32130 50490 39270 +32 33 1 32640 50490 39780 +33 33 1 33405 50490 40290 +34 33 1 34170 50490 40545 +35 33 1 34680 50490 41055 +36 33 1 35445 50490 41565 +37 33 1 36210 50490 41820 +38 33 1 37485 50490 42585 +39 33 1 38250 50490 43095 +40 33 1 39015 50490 43605 +41 33 1 39525 50490 43860 +42 33 1 40290 50490 44370 +43 33 1 41055 50490 44625 +44 33 1 41565 50490 45135 +45 33 1 42330 50490 45645 +46 33 1 43095 50490 45900 +47 33 1 43605 50490 46410 +48 33 1 45135 50490 47175 +49 33 1 45645 50490 47685 +50 33 1 46410 50490 47940 +51 33 1 47175 50490 48450 +52 33 1 47685 50490 48960 +53 33 1 48450 50490 49215 +54 33 1 49215 50490 49725 +55 33 1 49725 50490 49980 +56 33 1 50490 50490 50490 +57 33 1 52020 50490 51255 +58 33 1 52530 50490 51765 +59 33 1 53295 50490 52020 +60 33 1 54060 50490 52530 +61 33 1 54570 50490 53040 +62 33 1 55335 50490 53295 +63 33 1 56100 50490 53805 +0 34 1 8670 51510 25755 +1 34 1 9435 51510 26265 +2 34 1 10200 51510 26520 +3 34 1 10710 51510 27030 +4 34 1 11475 51510 27540 +5 34 1 12240 51510 27795 +6 34 1 13005 51510 28305 +7 34 1 13515 51510 28560 +8 34 1 15045 51510 29580 +9 34 1 15555 51510 29835 +10 34 1 16320 51510 30345 +11 34 1 17085 51510 30855 +12 34 1 17595 51510 31110 +13 34 1 18360 51510 31620 +14 34 1 19125 51510 31875 +15 34 1 19635 51510 32385 +16 34 1 20400 51510 32895 +17 34 1 21675 51510 33660 +18 34 1 22440 51510 33915 +19 34 1 23205 51510 34425 +20 34 1 23715 51510 34935 +21 34 1 24480 51510 35190 +22 34 1 25245 51510 35700 +23 34 1 26010 51510 36210 +24 34 1 26520 51510 36465 +25 34 1 27285 51510 36975 +26 34 1 28050 51510 37230 +27 34 1 29325 51510 38250 +28 34 1 30090 51510 38505 +29 34 1 30600 51510 39015 +30 34 1 31365 51510 39270 +31 34 1 32130 51510 39780 +32 34 1 32640 51510 40290 +33 34 1 33405 51510 40545 +34 34 1 34170 51510 41055 +35 34 1 34680 51510 41565 +36 34 1 35445 51510 41820 +37 34 1 36210 51510 42330 +38 34 1 37485 51510 43095 +39 34 1 38250 51510 43605 +40 34 1 39015 51510 43860 +41 34 1 39525 51510 44370 +42 34 1 40290 51510 44625 +43 34 1 41055 51510 45135 +44 34 1 41565 51510 45645 +45 34 1 42330 51510 45900 +46 34 1 43095 51510 46410 +47 34 1 43605 51510 46665 +48 34 1 45135 51510 47685 +49 34 1 45645 51510 47940 +50 34 1 46410 51510 48450 +51 34 1 47175 51510 48960 +52 34 1 47685 51510 49215 +53 34 1 48450 51510 49725 +54 34 1 49215 51510 49980 +55 34 1 49725 51510 50490 +56 34 1 50490 51510 51000 +57 34 1 52020 51510 51765 +58 34 1 52530 51510 52020 +59 34 1 53295 51510 52530 +60 34 1 54060 51510 53040 +61 34 1 54570 51510 53295 +62 34 1 55335 51510 53805 +63 34 1 56100 51510 54315 +0 35 1 8670 52530 26265 +1 35 1 9435 52530 26520 +2 35 1 10200 52530 27030 +3 35 1 10710 52530 27540 +4 35 1 11475 52530 27795 +5 35 1 12240 52530 28305 +6 35 1 13005 52530 28560 +7 35 1 13515 52530 29070 +8 35 1 15045 52530 29835 +9 35 1 15555 52530 30345 +10 35 1 16320 52530 30855 +11 35 1 17085 52530 31110 +12 35 1 17595 52530 31620 +13 35 1 18360 52530 31875 +14 35 1 19125 52530 32385 +15 35 1 19635 52530 32895 +16 35 1 20400 52530 33150 +17 35 1 21675 52530 33915 +18 35 1 22440 52530 34425 +19 35 1 23205 52530 34935 +20 35 1 23715 52530 35190 +21 35 1 24480 52530 35700 +22 35 1 25245 52530 36210 +23 35 1 26010 52530 36465 +24 35 1 26520 52530 36975 +25 35 1 27285 52530 37230 +26 35 1 28050 52530 37740 +27 35 1 29325 52530 38505 +28 35 1 30090 52530 39015 +29 35 1 30600 52530 39270 +30 35 1 31365 52530 39780 +31 35 1 32130 52530 40290 +32 35 1 32640 52530 40545 +33 35 1 33405 52530 41055 +34 35 1 34170 52530 41565 +35 35 1 34680 52530 41820 +36 35 1 35445 52530 42330 +37 35 1 36210 52530 42585 +38 35 1 37485 52530 43605 +39 35 1 38250 52530 43860 +40 35 1 39015 52530 44370 +41 35 1 39525 52530 44625 +42 35 1 40290 52530 45135 +43 35 1 41055 52530 45645 +44 35 1 41565 52530 45900 +45 35 1 42330 52530 46410 +46 35 1 43095 52530 46665 +47 35 1 43605 52530 47175 +48 35 1 45135 52530 47940 +49 35 1 45645 52530 48450 +50 35 1 46410 52530 48960 +51 35 1 47175 52530 49215 +52 35 1 47685 52530 49725 +53 35 1 48450 52530 49980 +54 35 1 49215 52530 50490 +55 35 1 49725 52530 51000 +56 35 1 50490 52530 51255 +57 35 1 52020 52530 52020 +58 35 1 52530 52530 52530 +59 35 1 53295 52530 53040 +60 35 1 54060 52530 53295 +61 35 1 54570 52530 53805 +62 35 1 55335 52530 54315 +63 35 1 56100 52530 54570 +0 36 1 8670 53550 26520 +1 36 1 9435 53550 27030 +2 36 1 10200 53550 27540 +3 36 1 10710 53550 27795 +4 36 1 11475 53550 28305 +5 36 1 12240 53550 28560 +6 36 1 13005 53550 29070 +7 36 1 13515 53550 29580 +8 36 1 15045 53550 30345 +9 36 1 15555 53550 30855 +10 36 1 16320 53550 31110 +11 36 1 17085 53550 31620 +12 36 1 17595 53550 31875 +13 36 1 18360 53550 32385 +14 36 1 19125 53550 32895 +15 36 1 19635 53550 33150 +16 36 1 20400 53550 33660 +17 36 1 21675 53550 34425 +18 36 1 22440 53550 34935 +19 36 1 23205 53550 35190 +20 36 1 23715 53550 35700 +21 36 1 24480 53550 36210 +22 36 1 25245 53550 36465 +23 36 1 26010 53550 36975 +24 36 1 26520 53550 37230 +25 36 1 27285 53550 37740 +26 36 1 28050 53550 38250 +27 36 1 29325 53550 39015 +28 36 1 30090 53550 39270 +29 36 1 30600 53550 39780 +30 36 1 31365 53550 40290 +31 36 1 32130 53550 40545 +32 36 1 32640 53550 41055 +33 36 1 33405 53550 41565 +34 36 1 34170 53550 41820 +35 36 1 34680 53550 42330 +36 36 1 35445 53550 42585 +37 36 1 36210 53550 43095 +38 36 1 37485 53550 43860 +39 36 1 38250 53550 44370 +40 36 1 39015 53550 44625 +41 36 1 39525 53550 45135 +42 36 1 40290 53550 45645 +43 36 1 41055 53550 45900 +44 36 1 41565 53550 46410 +45 36 1 42330 53550 46665 +46 36 1 43095 53550 47175 +47 36 1 43605 53550 47685 +48 36 1 45135 53550 48450 +49 36 1 45645 53550 48960 +50 36 1 46410 53550 49215 +51 36 1 47175 53550 49725 +52 36 1 47685 53550 49980 +53 36 1 48450 53550 50490 +54 36 1 49215 53550 51000 +55 36 1 49725 53550 51255 +56 36 1 50490 53550 51765 +57 36 1 52020 53550 52530 +58 36 1 52530 53550 53040 +59 36 1 53295 53550 53295 +60 36 1 54060 53550 53805 +61 36 1 54570 53550 54315 +62 36 1 55335 53550 54570 +63 36 1 56100 53550 55080 +0 37 1 8670 54570 27030 +1 37 1 9435 54570 27540 +2 37 1 10200 54570 27795 +3 37 1 10710 54570 28305 +4 37 1 11475 54570 28560 +5 37 1 12240 54570 29070 +6 37 1 13005 54570 29580 +7 37 1 13515 54570 29835 +8 37 1 15045 54570 30855 +9 37 1 15555 54570 31110 +10 37 1 16320 54570 31620 +11 37 1 17085 54570 31875 +12 37 1 17595 54570 32385 +13 37 1 18360 54570 32895 +14 37 1 19125 54570 33150 +15 37 1 19635 54570 33660 +16 37 1 20400 54570 33915 +17 37 1 21675 54570 34935 +18 37 1 22440 54570 35190 +19 37 1 23205 54570 35700 +20 37 1 23715 54570 36210 +21 37 1 24480 54570 36465 +22 37 1 25245 54570 36975 +23 37 1 26010 54570 37230 +24 37 1 26520 54570 37740 +25 37 1 27285 54570 38250 +26 37 1 28050 54570 38505 +27 37 1 29325 54570 39270 +28 37 1 30090 54570 39780 +29 37 1 30600 54570 40290 +30 37 1 31365 54570 40545 +31 37 1 32130 54570 41055 +32 37 1 32640 54570 41565 +33 37 1 33405 54570 41820 +34 37 1 34170 54570 42330 +35 37 1 34680 54570 42585 +36 37 1 35445 54570 43095 +37 37 1 36210 54570 43605 +38 37 1 37485 54570 44370 +39 37 1 38250 54570 44625 +40 37 1 39015 54570 45135 +41 37 1 39525 54570 45645 +42 37 1 40290 54570 45900 +43 37 1 41055 54570 46410 +44 37 1 41565 54570 46665 +45 37 1 42330 54570 47175 +46 37 1 43095 54570 47685 +47 37 1 43605 54570 47940 +48 37 1 45135 54570 48960 +49 37 1 45645 54570 49215 +50 37 1 46410 54570 49725 +51 37 1 47175 54570 49980 +52 37 1 47685 54570 50490 +53 37 1 48450 54570 51000 +54 37 1 49215 54570 51255 +55 37 1 49725 54570 51765 +56 37 1 50490 54570 52020 +57 37 1 52020 54570 53040 +58 37 1 52530 54570 53295 +59 37 1 53295 54570 53805 +60 37 1 54060 54570 54315 +61 37 1 54570 54570 54570 +62 37 1 55335 54570 55080 +63 37 1 56100 54570 55335 +0 38 1 8670 56610 27795 +1 38 1 9435 56610 28305 +2 38 1 10200 56610 28560 +3 38 1 10710 56610 29070 +4 38 1 11475 55590 29070 +5 38 1 12240 55590 29580 +6 38 1 13005 55590 29835 +7 38 1 13515 55590 30345 +8 38 1 15045 55590 31110 +9 38 1 15555 55590 31620 +10 38 1 16320 55590 31875 +11 38 1 17085 55590 32385 +12 38 1 17595 55590 32895 +13 38 1 18360 55590 33150 +14 38 1 19125 55590 33660 +15 38 1 19635 55590 33915 +16 38 1 20400 55590 34425 +17 38 1 21675 55590 35190 +18 38 1 22440 55590 35700 +19 38 1 23205 55590 36210 +20 38 1 23715 55590 36465 +21 38 1 24480 55590 36975 +22 38 1 25245 55590 37230 +23 38 1 26010 55590 37740 +24 38 1 26520 55590 38250 +25 38 1 27285 55590 38505 +26 38 1 28050 55590 39015 +27 38 1 29325 55590 39780 +28 38 1 30090 55590 40290 +29 38 1 30600 55590 40545 +30 38 1 31365 55590 41055 +31 38 1 32130 55590 41565 +32 38 1 32640 55590 41820 +33 38 1 33405 55590 42330 +34 38 1 34170 55590 42585 +35 38 1 34680 55590 43095 +36 38 1 35445 55590 43605 +37 38 1 36210 55590 43860 +38 38 1 37485 55590 44625 +39 38 1 38250 55590 45135 +40 38 1 39015 55590 45645 +41 38 1 39525 55590 45900 +42 38 1 40290 55590 46410 +43 38 1 41055 55590 46665 +44 38 1 41565 55590 47175 +45 38 1 42330 55590 47685 +46 38 1 43095 55590 47940 +47 38 1 43605 55590 48450 +48 38 1 45135 55590 49215 +49 38 1 45645 55590 49725 +50 38 1 46410 55590 49980 +51 38 1 47175 55590 50490 +52 38 1 47685 55590 51000 +53 38 1 48450 55590 51255 +54 38 1 49215 55590 51765 +55 38 1 49725 55590 52020 +56 38 1 50490 55590 52530 +57 38 1 52020 55590 53295 +58 38 1 52530 55590 53805 +59 38 1 53295 55590 54315 +60 38 1 54060 55590 54570 +61 38 1 54570 56610 55335 +62 38 1 55335 56610 55845 +63 38 1 56100 56610 56355 +0 39 1 8670 57630 28305 +1 39 1 9435 57630 28560 +2 39 1 10200 57630 29070 +3 39 1 10710 57630 29580 +4 39 1 11475 57630 29835 +5 39 1 12240 57630 30345 +6 39 1 13005 57630 30855 +7 39 1 13515 57630 31110 +8 39 1 15045 57630 31875 +9 39 1 15555 57630 32385 +10 39 1 16320 57630 32895 +11 39 1 17085 57630 33150 +12 39 1 17595 57630 33660 +13 39 1 18360 57630 33915 +14 39 1 19125 56610 33915 +15 39 1 19635 56610 34425 +16 39 1 20400 56610 34935 +17 39 1 21675 56610 35700 +18 39 1 22440 56610 36210 +19 39 1 23205 56610 36465 +20 39 1 23715 56610 36975 +21 39 1 24480 56610 37230 +22 39 1 25245 56610 37740 +23 39 1 26010 56610 38250 +24 39 1 26520 56610 38505 +25 39 1 27285 56610 39015 +26 39 1 28050 56610 39270 +27 39 1 29325 56610 40290 +28 39 1 30090 56610 40545 +29 39 1 30600 56610 41055 +30 39 1 31365 56610 41565 +31 39 1 32130 56610 41820 +32 39 1 32640 56610 42330 +33 39 1 33405 56610 42585 +34 39 1 34170 56610 43095 +35 39 1 34680 56610 43605 +36 39 1 35445 56610 43860 +37 39 1 36210 56610 44370 +38 39 1 37485 56610 45135 +39 39 1 38250 56610 45645 +40 39 1 39015 56610 45900 +41 39 1 39525 56610 46410 +42 39 1 40290 56610 46665 +43 39 1 41055 56610 47175 +44 39 1 41565 56610 47685 +45 39 1 42330 56610 47940 +46 39 1 43095 56610 48450 +47 39 1 43605 56610 48960 +48 39 1 45135 56610 49725 +49 39 1 45645 56610 49980 +50 39 1 46410 56610 50490 +51 39 1 47175 57630 51255 +52 39 1 47685 57630 51765 +53 39 1 48450 57630 52020 +54 39 1 49215 57630 52530 +55 39 1 49725 57630 53040 +56 39 1 50490 57630 53295 +57 39 1 52020 57630 54315 +58 39 1 52530 57630 54570 +59 39 1 53295 57630 55080 +60 39 1 54060 57630 55335 +61 39 1 54570 57630 55845 +62 39 1 55335 57630 56355 +63 39 1 56100 57630 56610 +0 40 1 8670 58650 28560 +1 40 1 9435 58650 29070 +2 40 1 10200 58650 29580 +3 40 1 10710 58650 29835 +4 40 1 11475 58650 30345 +5 40 1 12240 58650 30855 +6 40 1 13005 58650 31110 +7 40 1 13515 58650 31620 +8 40 1 15045 58650 32385 +9 40 1 15555 58650 32895 +10 40 1 16320 58650 33150 +11 40 1 17085 58650 33660 +12 40 1 17595 58650 33915 +13 40 1 18360 58650 34425 +14 40 1 19125 58650 34935 +15 40 1 19635 58650 35190 +16 40 1 20400 58650 35700 +17 40 1 21675 58650 36465 +18 40 1 22440 58650 36975 +19 40 1 23205 58650 37230 +20 40 1 23715 58650 37740 +21 40 1 24480 58650 38250 +22 40 1 25245 58650 38505 +23 40 1 26010 58650 39015 +24 40 1 26520 58650 39270 +25 40 1 27285 58650 39780 +26 40 1 28050 58650 40290 +27 40 1 29325 58650 41055 +28 40 1 30090 58650 41565 +29 40 1 30600 58650 41820 +30 40 1 31365 58650 42330 +31 40 1 32130 58650 42585 +32 40 1 32640 58650 43095 +33 40 1 33405 58650 43605 +34 40 1 34170 58650 43860 +35 40 1 34680 58650 44370 +36 40 1 35445 58650 44625 +37 40 1 36210 58650 45135 +38 40 1 37485 58650 45900 +39 40 1 38250 58650 46410 +40 40 1 39015 58650 46665 +41 40 1 39525 58650 47175 +42 40 1 40290 58650 47685 +43 40 1 41055 58650 47940 +44 40 1 41565 58650 48450 +45 40 1 42330 58650 48960 +46 40 1 43095 58650 49215 +47 40 1 43605 58650 49725 +48 40 1 45135 58650 50490 +49 40 1 45645 58650 51000 +50 40 1 46410 58650 51255 +51 40 1 47175 58650 51765 +52 40 1 47685 58650 52020 +53 40 1 48450 58650 52530 +54 40 1 49215 58650 53040 +55 40 1 49725 58650 53295 +56 40 1 50490 58650 53805 +57 40 1 52020 58650 54570 +58 40 1 52530 58650 55080 +59 40 1 53295 58650 55335 +60 40 1 54060 58650 55845 +61 40 1 54570 58650 56355 +62 40 1 55335 58650 56610 +63 40 1 56100 58650 57120 +0 41 1 8670 59670 29070 +1 41 1 9435 59670 29580 +2 41 1 10200 59670 29835 +3 41 1 10710 59670 30345 +4 41 1 11475 59670 30855 +5 41 1 12240 59670 31110 +6 41 1 13005 59670 31620 +7 41 1 13515 59670 31875 +8 41 1 15045 59670 32895 +9 41 1 15555 59670 33150 +10 41 1 16320 59670 33660 +11 41 1 17085 59670 33915 +12 41 1 17595 59670 34425 +13 41 1 18360 59670 34935 +14 41 1 19125 59670 35190 +15 41 1 19635 59670 35700 +16 41 1 20400 59670 36210 +17 41 1 21675 59670 36975 +18 41 1 22440 59670 37230 +19 41 1 23205 59670 37740 +20 41 1 23715 59670 38250 +21 41 1 24480 59670 38505 +22 41 1 25245 59670 39015 +23 41 1 26010 59670 39270 +24 41 1 26520 59670 39780 +25 41 1 27285 59670 40290 +26 41 1 28050 59670 40545 +27 41 1 29325 59670 41565 +28 41 1 30090 59670 41820 +29 41 1 30600 59670 42330 +30 41 1 31365 59670 42585 +31 41 1 32130 59670 43095 +32 41 1 32640 59670 43605 +33 41 1 33405 59670 43860 +34 41 1 34170 59670 44370 +35 41 1 34680 59670 44625 +36 41 1 35445 59670 45135 +37 41 1 36210 59670 45645 +38 41 1 37485 59670 46410 +39 41 1 38250 59670 46665 +40 41 1 39015 59670 47175 +41 41 1 39525 59670 47685 +42 41 1 40290 59670 47940 +43 41 1 41055 59670 48450 +44 41 1 41565 59670 48960 +45 41 1 42330 59670 49215 +46 41 1 43095 59670 49725 +47 41 1 43605 59670 49980 +48 41 1 45135 59670 51000 +49 41 1 45645 59670 51255 +50 41 1 46410 59670 51765 +51 41 1 47175 59670 52020 +52 41 1 47685 59670 52530 +53 41 1 48450 59670 53040 +54 41 1 49215 59670 53295 +55 41 1 49725 59670 53805 +56 41 1 50490 59670 54315 +57 41 1 52020 59670 55080 +58 41 1 52530 59670 55335 +59 41 1 53295 59670 55845 +60 41 1 54060 59670 56355 +61 41 1 54570 59670 56610 +62 41 1 55335 59670 57120 +63 41 1 56100 59670 57375 +0 42 1 8670 60690 29580 +1 42 1 9435 60690 29835 +2 42 1 10200 60690 30345 +3 42 1 10710 60690 30855 +4 42 1 11475 60690 31110 +5 42 1 12240 60690 31620 +6 42 1 13005 60690 31875 +7 42 1 13515 60690 32385 +8 42 1 15045 60690 33150 +9 42 1 15555 60690 33660 +10 42 1 16320 60690 33915 +11 42 1 17085 60690 34425 +12 42 1 17595 60690 34935 +13 42 1 18360 60690 35190 +14 42 1 19125 60690 35700 +15 42 1 19635 60690 36210 +16 42 1 20400 60690 36465 +17 42 1 21675 60690 37230 +18 42 1 22440 60690 37740 +19 42 1 23205 60690 38250 +20 42 1 23715 60690 38505 +21 42 1 24480 60690 39015 +22 42 1 25245 60690 39270 +23 42 1 26010 60690 39780 +24 42 1 26520 60690 40290 +25 42 1 27285 60690 40545 +26 42 1 28050 60690 41055 +27 42 1 29325 60690 41820 +28 42 1 30090 60690 42330 +29 42 1 30600 60690 42585 +30 42 1 31365 60690 43095 +31 42 1 32130 60690 43605 +32 42 1 32640 60690 43860 +33 42 1 33405 60690 44370 +34 42 1 34170 60690 44625 +35 42 1 34680 60690 45135 +36 42 1 35445 60690 45645 +37 42 1 36210 60690 45900 +38 42 1 37485 60690 46665 +39 42 1 38250 60690 47175 +40 42 1 39015 60690 47685 +41 42 1 39525 60690 47940 +42 42 1 40290 60690 48450 +43 42 1 41055 60690 48960 +44 42 1 41565 60690 49215 +45 42 1 42330 60690 49725 +46 42 1 43095 60690 49980 +47 42 1 43605 60690 50490 +48 42 1 45135 60690 51255 +49 42 1 45645 60690 51765 +50 42 1 46410 60690 52020 +51 42 1 47175 60690 52530 +52 42 1 47685 60690 53040 +53 42 1 48450 60690 53295 +54 42 1 49215 60690 53805 +55 42 1 49725 60690 54315 +56 42 1 50490 60690 54570 +57 42 1 52020 60690 55335 +58 42 1 52530 60690 55845 +59 42 1 53295 60690 56355 +60 42 1 54060 60690 56610 +61 42 1 54570 60690 57120 +62 42 1 55335 60690 57375 +63 42 1 56100 60690 57885 +0 43 1 8670 61710 29835 +1 43 1 9435 61710 30345 +2 43 1 10200 61710 30855 +3 43 1 10710 61710 31110 +4 43 1 11475 61710 31620 +5 43 1 12240 61710 31875 +6 43 1 13005 61710 32385 +7 43 1 13515 61710 32895 +8 43 1 15045 61710 33660 +9 43 1 15555 61710 33915 +10 43 1 16320 61710 34425 +11 43 1 17085 61710 34935 +12 43 1 17595 61710 35190 +13 43 1 18360 61710 35700 +14 43 1 19125 61710 36210 +15 43 1 19635 61710 36465 +16 43 1 20400 61710 36975 +17 43 1 21675 61710 37740 +18 43 1 22440 61710 38250 +19 43 1 23205 61710 38505 +20 43 1 23715 61710 39015 +21 43 1 24480 61710 39270 +22 43 1 25245 61710 39780 +23 43 1 26010 61710 40290 +24 43 1 26520 61710 40545 +25 43 1 27285 61710 41055 +26 43 1 28050 61710 41565 +27 43 1 29325 61710 42330 +28 43 1 30090 61710 42585 +29 43 1 30600 61710 43095 +30 43 1 31365 61710 43605 +31 43 1 32130 61710 43860 +32 43 1 32640 61710 44370 +33 43 1 33405 61710 44625 +34 43 1 34170 61710 45135 +35 43 1 34680 61710 45645 +36 43 1 35445 61710 45900 +37 43 1 36210 61710 46410 +38 43 1 37485 61710 47175 +39 43 1 38250 61710 47685 +40 43 1 39015 61710 47940 +41 43 1 39525 61710 48450 +42 43 1 40290 61710 48960 +43 43 1 41055 61710 49215 +44 43 1 41565 61710 49725 +45 43 1 42330 61710 49980 +46 43 1 43095 61710 50490 +47 43 1 43605 61710 51000 +48 43 1 45135 61710 51765 +49 43 1 45645 61710 52020 +50 43 1 46410 61710 52530 +51 43 1 47175 61710 53040 +52 43 1 47685 61710 53295 +53 43 1 48450 61710 53805 +54 43 1 49215 61710 54315 +55 43 1 49725 61710 54570 +56 43 1 50490 61710 55080 +57 43 1 52020 61710 55845 +58 43 1 52530 61710 56355 +59 43 1 53295 61710 56610 +60 43 1 54060 61710 57120 +61 43 1 54570 61710 57375 +62 43 1 55335 61710 57885 +63 43 1 56100 61710 58395 +0 44 1 8670 62730 30345 +1 44 1 9435 62730 30855 +2 44 1 10200 62730 31110 +3 44 1 10710 62730 31620 +4 44 1 11475 62730 31875 +5 44 1 12240 62730 32385 +6 44 1 13005 62730 32895 +7 44 1 13515 62730 33150 +8 44 1 15045 62730 33915 +9 44 1 15555 62730 34425 +10 44 1 16320 62730 34935 +11 44 1 17085 62730 35190 +12 44 1 17595 62730 35700 +13 44 1 18360 62730 36210 +14 44 1 19125 62730 36465 +15 44 1 19635 62730 36975 +16 44 1 20400 62730 37230 +17 44 1 21675 62730 38250 +18 44 1 22440 62730 38505 +19 44 1 23205 62730 39015 +20 44 1 23715 62730 39270 +21 44 1 24480 62730 39780 +22 44 1 25245 62730 40290 +23 44 1 26010 62730 40545 +24 44 1 26520 62730 41055 +25 44 1 27285 62730 41565 +26 44 1 28050 62730 41820 +27 44 1 29325 62730 42585 +28 44 1 30090 62730 43095 +29 44 1 30600 62730 43605 +30 44 1 31365 62730 43860 +31 44 1 32130 62730 44370 +32 44 1 32640 62730 44625 +33 44 1 33405 62730 45135 +34 44 1 34170 62730 45645 +35 44 1 34680 62730 45900 +36 44 1 35445 62730 46410 +37 44 1 36210 62730 46665 +38 44 1 37485 62730 47685 +39 44 1 38250 62730 47940 +40 44 1 39015 62730 48450 +41 44 1 39525 62730 48960 +42 44 1 40290 62730 49215 +43 44 1 41055 62730 49725 +44 44 1 41565 62730 49980 +45 44 1 42330 62730 50490 +46 44 1 43095 62730 51000 +47 44 1 43605 62730 51255 +48 44 1 45135 62730 52020 +49 44 1 45645 62730 52530 +50 44 1 46410 62730 53040 +51 44 1 47175 62730 53295 +52 44 1 47685 62730 53805 +53 44 1 48450 62730 54315 +54 44 1 49215 62730 54570 +55 44 1 49725 62730 55080 +56 44 1 50490 62730 55335 +57 44 1 52020 62730 56355 +58 44 1 52530 62730 56610 +59 44 1 53295 62730 57120 +60 44 1 54060 62730 57375 +61 44 1 54570 62730 57885 +62 44 1 55335 62730 58395 +63 44 1 56100 62730 58650 +0 45 1 8670 63750 30855 +1 45 1 9435 63750 31110 +2 45 1 10200 63750 31620 +3 45 1 10710 63750 31875 +4 45 1 11475 63750 32385 +5 45 1 12240 63750 32895 +6 45 1 13005 63750 33150 +7 45 1 13515 63750 33660 +8 45 1 15045 63750 34425 +9 45 1 15555 63750 34935 +10 45 1 16320 63750 35190 +11 45 1 17085 63750 35700 +12 45 1 17595 63750 36210 +13 45 1 18360 63750 36465 +14 45 1 19125 63750 36975 +15 45 1 19635 63750 37230 +16 45 1 20400 63750 37740 +17 45 1 21675 63750 38505 +18 45 1 22440 63750 39015 +19 45 1 23205 63750 39270 +20 45 1 23715 63750 39780 +21 45 1 24480 63750 40290 +22 45 1 25245 63750 40545 +23 45 1 26010 63750 41055 +24 45 1 26520 63750 41565 +25 45 1 27285 63750 41820 +26 45 1 28050 63750 42330 +27 45 1 29325 63750 43095 +28 45 1 30090 63750 43605 +29 45 1 30600 63750 43860 +30 45 1 31365 63750 44370 +31 45 1 32130 63750 44625 +32 45 1 32640 63750 45135 +33 45 1 33405 63750 45645 +34 45 1 34170 63750 45900 +35 45 1 34680 63750 46410 +36 45 1 35445 63750 46665 +37 45 1 36210 63750 47175 +38 45 1 37485 63750 47940 +39 45 1 38250 63750 48450 +40 45 1 39015 63750 48960 +41 45 1 39525 63750 49215 +42 45 1 40290 63750 49725 +43 45 1 41055 63750 49980 +44 45 1 41565 63750 50490 +45 45 1 42330 63750 51000 +46 45 1 43095 63750 51255 +47 45 1 43605 63750 51765 +48 45 1 45135 63750 52530 +49 45 1 45645 63750 53040 +50 45 1 46410 63750 53295 +51 45 1 47175 63750 53805 +52 45 1 47685 63750 54315 +53 45 1 48450 63750 54570 +54 45 1 49215 63750 55080 +55 45 1 49725 63750 55335 +56 45 1 50490 63750 55845 +57 45 1 52020 63750 56610 +58 45 1 52530 63750 57120 +59 45 1 53295 63750 57375 +60 45 1 54060 63750 57885 +61 45 1 54570 63750 58395 +62 45 1 55335 63750 58650 +63 45 1 56100 63750 59160 +0 46 1 8670 65025 31110 +1 46 1 9435 65025 31620 +2 46 1 10200 65025 31875 +3 46 1 10710 65025 32385 +4 46 1 11475 65025 32895 +5 46 1 12240 65025 33150 +6 46 1 13005 65025 33660 +7 46 1 13515 65025 33915 +8 46 1 15045 65025 34935 +9 46 1 15555 65025 35190 +10 46 1 16320 65025 35700 +11 46 1 17085 65025 36210 +12 46 1 17595 65025 36465 +13 46 1 18360 65025 36975 +14 46 1 19125 65025 37230 +15 46 1 19635 65025 37740 +16 46 1 20400 65025 38250 +17 46 1 21675 65025 39015 +18 46 1 22440 65025 39270 +19 46 1 23205 65025 39780 +20 46 1 23715 65025 40290 +21 46 1 24480 65025 40545 +22 46 1 25245 65025 41055 +23 46 1 26010 65025 41565 +24 46 1 26520 65025 41820 +25 46 1 27285 65025 42330 +26 46 1 28050 65025 42585 +27 46 1 29325 65025 43605 +28 46 1 30090 65025 43860 +29 46 1 30600 65025 44370 +30 46 1 31365 65025 44625 +31 46 1 32130 65025 45135 +32 46 1 32640 65025 45645 +33 46 1 33405 65025 45900 +34 46 1 34170 65025 46410 +35 46 1 34680 65025 46665 +36 46 1 35445 65025 47175 +37 46 1 36210 65025 47685 +38 46 1 37485 65025 48450 +39 46 1 38250 65025 48960 +40 46 1 39015 65025 49215 +41 46 1 39525 65025 49725 +42 46 1 40290 65025 49980 +43 46 1 41055 65025 50490 +44 46 1 41565 65025 51000 +45 46 1 42330 65025 51255 +46 46 1 43095 65025 51765 +47 46 1 43605 65025 52020 +48 46 1 45135 65025 53040 +49 46 1 45645 65025 53295 +50 46 1 46410 65025 53805 +51 46 1 47175 65025 54315 +52 46 1 47685 65025 54570 +53 46 1 48450 65025 55080 +54 46 1 49215 65025 55335 +55 46 1 49725 65025 55845 +56 46 1 50490 65025 56355 +57 46 1 52020 65025 57120 +58 46 1 52530 65025 57375 +59 46 1 53295 65025 57885 +60 46 1 54060 65025 58395 +61 46 1 54570 65025 58650 +62 46 1 55335 65025 59160 +63 46 1 56100 65025 59670 +0 47 0 0 0 0 +1 47 0 0 0 0 +2 47 0 0 0 0 +3 47 0 0 0 0 +4 47 0 0 0 0 +5 47 0 0 0 0 +6 47 0 0 0 0 +7 47 0 0 0 0 +8 47 0 0 0 0 +9 47 0 0 0 0 +10 47 0 0 0 0 +11 47 0 0 0 0 +12 47 0 0 0 0 +13 47 0 0 0 0 +14 47 0 0 0 0 +15 47 0 0 0 0 +16 47 0 0 0 0 +17 47 0 0 0 0 +18 47 0 0 0 0 +19 47 0 0 0 0 +20 47 0 0 0 0 +21 47 0 0 0 0 +22 47 0 0 0 0 +23 47 0 0 0 0 +24 47 0 0 0 0 +25 47 0 0 0 0 +26 47 0 0 0 0 +27 47 0 0 0 0 +28 47 0 0 0 0 +29 47 0 0 0 0 +30 47 0 0 0 0 +31 47 0 0 0 0 +32 47 0 0 0 0 +33 47 0 0 0 0 +34 47 0 0 0 0 +35 47 0 0 0 0 +36 47 0 0 0 0 +37 47 0 0 0 0 +38 47 0 0 0 0 +39 47 0 0 0 0 +40 47 0 0 0 0 +41 47 0 0 0 0 +42 47 0 0 0 0 +43 47 0 0 0 0 +44 47 0 0 0 0 +45 47 0 0 0 0 +46 47 0 0 0 0 +47 47 0 0 0 0 +48 47 0 0 0 0 +49 47 0 0 0 0 +50 47 0 0 0 0 +51 47 0 0 0 0 +52 47 0 0 0 0 +53 47 0 0 0 0 +54 47 0 0 0 0 +55 47 0 0 0 0 +56 47 0 0 0 0 +57 47 0 0 0 0 +58 47 0 0 0 0 +59 47 0 0 0 0 +60 47 0 0 0 0 +61 47 0 0 0 0 +62 47 0 0 0 0 +63 47 0 0 0 0 diff --git a/tests/golden/test.jpg b/tests/golden/test.jpg Binary files differnew file mode 100644 index 0000000..5ea9549 --- /dev/null +++ b/tests/golden/test.jpg diff --git a/tests/harness/build/cyl_funcs.cxx b/tests/harness/build/cyl_funcs.cxx new file mode 100644 index 0000000..5143ba5 --- /dev/null +++ b/tests/harness/build/cyl_funcs.cxx @@ -0,0 +1,10 @@ +double cyl_mac_x ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return (cos(c_tilt)*(m_view-c_view)-(sin(c_tilt)*(_2-_1))/(_1*_2+1))*scale ; } +double cyl_mac_y ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return ((-sin(c_tilt)*(m_view-c_view))-(cos(c_tilt)*(_2-_1))/(_1*_2+1))*scale ; } +double cyl_mac_x_dc_view ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { return -cos(c_tilt)*scale ; } +double cyl_mac_x_dc_nick ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 ; _1 = pow(sec(c_nick),2) ; _2 = sin(c_tilt) ; _3 = tan(m_nick) ; _4 = tan(c_nick) ; _5 = _4*_3+1 ; return ((_1*_2*_3*(_3-_4))/pow(_5,2)+(_1*_2)/_5)*scale ; } +double cyl_mac_x_dc_tilt ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return ((-sin(c_tilt)*(m_view-c_view))-(cos(c_tilt)*(_2-_1))/(_1*_2+1))*scale ; } +double cyl_mac_x_dscale ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return cos(c_tilt)*(m_view-c_view)-(sin(c_tilt)*(_2-_1))/(_1*_2+1) ; } +double cyl_mac_y_dc_view ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { return sin(c_tilt)*scale ; } +double cyl_mac_y_dc_nick ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 ; _1 = pow(sec(c_nick),2) ; _2 = cos(c_tilt) ; _3 = tan(m_nick) ; _4 = tan(c_nick) ; _5 = _4*_3+1 ; return ((_1*_2*_3*(_3-_4))/pow(_5,2)+(_1*_2)/_5)*scale ; } +double cyl_mac_y_dc_tilt ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return ((sin(c_tilt)*(_2-_1))/(_1*_2+1)-cos(c_tilt)*(m_view-c_view))*scale ; } +double cyl_mac_y_dscale ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 ; _1 = tan(c_nick) ; _2 = tan(m_nick) ; return (-sin(c_tilt)*(m_view-c_view))-(cos(c_tilt)*(_2-_1))/(_1*_2+1) ; } diff --git a/tests/harness/build/exportdump b/tests/harness/build/exportdump Binary files differnew file mode 100755 index 0000000..e732af7 --- /dev/null +++ b/tests/harness/build/exportdump diff --git a/tests/harness/build/fitdump b/tests/harness/build/fitdump Binary files differnew file mode 100755 index 0000000..37383c8 --- /dev/null +++ b/tests/harness/build/fitdump diff --git a/tests/harness/build/funcsdump b/tests/harness/build/funcsdump Binary files differnew file mode 100755 index 0000000..37efb59 --- /dev/null +++ b/tests/harness/build/funcsdump diff --git a/tests/harness/build/rect_funcs.cxx b/tests/harness/build/rect_funcs.cxx new file mode 100644 index 0000000..95c5cc2 --- /dev/null +++ b/tests/harness/build/rect_funcs.cxx @@ -0,0 +1,16 @@ +double rect_mac_x ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2)+pow(_13*_11*_12-_1*_5*_14*_11,2) ; return scale*_15*(k0*sqrt(_16)+k1*_16+1) ; } +double rect_mac_y ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = pow(x0+_1*_5*_14*_11+_13*_11*_12,2)+pow(_15,2) ; return _15*scale*(k0*sqrt(_16)+k1*_16+1) ; } +double rect_mac_x_dc_view ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 , _20 , _21 , _22 , _23 , _24 , _25 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = cos(c_tilt) ; _15 = _6*_9-_8*_7 ; _16 = x0+_14*_5*_15*_12+_1*_12*_13 ; _17 = _14*_12*_13-_1*_5*_15*_12 ; _18 = 1/pow(_11,2) ; _19 = pow(_5,2) ; _20 = pow(_15,2) ; _21 = (-_8*_9)-_6*_7 ; _22 = _14*_5*_21*_12+_2*_1*_5*_15*_12-_4*_14*_19*_20*_18-_4*_1*_5*_15*_18*_13 ; _23 = 2*_22*_16+2*_17*((-_1*_5*_21*_12)+_2*_14*_5*_15*_12+_4*_1*_19*_20*_18-_4*_14*_5*_15*_18*_13) ; _24 = pow(_17,2)+pow(_16,2) ; _25 = sqrt(_24) ; return _22*scale*(k0*_25+k1*_24+1)+scale*_16*((k0*_23)/(2*_25)+k1*_23) ; } +double rect_mac_x_dc_nick ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 , _20 , _21 , _22 , _23 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = cos(c_tilt) ; _15 = _6*_9-_8*_7 ; _16 = x0+_14*_5*_15*_12+_1*_12*_13 ; _17 = 1/pow(_11,2) ; _18 = _4*_3-_2*_5*_10 ; _19 = _14*_12*_13-_1*_5*_15*_12 ; _20 = (-_1*_17*_18*_13)-_14*_5*_15*_17*_18+_1 ; _21 = 2*_20*_16+2*((-_14*_17*_18*_13)+_1*_5*_15*_17*_18+_14)*_19 ; _22 = pow(_19,2)+pow(_16,2) ; _23 = sqrt(_22) ; return _20*scale*(k0*_23+k1*_22+1)+scale*_16*((k0*_21)/(2*_23)+k1*_21) ; } +double rect_mac_x_dc_tilt ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = _13*_11*_12-_1*_5*_14*_11 ; _17 = 2*_16*_15+2*((-_13*_5*_14*_11)-_1*_11*_12)*_16 ; _18 = pow(_16,2)+pow(_15,2) ; _19 = sqrt(_18) ; return _16*scale*(k0*_19+k1*_18+1)+scale*_15*((k0*_17)/(2*_19)+k1*_17) ; } +double rect_mac_x_dscale ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2)+pow(_13*_11*_12-_1*_5*_14*_11,2) ; return _15*(k0*sqrt(_16)+k1*_16+1) ; } +double rect_mac_x_dk0 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; return _15*sqrt(pow(_15,2)+pow(_13*_11*_12-_1*_5*_14*_11,2))*scale ; } +double rect_mac_x_dk1 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; return _15*(pow(_15,2)+pow(_13*_11*_12-_1*_5*_14*_11,2))*scale ; } +double rect_mac_x_dx0 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 ; _1 = sin(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = cos(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = x0+_13*_5*_14*_11+_1*_11*_12 ; _16 = pow(_15,2)+pow(_13*_11*_12-_1*_5*_14*_11,2) ; _17 = sqrt(_16) ; return scale*(k0*_17+k1*_16+1)+scale*_15*((k0*_15)/_17+2*k1*_15) ; } +double rect_mac_y_dc_view ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 , _20 , _21 , _22 , _23 , _24 , _25 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = sin(c_tilt) ; _15 = _6*_9-_8*_7 ; _16 = _1*_12*_13-_14*_5*_15*_12 ; _17 = 1/pow(_11,2) ; _18 = pow(_5,2) ; _19 = pow(_15,2) ; _20 = (-_8*_9)-_6*_7 ; _21 = (-_14*_5*_20*_12)+_2*_1*_5*_15*_12+_4*_14*_18*_19*_17-_4*_1*_5*_15*_17*_13 ; _22 = x0+_1*_5*_15*_12+_14*_12*_13 ; _23 = 2*(_1*_5*_20*_12+_2*_14*_5*_15*_12-_4*_1*_18*_19*_17-_4*_14*_5*_15*_17*_13)*_22+2*_16*_21 ; _24 = pow(_22,2)+pow(_16,2) ; _25 = sqrt(_24) ; return _21*scale*(k0*_25+k1*_24+1)+_16*scale*((k0*_23)/(2*_25)+k1*_23) ; } +double rect_mac_y_dc_nick ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 , _20 , _21 , _22 , _23 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = _4*_5*_10+_2*_3 ; _12 = 1/_11 ; _13 = _2*_5*_10-_4*_3 ; _14 = sin(c_tilt) ; _15 = _6*_9-_8*_7 ; _16 = _1*_12*_13-_14*_5*_15*_12 ; _17 = 1/pow(_11,2) ; _18 = _4*_3-_2*_5*_10 ; _19 = (-_1*_17*_18*_13)+_14*_5*_15*_17*_18+_1 ; _20 = x0+_1*_5*_15*_12+_14*_12*_13 ; _21 = 2*((-_14*_17*_18*_13)-_1*_5*_15*_17*_18+_14)*_20+2*_19*_16 ; _22 = pow(_20,2)+pow(_16,2) ; _23 = sqrt(_22) ; return _19*scale*(k0*_23+k1*_22+1)+_16*scale*((k0*_21)/(2*_23)+k1*_21) ; } +double rect_mac_y_dc_tilt ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 , _17 , _18 , _19 , _20 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = (-_1*_5*_14*_11)-_13*_11*_12 ; _17 = x0+_1*_5*_14*_11+_13*_11*_12 ; _18 = 2*_15*_17+2*_16*_15 ; _19 = pow(_17,2)+pow(_15,2) ; _20 = sqrt(_19) ; return _16*scale*(k0*_20+k1*_19+1)+_15*scale*((k0*_18)/(2*_20)+k1*_18) ; } +double rect_mac_y_dscale ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = pow(x0+_1*_5*_14*_11+_13*_11*_12,2)+pow(_15,2) ; return _15*(k0*sqrt(_16)+k1*_16+1) ; } +double rect_mac_y_dk0 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; return _15*scale*sqrt(pow(x0+_1*_5*_14*_11+_13*_11*_12,2)+pow(_15,2)) ; } +double rect_mac_y_dk1 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; return _15*scale*(pow(x0+_1*_5*_14*_11+_13*_11*_12,2)+pow(_15,2)) ; } +double rect_mac_y_dx0 ( double c_view, double c_nick, double c_tilt, double scale, double k0, double k1, double x0, double m_view, double m_nick ) { double _1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , _11 , _12 , _13 , _14 , _15 , _16 ; _1 = cos(c_tilt) ; _2 = sin(c_nick) ; _3 = sin(m_nick) ; _4 = cos(c_nick) ; _5 = cos(m_nick) ; _6 = cos(c_view) ; _7 = cos(m_view) ; _8 = sin(c_view) ; _9 = sin(m_view) ; _10 = _8*_9+_6*_7 ; _11 = 1/(_4*_5*_10+_2*_3) ; _12 = _2*_5*_10-_4*_3 ; _13 = sin(c_tilt) ; _14 = _6*_9-_8*_7 ; _15 = _1*_11*_12-_13*_5*_14*_11 ; _16 = x0+_1*_5*_14*_11+_13*_11*_12 ; return _15*((_16*k0)/sqrt(pow(_16,2)+pow(_15,2))+2*k1*_16)*scale ; } diff --git a/tests/harness/build/stitchdump b/tests/harness/build/stitchdump Binary files differnew file mode 100755 index 0000000..e63a628 --- /dev/null +++ b/tests/harness/build/stitchdump diff --git a/tests/harness/exportdump.cxx b/tests/harness/exportdump.cxx new file mode 100644 index 0000000..2c0e736 --- /dev/null +++ b/tests/harness/exportdump.cxx @@ -0,0 +1,77 @@ +// Golden dump: export-hills pipeline through the original C++ Panorama + +// ImageMetaData (mirrors GipfelWidget::load_image + export_hills, no FLTK). +// Usage: exportdump <img> <gipfel.dat> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <exiv2/exiv2.hpp> + +#include "Panorama.H" +#include "Hill.H" +#include "ImageMetaData.H" + +int main(int argc, char **argv) { + if (argc < 3) { + fprintf(stderr, "usage: exportdump <img> <gipfel.dat>\n"); + return 1; + } + + int w = 0, h = 0; + { + auto im = Exiv2::ImageFactory::open(argv[1]); + im->readMetadata(); + w = im->pixelWidth(); + h = im->pixelHeight(); + } + + ImageMetaData md; + md.load_image(argv[1]); + + Panorama pan; + pan.set_view_long(md.longitude()); + pan.set_view_lat(md.latitude()); + pan.set_view_height(md.height()); + pan.set_projection((ProjectionLSQ::Projection_t) md.projection_type()); + + int have = 1; + double v; + v = md.direction(); + if (std::isnan(v)) { pan.set_center_angle(0.0); have = 0; } else pan.set_center_angle(v); + v = md.nick(); + if (std::isnan(v)) { pan.set_nick_angle(0.0); have = 0; } else pan.set_nick_angle(v); + v = md.tilt(); + if (std::isnan(v)) { pan.set_tilt_angle(0.0); have = 0; } else pan.set_tilt_angle(v); + v = md.focal_length_35mm(); + if (std::isnan(v) || v == 0.0) { pan.set_scale(35.0 * (double)(w > h ? w : h) / 35.0); have = 0; } + else pan.set_scale(v * (double)(w > h ? w : h) / 35.0); + + md.distortion_params(&pan.parms.k0, &pan.parms.k1, &pan.parms.x0); + if (std::isnan(pan.parms.k0)) pan.parms.k0 = 0.0; + if (std::isnan(pan.parms.k1)) pan.parms.k1 = 0.0; + if (std::isnan(pan.parms.x0)) pan.parms.x0 = 0.0; + + if (pan.load_data(argv[2])) return 1; + pan.set_height_dist_ratio(0.07); + + if (!have) { + fprintf(stderr, "No gipfel info available.\n"); + return 0; + } + + printf("#\n# name\theight\tx\ty\tdistance\tflags\n#\n"); + Hills *mnts = pan.get_visible_mountains(); + for (int i = 0; i < mnts->get_num(); i++) { + Hill *m = mnts->get(i); + int _x = (int) rint(m->x) + w / 2; + int _y = (int) rint(m->y) + h / 2; + + if (m->flags & Hill::DUPLIC || m->flags & Hill::HIDDEN) + continue; + if (_x < 0 || _x > w || _y < 0 || _y > h) + continue; + + printf("%s\t%d\t%d\t%d\t%d\n", m->name, (int) rint(m->height), _x, _y, + (int) rint(pan.get_real_distance(m))); + } + return 0; +} diff --git a/tests/harness/fitdump.cxx b/tests/harness/fitdump.cxx new file mode 100644 index 0000000..8ef392c --- /dev/null +++ b/tests/harness/fitdump.cxx @@ -0,0 +1,83 @@ +// Golden dump: full fit through the original C++ Panorama + GSL solver. +// Usage: fitdump <gipfel.dat> <projection 0|1> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> + +#include "Panorama.H" +#include "Hill.H" +#include "ProjectionRectilinear.H" +#include "ProjectionCylindrical.H" + +int main(int argc, char **argv) { + if (argc < 3) { + fprintf(stderr, "usage: fitdump <gipfel.dat> <projection>\n"); + return 1; + } + + Panorama pan; + if (pan.load_data(argv[1])) { + fprintf(stderr, "load failed\n"); + return 1; + } + pan.set_projection((ProjectionLSQ::Projection_t) atoi(argv[2])); + + pan.set_view_lat(47.36); + pan.set_view_long(11.15); + pan.set_view_height(2100.0); + + // ground truth + pan.set_center_angle(20.0); + pan.set_nick_angle(0.0); + pan.set_tilt_angle(1.0); + pan.set_scale(3000.0); + + // observed positions = projection of first 6 visible hills (snapshotted + // as copies so the perturbation below does not overwrite them) + Hills *vis = pan.get_visible_mountains(); + Hills known; + int n = vis->get_num() < 6 ? vis->get_num() : 6; + for (int i = 0; i < n; i++) { + Hill *c = new Hill(*vis->get(i)); + // NOTE: Hill's copy ctor in the original does not copy a_nick + // (uninitialized garbage -> LSQR stagnates). The real app always + // fits hills living in the main list where a_nick is set, so set it + // here to reproduce the real-app behavior. + c->a_nick = vis->get(i)->a_nick; + known.add(c); + } + + // perturb the initial guess + pan.set_center_angle(pan.get_center_angle() + 0.5); + pan.set_nick_angle(0.3); + pan.set_tilt_angle(0.5); + pan.set_scale(2800.0); + + int ret = pan.comp_params(&known); + + ViewParams *p = &pan.parms; + printf("ret %d\n", ret); + printf("a_center %.17g\n", p->a_center); + printf("a_nick %.17g\n", p->a_nick); + printf("a_tilt %.17g\n", p->a_tilt); + printf("scale %.17g\n", p->scale); + printf("k0 %.17g\n", p->k0); + printf("k1 %.17g\n", p->k1); + printf("x0 %.17g\n", p->x0); + + // final image coordinates of the known hills (bypass visibility check) + for (int i = 0; i < known.get_num(); i++) { + Hill *m = known.get(i); + double x = NAN, y = NAN; + if (atoi(argv[2]) == 1) { + ProjectionCylindrical pc; + pc.get_coordinates(m->alph, m->a_nick, &pan.parms, &x, &y); + } else { + ProjectionRectilinear pr; + pr.get_coordinates(m->alph, m->a_nick, &pan.parms, &x, &y); + } + printf("hill %d %.17g %.17g\n", i, x, y); + } + + return 0; +} diff --git a/tests/harness/funcsdump.cxx b/tests/harness/funcsdump.cxx new file mode 100644 index 0000000..b416f2c --- /dev/null +++ b/tests/harness/funcsdump.cxx @@ -0,0 +1,60 @@ +// Golden dump of the maxima-generated projection functions (C reference). +// The `#define` trick turns the generated class methods into free functions. +#include <stdio.h> +#include <math.h> + +static double sec(double a) { return 1.0 / cos(a); } + +// gen_golden.sh copies the generated files with the class scope replaced: +// ProjectionRectilinear::rect_mac_x -> rect_mac_x, ProjectionCylindrical::rect_mac_x -> cyl_mac_x +#include "rect_funcs.cxx" +#include "cyl_funcs.cxx" + +int main(void) { + for (int k = 0; k < 200; k++) { + double kk = (double) k; + double c_view = -3.0 + 0.03 * kk; + double c_nick = 0.10 * sin(0.11 * kk); + double c_tilt = 0.05 * cos(0.07 * kk); + double scale = 2000.0 + 37.0 * fmod(kk, 19.0); + double k0 = 0.001 * sin(0.31 * kk); + double k1 = 0.0001 * cos(0.17 * kk); + double x0 = 50.0 * sin(0.13 * kk); + double m_view = c_view + 0.5 * sin(0.23 * kk); + double m_nick = 0.05 * cos(0.19 * kk); + + printf("%.17g %.17g %.17g %.17g %.17g %.17g %.17g %.17g %.17g ", + rect_mac_x(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dc_view(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dc_nick(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dc_tilt(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dscale(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dk0(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dk1(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_x_dx0(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick)); + printf("%.17g %.17g %.17g %.17g %.17g %.17g %.17g ", + rect_mac_y_dc_view(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dc_nick(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dc_tilt(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dscale(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dk0(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dk1(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + rect_mac_y_dx0(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick)); + + printf("%.17g %.17g ", + cyl_mac_x(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_y(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick)); + printf("%.17g %.17g %.17g %.17g ", + cyl_mac_x_dc_view(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_x_dc_nick(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_x_dc_tilt(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_x_dscale(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick)); + printf("%.17g %.17g %.17g %.17g\n", + cyl_mac_y_dc_view(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_y_dc_nick(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_y_dc_tilt(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick), + cyl_mac_y_dscale(c_view, c_nick, c_tilt, scale, k0, k1, x0, m_view, m_nick)); + } + return 0; +} diff --git a/tests/harness/stitchdump.cxx b/tests/harness/stitchdump.cxx new file mode 100644 index 0000000..cd015e7 --- /dev/null +++ b/tests/harness/stitchdump.cxx @@ -0,0 +1,102 @@ +// Golden dump: Stitch::resample pipeline through the original C++ Panorama + +// ScanImage (no FLTK GUI; PPM input wrapped in Fl_RGB_Image). +// Usage: stitchdump <img.ppm> <projtype> <center> <pitch> <roll> <scale> +// <k0> <k1> <x0> <w> <h> <from> <to> <bicubic> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <algorithm> + +#include <FL/Fl.H> +#include <FL/Fl_RGB_Image.H> + +#include "Panorama.H" +#include "ScanImage.H" + +#define MAX_VALUE 65025 + +// minimal P6 PPM reader +static unsigned char *read_ppm(const char *path, int *w, int *h) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + int c; + if (fgetc(f) != 'P' || fgetc(f) != '6' || (c = fgetc(f), !isspace(c))) { + fclose(f); return NULL; + } + int skip = 0; + while (!skip) { + c = fgetc(f); + if (c == '#') { while ((c = fgetc(f)) != '\n' && c != EOF); continue; } + if (c == ' ' || c == '\n' || c == '\t' || c == '\r') continue; + skip = 1; + ungetc(c, f); + } + (void)c; + if (fscanf(f, "%d %d %*d", w, h) != 2) { fclose(f); return NULL; } + fgetc(f); // single whitespace + unsigned char *buf = (unsigned char*)malloc((size_t)(*w) * (*h) * 3); + if (fread(buf, 1, (size_t)(*w) * (*h) * 3, f) != (size_t)(*w) * (*h) * 3) { + free(buf); fclose(f); return NULL; + } + fclose(f); + return buf; +} + +int main(int argc, char **argv) { + if (argc < 15) { + fprintf(stderr, "usage: stitchdump <img.ppm> <projtype> <center> <pitch> " + "<roll> <scale> <k0> <k1> <x0> <w> <h> <from> <to> <bicubic>\n"); + return 1; + } + int iw = 0, ih = 0; + unsigned char *px = read_ppm(argv[1], &iw, &ih); + if (!px) { fprintf(stderr, "cannot read %s\n", argv[1]); return 1; } + + Fl_RGB_Image img(px, iw, ih, 3); + + Panorama pan; + pan.set_projection((ProjectionLSQ::Projection_t) atoi(argv[2])); + pan.set_center_angle(atof(argv[3])); + pan.set_nick_angle(atof(argv[4])); + pan.set_tilt_angle(atof(argv[5])); + pan.set_scale(atof(argv[6])); + pan.parms.k0 = atof(argv[7]); + pan.parms.k1 = atof(argv[8]); + pan.parms.x0 = atof(argv[9]); + + int w = atoi(argv[10]), h = atoi(argv[11]); + double view_start = atof(argv[12]) * M_PI / 180.0; + double view_end = atof(argv[13]) * M_PI / 180.0; + ScanImage::mode_t mode = atoi(argv[14]) ? ScanImage::BICUBIC : ScanImage::NEAREST; + + double step_view = (view_end - view_start) / w; + int y_off = h / 2; + double radius = (double) w / (view_end - view_start); + + for (int y = 0; y < h; y++) { + double a_nick = atan((double)(y_off - y) / radius); + for (int x = 0; x < w; x++) { + double a_view = view_start + x * step_view; + int hit = 0, r = 0, g = 0, b = 0; + double px2, py2; + if (pan.get_coordinates(a_view, a_nick, &px2, &py2) == 0) { + int rr, gg, bb; + if (ScanImage::get_pixel(&img, mode, + px2 + ((double) img.w()) / 2.0, + py2 + ((double) img.h()) / 2.0, + &rr, &gg, &bb) == 0) { + hit = 1; + r = std::max(std::min(rr, MAX_VALUE), 0); + g = std::max(std::min(gg, MAX_VALUE), 0); + b = std::max(std::min(bb, MAX_VALUE), 0); + } + } + printf("%d %d %d %d %d %d\n", x, y, hit, r, g, b); + } + } + + free(px); + return 0; +} |
